Coverage for tests/test_coordinates.py: 9%

301 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-03-14 10:31 -0700

1# 

2# Developed for the LSST Data Management System. 

3# This product includes software developed by the LSST Project 

4# (https://www.lsst.org). 

5# See the COPYRIGHT file at the top-level directory of this distribution 

6# for details of code ownership. 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the GNU General Public License 

19# along with this program. If not, see <https://www.gnu.org/licenses/>. 

20# 

21 

22""" 

23Tests for geom.Point, geom.Extent, geom.CoordinateExpr 

24 

25Run with: 

26 ./Coordinates.py 

27or 

28 python 

29 >>> import coordinates; coordinates.run() 

30""" 

31 

32import unittest 

33import math 

34import operator 

35 

36import numpy as np 

37 

38import lsst.utils.tests 

39import lsst.geom as geom 

40 

41 

42class CoordinateTestCase: 

43 """Mixin for some of the tests below. 

44 """ 

45 

46 def testAccessors(self): 

47 for dtype, cls, rnd in self.classes: 

48 vector1 = rnd() 

49 p = cls(*vector1) 

50 self.assertEqual(p.__class__, cls) 

51 self.assertEqual(tuple(p), tuple(vector1)) 

52 self.assertEqual(tuple(p.clone()), tuple(p)) 

53 self.assertIsNot(p.clone(), p) 

54 vector2 = rnd() 

55 for n in range(cls.dimensions): 

56 p[n] = vector2[n] 

57 self.assertEqual(tuple(p), tuple(vector2)) 

58 

59 def testComparison(self): 

60 for dtype, cls, rnd in self.classes: 

61 CoordinateExpr = geom.CoordinateExpr[cls.dimensions] 

62 vector1 = rnd() 

63 vector2 = rnd() 

64 p1 = cls(*vector1) 

65 p2 = cls(*vector2) 

66 

67 self.assertEqual(p1 == p2, all(p1.eq(p2))) 

68 self.assertEqual(p1 != p2, any(p1.ne(p2))) 

69 self.assertIsNotNone(p1) # should not throw 

70 self.assertNotEqual(p1, tuple(p1)) # should not throw 

71 

72 self.assertEqual( 

73 tuple(p1.eq(p2)), 

74 tuple([v1 == v2 for v1, v2 in zip(vector1, vector2)])) 

75 self.assertEqual( 

76 tuple(p1.ne(p2)), 

77 tuple([v1 != v2 for v1, v2 in zip(vector1, vector2)])) 

78 self.assertEqual( 

79 tuple(p1.lt(p2)), 

80 tuple([v1 < v2 for v1, v2 in zip(vector1, vector2)])) 

81 self.assertEqual( 

82 tuple(p1.le(p2)), 

83 tuple([v1 <= v2 for v1, v2 in zip(vector1, vector2)])) 

84 self.assertEqual( 

85 tuple(p1.gt(p2)), 

86 tuple([v1 > v2 for v1, v2 in zip(vector1, vector2)])) 

87 self.assertEqual( 

88 tuple(p1.ge(p2)), 

89 tuple([v1 >= v2 for v1, v2 in zip(vector1, vector2)])) 

90 self.assertEqual(type(p1.eq(p2)), CoordinateExpr) 

91 self.assertEqual(type(p1.ne(p2)), CoordinateExpr) 

92 self.assertEqual(type(p1.lt(p2)), CoordinateExpr) 

93 self.assertEqual(type(p1.le(p2)), CoordinateExpr) 

94 self.assertEqual(type(p1.gt(p2)), CoordinateExpr) 

95 self.assertEqual(type(p1.ge(p2)), CoordinateExpr) 

96 scalar = dtype(rnd()[0]) 

97 self.assertEqual(tuple(p1.eq(scalar)), 

98 tuple([v1 == scalar for v1 in vector1])) 

99 self.assertEqual(tuple(p1.ne(scalar)), 

100 tuple([v1 != scalar for v1 in vector1])) 

101 self.assertEqual(tuple(p1.lt(scalar)), 

102 tuple([v1 < scalar for v1 in vector1])) 

103 self.assertEqual(tuple(p1.le(scalar)), 

104 tuple([v1 <= scalar for v1 in vector1])) 

105 self.assertEqual(tuple(p1.gt(scalar)), 

106 tuple([v1 > scalar for v1 in vector1])) 

107 self.assertEqual(tuple(p1.ge(scalar)), 

108 tuple([v1 >= scalar for v1 in vector1])) 

109 self.assertEqual(type(p1.eq(scalar)), CoordinateExpr) 

110 self.assertEqual(type(p1.ne(scalar)), CoordinateExpr) 

111 self.assertEqual(type(p1.lt(scalar)), CoordinateExpr) 

112 self.assertEqual(type(p1.le(scalar)), CoordinateExpr) 

113 self.assertEqual(type(p1.gt(scalar)), CoordinateExpr) 

114 self.assertEqual(type(p1.ge(scalar)), CoordinateExpr) 

115 

116 def test_repr(self): 

117 # repr uses unqualified names, so we need to bring them all in 

118 # scope for eval(repr(...)) to work. 

119 from lsst.geom import ( # noqa: F401 

120 Extent2D, 

121 Extent2I, 

122 Extent3D, 

123 Extent3I, 

124 Point2D, 

125 Point2I, 

126 Point3D, 

127 Point3I, 

128 ) 

129 for _, cls, rnd in self.classes: 

130 p = cls(*rnd()) 

131 self.assertEqual(eval(repr(p)), p) 

132 self.assertEqual(eval(repr(cls())), cls()) 

133 

134 

135class PointTestCase(CoordinateTestCase, lsst.utils.tests.TestCase): 

136 """A test case for Point""" 

137 

138 def setUp(self): 

139 np.random.seed(1) 

140 self.classes = [ 

141 (float, geom.Point2D, lambda: [float(x) 

142 for x in np.random.randn(2)]), 

143 (int, geom.Point2I, lambda: [int(x) 

144 for x in np.random.randint(-5, 5, 2)]), 

145 (float, geom.Point3D, lambda: [float(x) 

146 for x in np.random.randn(3)]), 

147 (int, geom.Point3I, lambda: [int(x) 

148 for x in np.random.randint(-5, 5, 3)]), 

149 ] 

150 

151 def testConstructors(self): 

152 # test 2-d 

153 e1 = geom.Point2I(1, 2) 

154 e2 = geom.Point2I(e1) 

155 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

156 

157 e1 = geom.Point2D(1.2, 3.4) 

158 e2 = geom.Point2D(e1) 

159 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

160 

161 e1 = geom.Point2I(1, 3) 

162 e2 = geom.Point2D(e1) 

163 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

164 

165 # test 3-d 

166 e1 = geom.Point3I(1, 2, 3) 

167 e2 = geom.Point3I(e1) 

168 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

169 

170 e1 = geom.Point3D(1.2, 3.4, 5.6) 

171 e2 = geom.Point3D(e1) 

172 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

173 

174 e1 = geom.Point3I(1, 2, 3) 

175 e2 = geom.Point3D(e1) 

176 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

177 

178 # test rounding to integral coordinates 

179 e1 = geom.Point2D(1.2, 3.4) 

180 e2 = geom.Point2I(e1) 

181 self.assertAlmostEqual( 

182 tuple([math.floor(v + 0.5) for v in e1]), tuple(e2)) 

183 

184 e1 = geom.Point3D(1.2, 3.4, 5.6) 

185 e2 = geom.Point3I(e1) 

186 self.assertAlmostEqual( 

187 tuple([math.floor(v + 0.5) for v in e1]), tuple(e2)) 

188 

189 

190class ExtentTestCase(CoordinateTestCase, lsst.utils.tests.TestCase): 

191 """A test case for Extent""" 

192 

193 def setUp(self): 

194 np.random.seed(1) 

195 self.classes = [ 

196 (float, geom.Extent2D, lambda: [float(x) 

197 for x in np.random.randn(2)]), 

198 (int, geom.Extent2I, lambda: [int(x) 

199 for x in np.random.randint(-5, 5, 2)]), 

200 (float, geom.Extent3D, lambda: [float(x) 

201 for x in np.random.randn(3)]), 

202 (int, geom.Extent3I, lambda: [int(x) 

203 for x in np.random.randint(-5, 5, 3)]), 

204 ] 

205 

206 def testRounding(self): 

207 e1 = geom.Extent2D(1.2, -3.4) 

208 self.assertEqual(e1.floor(), geom.Extent2I(1, -4)) 

209 self.assertEqual(e1.ceil(), geom.Extent2I(2, -3)) 

210 self.assertEqual(e1.truncate(), geom.Extent2I(1, -3)) 

211 

212 def testConstructors(self): 

213 # test extent from extent 2-d 

214 e1 = geom.Extent2I(1, 2) 

215 e2 = geom.Extent2I(e1) 

216 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

217 

218 e1 = geom.Extent2D(1.2, 3.4) 

219 e2 = geom.Extent2D(e1) 

220 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

221 

222 e1 = geom.Extent2I(1, 2) 

223 e2 = geom.Extent2D(e1) 

224 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

225 

226 # test extent from extent 3-d 

227 e1 = geom.Extent3I(1, 2, 3) 

228 e2 = geom.Extent3I(e1) 

229 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

230 

231 e1 = geom.Extent3D(1.2, 3.4, 5.6) 

232 e2 = geom.Extent3D(e1) 

233 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

234 

235 e1 = geom.Extent3I(1, 2, 3) 

236 e2 = geom.Extent3D(e1) 

237 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

238 

239 # test extent from point 2-d 

240 e1 = geom.Point2I(1, 2) 

241 e2 = geom.Extent2I(e1) 

242 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

243 

244 e1 = geom.Point2D(1.2, 3.4) 

245 e2 = geom.Extent2D(e1) 

246 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

247 

248 e1 = geom.Point2I(1, 2) 

249 e2 = geom.Extent2D(e1) 

250 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

251 

252 # test extent from point 3-d 

253 e1 = geom.Point3I(1, 2, 3) 

254 e2 = geom.Extent3I(e1) 

255 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

256 

257 e1 = geom.Point3D(1.2, 3.4, 5.6) 

258 e2 = geom.Extent3D(e1) 

259 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

260 

261 e1 = geom.Point3I(1, 2, 3) 

262 e2 = geom.Extent3D(e1) 

263 self.assertAlmostEqual(tuple(e1), tuple(e2)) 

264 

265 

266class OperatorTestCase(lsst.utils.tests.TestCase): 

267 

268 @staticmethod 

269 def makeRandom(cls): 

270 """Make a random Point, Extent, int, or float of the given type.""" 

271 if cls is int: 

272 v = 0 

273 while v == 0: 

274 v = int(np.random.randn()*10) 

275 elif cls is float: 

276 v = float(np.random.randn()*10) 

277 else: 

278 v = cls() 

279 t = type(v[0]) 

280 for i in range(len(v)): 

281 while v[i] == 0: 

282 v[i] = t(np.random.randn()*10) 

283 return v 

284 

285 def checkOperator(self, op, lhs, rhs, expected, inPlace=False): 

286 """Check that the type and result of applying operator 'op' to types 'lhs' and 'rhs' 

287 yield a result of type 'expected', and that the computed value is correct. If 

288 'expected' is an Exception subclass, instead check that attempting to apply the 

289 operator raises that exception. 

290 """ 

291 v1 = self.makeRandom(lhs) 

292 v2 = self.makeRandom(rhs) 

293 if issubclass(expected, Exception): 

294 with self.assertRaises(expected): 

295 op(v1, v2) 

296 else: 

297 check = op(np.array(v1), np.array(v2)) 

298 result = op(v1, v2) 

299 if type(result) != expected: 

300 self.fail("%s(%s, %s): expected %s, got %s" % 

301 (op.__name__, lhs.__name__, rhs.__name__, 

302 expected.__name__, type(result).__name__)) 

303 if not np.allclose(result, check): 

304 self.fail("%s(%s, %s): expected %s, got %s" % 

305 (op.__name__, lhs.__name__, rhs.__name__, tuple(check), tuple(result))) 

306 if inPlace and result is not v1: 

307 self.fail("%s(%s, %s): result is not self" % 

308 (op.__name__, lhs.__name__, rhs.__name__)) 

309 

310 def testPointAsExtent(self): 

311 for n in (2, 3): 

312 for t in (int, float): 

313 p = self.makeRandom(geom.Point[t, n]) 

314 e = p.asExtent() 

315 self.assertEqual(type(e), geom.Extent[t, n]) 

316 self.assertFloatsAlmostEqual( 

317 np.array(p), np.array(e), rtol=0.0, atol=0.0) 

318 

319 def testExtentAsPoint(self): 

320 for n in (2, 3): 

321 for t in (int, float): 

322 e = self.makeRandom(geom.Extent[t, n]) 

323 p = e.asPoint() 

324 self.assertEqual(type(p), geom.Point[t, n]) 

325 self.assertFloatsAlmostEqual( 

326 np.array(p), np.array(e), rtol=0.0, atol=0.0) 

327 

328 def testUnaryOperators(self): 

329 for n in (2, 3): 

330 for t in (int, float): 

331 e1 = self.makeRandom(geom.Extent[t, n]) 

332 e2 = +e1 

333 self.assertEqual(type(e1), type(e2)) 

334 self.assertFloatsAlmostEqual( 

335 np.array(e1), np.array(e2), rtol=0.0, atol=0.0) 

336 e3 = -e1 

337 self.assertEqual(type(e1), type(e3)) 

338 self.assertFloatsAlmostEqual( 

339 np.array(e3), -np.array(e1), rtol=0.0, atol=0.0) 

340 

341 def testBinaryOperators(self): 

342 for n in (2, 3): 

343 pD = geom.Point[float, n] 

344 pI = geom.Point[int, n] 

345 eD = geom.Extent[float, n] 

346 eI = geom.Extent[int, n] 

347 # Addition 

348 self.checkOperator(operator.add, pD, pD, TypeError) 

349 self.checkOperator(operator.add, pD, pI, TypeError) 

350 self.checkOperator(operator.add, pD, eD, pD) 

351 self.checkOperator(operator.add, pD, eI, pD) 

352 self.checkOperator(operator.add, pI, pD, TypeError) 

353 self.checkOperator(operator.add, pI, pI, TypeError) 

354 self.checkOperator(operator.add, pI, eD, pD) 

355 self.checkOperator(operator.add, pI, eI, pI) 

356 self.checkOperator(operator.add, eD, pD, pD) 

357 self.checkOperator(operator.add, eD, pI, pD) 

358 self.checkOperator(operator.add, eD, eI, eD) 

359 self.checkOperator(operator.add, eD, eD, eD) 

360 self.checkOperator(operator.add, eI, pD, pD) 

361 self.checkOperator(operator.add, eI, pI, pI) 

362 self.checkOperator(operator.add, eI, eD, eD) 

363 self.checkOperator(operator.add, eI, eI, eI) 

364 # Subtraction 

365 self.checkOperator(operator.sub, pD, pD, eD) 

366 self.checkOperator(operator.sub, pD, pI, eD) 

367 self.checkOperator(operator.sub, pD, eD, pD) 

368 self.checkOperator(operator.sub, pD, eI, pD) 

369 self.checkOperator(operator.sub, pI, pD, eD) 

370 self.checkOperator(operator.sub, pI, pI, eI) 

371 self.checkOperator(operator.sub, pI, eD, pD) 

372 self.checkOperator(operator.sub, pI, eI, pI) 

373 self.checkOperator(operator.sub, eD, pD, TypeError) 

374 self.checkOperator(operator.sub, eD, pI, TypeError) 

375 self.checkOperator(operator.sub, eD, eD, eD) 

376 self.checkOperator(operator.sub, eD, eI, eD) 

377 self.checkOperator(operator.sub, eI, pD, TypeError) 

378 self.checkOperator(operator.sub, eI, pI, TypeError) 

379 self.checkOperator(operator.sub, eI, eD, eD) 

380 self.checkOperator(operator.sub, eI, eI, eI) 

381 # Multiplication 

382 self.checkOperator(operator.mul, eD, int, eD) 

383 self.checkOperator(operator.mul, eD, float, eD) 

384 self.checkOperator(operator.mul, eI, int, eI) 

385 self.checkOperator(operator.mul, eI, float, eD) 

386 self.checkOperator(operator.mul, int, eD, eD) 

387 self.checkOperator(operator.mul, float, eD, eD) 

388 self.checkOperator(operator.mul, int, eI, eI) 

389 self.checkOperator(operator.mul, float, eI, eD) 

390 # New-Style Division 

391 self.checkOperator(operator.truediv, eD, int, eD) 

392 self.checkOperator(operator.truediv, eD, float, eD) 

393 self.checkOperator(operator.truediv, eI, int, eD) 

394 self.checkOperator(operator.truediv, eI, float, eD) 

395 # Floor Division 

396 self.checkOperator(operator.floordiv, eD, int, TypeError) 

397 self.checkOperator(operator.floordiv, eD, float, TypeError) 

398 self.checkOperator(operator.floordiv, eI, int, eI) 

399 self.checkOperator(operator.floordiv, eI, float, TypeError) 

400 

401 def testInPlaceOperators(self): 

402 for n in (2, 3): 

403 pD = geom.Point[float, n] 

404 pI = geom.Point[int, n] 

405 eD = geom.Extent[float, n] 

406 eI = geom.Extent[int, n] 

407 # Addition 

408 self.checkOperator(operator.iadd, pD, pD, TypeError) 

409 self.checkOperator(operator.iadd, pD, pI, TypeError) 

410 self.checkOperator(operator.iadd, pD, eD, pD, inPlace=True) 

411 self.checkOperator(operator.iadd, pD, eI, pD, inPlace=True) 

412 self.checkOperator(operator.iadd, pI, pD, TypeError) 

413 self.checkOperator(operator.iadd, pI, pI, TypeError) 

414 self.checkOperator(operator.iadd, pI, eD, TypeError) 

415 self.checkOperator(operator.iadd, pI, eI, pI, inPlace=True) 

416 self.checkOperator(operator.iadd, eD, pD, TypeError) 

417 self.checkOperator(operator.iadd, eD, pI, TypeError) 

418 self.checkOperator(operator.iadd, eD, eI, eD, inPlace=True) 

419 self.checkOperator(operator.iadd, eD, eD, eD, inPlace=True) 

420 self.checkOperator(operator.iadd, eI, pD, TypeError) 

421 self.checkOperator(operator.iadd, eI, pI, TypeError) 

422 self.checkOperator(operator.iadd, eI, eD, TypeError) 

423 self.checkOperator(operator.iadd, eI, eI, eI, inPlace=True) 

424 # Subtraction 

425 self.checkOperator(operator.isub, pD, pD, TypeError) 

426 self.checkOperator(operator.isub, pD, pI, TypeError) 

427 self.checkOperator(operator.isub, pD, eD, pD, inPlace=True) 

428 self.checkOperator(operator.isub, pD, eI, pD, inPlace=True) 

429 self.checkOperator(operator.isub, pI, pD, TypeError) 

430 self.checkOperator(operator.isub, pI, pI, TypeError) 

431 self.checkOperator(operator.isub, pI, eD, TypeError) 

432 self.checkOperator(operator.isub, pI, eI, pI, inPlace=True) 

433 self.checkOperator(operator.isub, eD, pD, TypeError) 

434 self.checkOperator(operator.isub, eD, pI, TypeError) 

435 self.checkOperator(operator.isub, eD, eD, eD, inPlace=True) 

436 self.checkOperator(operator.isub, eD, eI, eD, inPlace=True) 

437 self.checkOperator(operator.isub, eI, pD, TypeError) 

438 self.checkOperator(operator.isub, eI, pI, TypeError) 

439 self.checkOperator(operator.isub, eI, eD, TypeError) 

440 self.checkOperator(operator.isub, eI, eI, eI, inPlace=True) 

441 # Multiplication 

442 self.checkOperator(operator.imul, eD, int, eD, inPlace=True) 

443 self.checkOperator(operator.imul, eD, float, eD, inPlace=True) 

444 self.checkOperator(operator.imul, eI, int, eI, inPlace=True) 

445 self.checkOperator(operator.imul, eI, float, TypeError) 

446 # New-Style Division 

447 self.checkOperator(operator.itruediv, eD, int, eD, inPlace=True) 

448 self.checkOperator(operator.itruediv, eD, float, eD, inPlace=True) 

449 self.checkOperator(operator.itruediv, eI, int, TypeError) 

450 self.checkOperator(operator.itruediv, eI, float, TypeError) 

451 # Floor Division 

452 self.checkOperator(operator.floordiv, eD, int, TypeError) 

453 self.checkOperator(operator.floordiv, eD, float, TypeError) 

454 self.checkOperator(operator.floordiv, eI, int, eI) 

455 self.checkOperator(operator.floordiv, eI, float, TypeError) 

456 

457 

458class MemoryTester(lsst.utils.tests.MemoryTestCase): 

459 pass 

460 

461 

462def setup_module(module): 

463 lsst.utils.tests.init() 

464 

465 

466if __name__ == "__main__": 466 ↛ 467line 466 didn't jump to line 467, because the condition on line 466 was never true

467 lsst.utils.tests.init() 

468 unittest.main()