def test_expected_hypervolume_improvement(self): tkwargs = {"device": self.device} for dtype in (torch.float, torch.double): ref_point = [0.0, 0.0] tkwargs["dtype"] = dtype pareto_Y = torch.tensor( [[4.0, 5.0], [5.0, 5.0], [8.5, 3.5], [8.5, 3.0], [9.0, 1.0]], **tkwargs) partitioning = NondominatedPartitioning(num_outcomes=2) # the event shape is `b x q x m` = 1 x 1 x 1 mean = torch.zeros(1, 1, 2, **tkwargs) variance = torch.zeros(1, 1, 2, **tkwargs) mm = MockModel(MockPosterior(mean=mean, variance=variance)) # test error if there is not pareto_Y initialized in partitioning with self.assertRaises(BotorchError): ExpectedHypervolumeImprovement(model=mm, ref_point=ref_point, partitioning=partitioning) partitioning.update(Y=pareto_Y) # test error if ref point has wrong shape with self.assertRaises(ValueError): ExpectedHypervolumeImprovement(model=mm, ref_point=ref_point[:1], partitioning=partitioning) with self.assertRaises(ValueError): # test error if no pareto_Y point is better than ref_point ExpectedHypervolumeImprovement(model=mm, ref_point=[10.0, 10.0], partitioning=partitioning) X = torch.zeros(1, 1, **tkwargs) # basic test acqf = ExpectedHypervolumeImprovement(model=mm, ref_point=ref_point, partitioning=partitioning) res = acqf(X) self.assertEqual(res.item(), 0.0) # check ref point self.assertTrue( torch.equal(acqf.ref_point, torch.tensor(ref_point, **tkwargs))) # check bounds self.assertTrue(hasattr(acqf, "cell_lower_bounds")) self.assertTrue(hasattr(acqf, "cell_upper_bounds")) # check cached indices expected_indices = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=torch.long, device=self.device) self.assertTrue( torch.equal(acqf._cross_product_indices, expected_indices))
def test_constrained_q_expected_hypervolume_improvement(self): for dtype in (torch.float, torch.double): tkwargs = {"device": self.device, "dtype": dtype} ref_point = [0.0, 0.0] pareto_Y = torch.tensor( [[4.0, 5.0], [5.0, 5.0], [8.5, 3.5], [8.5, 3.0], [9.0, 1.0]], **tkwargs) partitioning = NondominatedPartitioning(num_outcomes=2) partitioning.update(Y=pareto_Y) # test q=1 # the event shape is `b x q x m` = 1 x 1 x 2 samples = torch.tensor([[[6.5, 4.5]]], **tkwargs) mm = MockModel(MockPosterior(samples=samples)) sampler = IIDNormalSampler(num_samples=1) X = torch.zeros(1, 1, **tkwargs) # test zero slack for eta in (1e-1, 1e-2): acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, constraints=[lambda Z: torch.zeros_like(Z[..., -1])], eta=eta, ) res = acqf(X) self.assertAlmostEqual(res.item(), 0.5 * 1.5, places=4) # test feasible acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, constraints=[lambda Z: -100.0 * torch.ones_like(Z[..., -1])], eta=1e-3, ) res = acqf(X) self.assertAlmostEqual(res.item(), 1.5, places=4) # test infeasible acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, constraints=[lambda Z: 100.0 * torch.ones_like(Z[..., -1])], eta=1e-3, ) res = acqf(X) self.assertAlmostEqual(res.item(), 0.0, places=4)
def test_non_dominated_partitioning(self): tkwargs = {"device": self.device} for dtype in (torch.float, torch.double): tkwargs["dtype"] = dtype partitioning = NondominatedPartitioning(num_outcomes=2) # assert error is raised if pareto_Y has not been computed with self.assertRaises(BotorchError): partitioning.pareto_Y # test eps # no pareto_Y self.assertEqual(partitioning.eps, 1e-6) partitioning = NondominatedPartitioning(num_outcomes=2, eps=1.0) # eps set self.assertEqual(partitioning.eps, 1.0) # set pareto_Y partitioning = NondominatedPartitioning(num_outcomes=2) Y = torch.zeros(1, 2, **tkwargs) partitioning.update(Y=Y) self.assertEqual(partitioning.eps, 1e-6 if dtype == torch.float else 1e-8) # test _update_pareto_Y partitioning.Y = -Y self.assertFalse(partitioning._update_pareto_Y()) # test m=2 arange = torch.arange(3, 9, **tkwargs) pareto_Y = torch.stack([arange, 11 - arange], dim=-1) Y = torch.cat( [ pareto_Y, torch.tensor( [[8.0, 2.0], [7.0, 1.0]], **tkwargs ), # add some non-pareto elements ], dim=0, ) partitioning = NondominatedPartitioning(num_outcomes=2, Y=Y) sorting = torch.argsort(pareto_Y[:, 0], descending=True) self.assertTrue(torch.equal(pareto_Y[sorting], partitioning.pareto_Y)) ref_point = torch.zeros(2, **tkwargs) inf = float("inf") expected_cell_bounds = torch.tensor( [ [ [8.0, 0.0], [7.0, 3.0], [6.0, 4.0], [5.0, 5.0], [4.0, 6.0], [3.0, 7.0], [0.0, 8.0], ], [ [inf, inf], [8.0, inf], [7.0, inf], [6.0, inf], [5.0, inf], [4.0, inf], [3.0, inf], ], ], **tkwargs, ) cell_bounds = partitioning.get_hypercell_bounds(ref_point) self.assertTrue(torch.equal(cell_bounds, expected_cell_bounds)) # test compute hypervolume hv = partitioning.compute_hypervolume(ref_point) self.assertEqual(hv.item(), 49.0) # test error when reference is not worse than all pareto_Y with self.assertRaises(ValueError): partitioning.compute_hypervolume(pareto_Y.max(dim=0).values) # test batched, m=2 case Y = torch.rand(3, 10, 2, **tkwargs) partitioning = NondominatedPartitioning(num_outcomes=2, Y=Y) cell_bounds = partitioning.get_hypercell_bounds(ref_point) partitionings = [] for i in range(Y.shape[0]): partitioning_i = NondominatedPartitioning(num_outcomes=2, Y=Y[i]) partitionings.append(partitioning_i) # check pareto_Y pareto_set1 = {tuple(x) for x in partitioning_i.pareto_Y.tolist()} pareto_set2 = {tuple(x) for x in partitioning.pareto_Y[i].tolist()} self.assertEqual(pareto_set1, pareto_set2) expected_cell_bounds_i = partitioning_i.get_hypercell_bounds(ref_point) # remove padding no_padding_cell_bounds_i = cell_bounds[:, i][ :, ((cell_bounds[1, i] - cell_bounds[0, i]) != 0).all(dim=-1) ] self.assertTrue( torch.equal(expected_cell_bounds_i, no_padding_cell_bounds_i) ) # test batch ref point cell_bounds2 = partitioning.get_hypercell_bounds( ref_point.unsqueeze(0).expand(3, 2) ) self.assertTrue(torch.equal(cell_bounds, cell_bounds2)) # test improper batch shape with self.assertRaises(BotorchTensorDimensionError): partitioning.get_hypercell_bounds(ref_point.unsqueeze(0).expand(4, 2)) # test improper Y shape (too many batch dims) with self.assertRaises(NotImplementedError): NondominatedPartitioning(num_outcomes=2, Y=Y.unsqueeze(0)) # test batched compute_hypervolume, m=2 hvs = partitioning.compute_hypervolume(ref_point) hvs_non_batch = torch.stack( [ partitioning_i.compute_hypervolume(ref_point) for partitioning_i in partitionings ], dim=0, ) self.assertTrue(torch.allclose(hvs, hvs_non_batch)) # test batched m>2 with self.assertRaises(NotImplementedError): NondominatedPartitioning( num_outcomes=3, Y=torch.cat([Y, Y[..., :1]], dim=-1) ) # test error with partition_non_dominated_space_2d for m=3 partitioning = NondominatedPartitioning( num_outcomes=3, Y=torch.zeros(1, 3, **tkwargs) ) with self.assertRaises(BotorchTensorDimensionError): partitioning.partition_non_dominated_space_2d() # test m=3 pareto_Y = torch.tensor( [[1.0, 6.0, 8.0], [2.0, 4.0, 10.0], [3.0, 5.0, 7.0]], **tkwargs ) partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y) sorting = torch.argsort(pareto_Y[:, 0], descending=True) self.assertTrue(torch.equal(pareto_Y[sorting], partitioning.pareto_Y)) ref_point = torch.tensor([-1.0, -2.0, -3.0], **tkwargs) expected_cell_bounds = torch.tensor( [ [ [1.0, 4.0, 7.0], [-1.0, -2.0, 10.0], [-1.0, 4.0, 8.0], [1.0, -2.0, 10.0], [1.0, 4.0, 8.0], [-1.0, 6.0, -3.0], [1.0, 5.0, -3.0], [-1.0, 5.0, 8.0], [2.0, -2.0, 7.0], [2.0, 4.0, 7.0], [3.0, -2.0, -3.0], [2.0, -2.0, 8.0], [2.0, 5.0, -3.0], ], [ [2.0, 5.0, 8.0], [1.0, 4.0, inf], [1.0, 5.0, inf], [2.0, 4.0, inf], [2.0, 5.0, inf], [1.0, inf, 8.0], [2.0, inf, 8.0], [2.0, inf, inf], [3.0, 4.0, 8.0], [3.0, 5.0, 8.0], [inf, 5.0, 8.0], [inf, 5.0, inf], [inf, inf, inf], ], ], **tkwargs, ) cell_bounds = partitioning.get_hypercell_bounds(ref_point) # cell bounds can have different order num_matches = ( (cell_bounds.unsqueeze(0) == expected_cell_bounds.unsqueeze(1)) .all(dim=-1) .any(dim=0) .sum() ) self.assertTrue(num_matches, 9) # test compute hypervolume hv = partitioning.compute_hypervolume(ref_point) self.assertEqual(hv.item(), 358.0)
def test_q_expected_hypervolume_improvement(self): tkwargs = {"device": self.device} for dtype in (torch.float, torch.double): ref_point = [0.0, 0.0] tkwargs["dtype"] = dtype pareto_Y = torch.tensor( [[4.0, 5.0], [5.0, 5.0], [8.5, 3.5], [8.5, 3.0], [9.0, 1.0]], **tkwargs) partitioning = NondominatedPartitioning(num_outcomes=2) # the event shape is `b x q x m` = 1 x 1 x 2 samples = torch.zeros(1, 1, 2, **tkwargs) mm = MockModel(MockPosterior(samples=samples)) # test error if there is not pareto_Y initialized in partitioning with self.assertRaises(BotorchError): qExpectedHypervolumeImprovement(model=mm, ref_point=ref_point, partitioning=partitioning) partitioning.update(Y=pareto_Y) # test error if ref point has wrong shape with self.assertRaises(ValueError): qExpectedHypervolumeImprovement(model=mm, ref_point=ref_point[:1], partitioning=partitioning) X = torch.zeros(1, 1, **tkwargs) # basic test sampler = IIDNormalSampler(num_samples=1) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) # check ref point self.assertTrue( torch.equal(acqf.ref_point, torch.tensor(ref_point, **tkwargs))) # check cached indices self.assertTrue(hasattr(acqf, "q_subset_indices")) self.assertIn("q_choose_1", acqf.q_subset_indices) self.assertTrue( torch.equal( acqf.q_subset_indices["q_choose_1"], torch.tensor([[0]], device=self.device), )) # test q=2 X2 = torch.zeros(2, 1, **tkwargs) samples2 = torch.zeros(1, 2, 2, **tkwargs) mm2 = MockModel(MockPosterior(samples=samples2)) acqf.model = mm2 res = acqf(X2) self.assertEqual(res.item(), 0.0) # check cached indices self.assertTrue(hasattr(acqf, "q_subset_indices")) self.assertIn("q_choose_1", acqf.q_subset_indices) self.assertTrue( torch.equal( acqf.q_subset_indices["q_choose_1"], torch.tensor([[0], [1]], device=self.device), )) self.assertIn("q_choose_2", acqf.q_subset_indices) self.assertTrue( torch.equal( acqf.q_subset_indices["q_choose_2"], torch.tensor([[0, 1]], device=self.device), )) self.assertNotIn("q_choose_3", acqf.q_subset_indices) # now back to 1 and sure all caches were cleared acqf.model = mm res = acqf(X) self.assertNotIn("q_choose_2", acqf.q_subset_indices) self.assertIn("q_choose_1", acqf.q_subset_indices) self.assertTrue( torch.equal( acqf.q_subset_indices["q_choose_1"], torch.tensor([[0]], device=self.device), )) X = torch.zeros(1, 1, **tkwargs) samples = torch.zeros(1, 1, 2, **tkwargs) mm = MockModel(MockPosterior(samples=samples)) # basic test, no resample sampler = IIDNormalSampler(num_samples=2, seed=12345) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 2])) bs = acqf.sampler.base_samples.clone() res = acqf(X) self.assertTrue(torch.equal(acqf.sampler.base_samples, bs)) # basic test, qmc, no resample sampler = SobolQMCNormalSampler(num_samples=2) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 2])) bs = acqf.sampler.base_samples.clone() acqf(X) self.assertTrue(torch.equal(acqf.sampler.base_samples, bs)) # basic test, qmc, resample sampler = SobolQMCNormalSampler(num_samples=2, resample=True) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 2])) bs = acqf.sampler.base_samples.clone() acqf(X) self.assertFalse(torch.equal(acqf.sampler.base_samples, bs)) # basic test for X_pending and warning acqf.set_X_pending() self.assertIsNone(acqf.X_pending) acqf.set_X_pending(None) self.assertIsNone(acqf.X_pending) acqf.set_X_pending(X) self.assertEqual(acqf.X_pending, X) res = acqf(X) X2 = torch.zeros(1, 1, 1, requires_grad=True, **tkwargs) with warnings.catch_warnings( record=True) as ws, settings.debug(True): acqf.set_X_pending(X2) self.assertEqual(acqf.X_pending, X2) self.assertEqual(len(ws), 1) self.assertTrue(issubclass(ws[-1].category, BotorchWarning)) # test objective acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, objective=IdentityMCMultiOutputObjective(), ) res = acqf(X) self.assertEqual(res.item(), 0.0) # Test that the hypervolume improvement is correct for given sample # test q = 1 X = torch.zeros(1, 1, **tkwargs) # basic test samples = torch.tensor([[[6.5, 4.5]]], **tkwargs) mm = MockModel(MockPosterior(samples=samples)) sampler = IIDNormalSampler(num_samples=1) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 1.5) # test q = 1, does not contribute samples = torch.tensor([0.0, 1.0], **tkwargs).view(1, 1, 2) sampler = IIDNormalSampler(1) mm = MockModel(MockPosterior(samples=samples)) acqf.model = mm res = acqf(X) self.assertEqual(res.item(), 0.0) # test q = 2, both points contribute X = torch.zeros(2, 1, **tkwargs) samples = torch.tensor([[6.5, 4.5], [7.0, 4.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf.model = mm res = acqf(X) self.assertEqual(res.item(), 1.75) # test q = 2, only 1 point contributes samples = torch.tensor([[6.5, 4.5], [6.0, 4.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf.model = mm res = acqf(X) self.assertEqual(res.item(), 1.5) # test q = 2, neither contributes samples = torch.tensor([[2.0, 2.0], [0.0, 0.1]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf.model = mm res = acqf(X) self.assertEqual(res.item(), 0.0) # test q = 2, test point better than current best second objective samples = torch.tensor([[6.5, 4.5], [6.0, 6.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf.model = mm res = acqf(X) self.assertEqual(res.item(), 8.0) # test q = 2, test point better than current-best first objective samples = torch.tensor([[6.5, 4.5], [9.0, 2.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 2.0) # test q = 3, all contribute X = torch.zeros(3, 1, **tkwargs) samples = torch.tensor([[6.5, 4.5], [9.0, 2.0], [7.0, 4.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 2.25) # test q = 3, not all contribute samples = torch.tensor([[6.5, 4.5], [9.0, 2.0], [7.0, 5.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 3.5) # test q = 3, none contribute samples = torch.tensor([[0.0, 4.5], [1.0, 2.0], [3.0, 0.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) # test m = 3, q=1 pareto_Y = torch.tensor( [[4.0, 2.0, 3.0], [3.0, 5.0, 1.0], [2.0, 4.0, 2.0], [1.0, 3.0, 4.0]], **tkwargs, ) partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y) samples = torch.tensor([[1.0, 2.0, 6.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) ref_point = [-1.0] * 3 acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) X = torch.zeros(1, 2, **tkwargs) res = acqf(X) self.assertEqual(res.item(), 12.0) # change reference point ref_point = [0.0] * 3 acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 4.0) # test m = 3, no contribution ref_point = [1.0] * 3 acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) res = acqf(X) self.assertEqual(res.item(), 0.0) # test m = 3, q = 2 pareto_Y = torch.tensor( [[4.0, 2.0, 3.0], [3.0, 5.0, 1.0], [2.0, 4.0, 2.0]], **tkwargs) samples = torch.tensor([[1.0, 2.0, 6.0], [1.0, 3.0, 4.0]], **tkwargs).unsqueeze(0) mm = MockModel(MockPosterior(samples=samples)) ref_point = [-1.0] * 3 partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y) acqf = qExpectedHypervolumeImprovement( model=mm, ref_point=ref_point, partitioning=partitioning, sampler=sampler, ) X = torch.zeros(2, 2, **tkwargs) res = acqf(X) self.assertEqual(res.item(), 22.0)
def test_non_dominated_partitioning(self): tkwargs = {"device": self.device} for dtype in (torch.float, torch.double): tkwargs["dtype"] = dtype partitioning = NondominatedPartitioning(num_outcomes=2) # assert error is raised if pareto_Y has not been computed with self.assertRaises(BotorchError): partitioning.pareto_Y # test eps # no pareto_Y self.assertEqual(partitioning.eps, 1e-6) partitioning = NondominatedPartitioning(num_outcomes=2, eps=1.0) # eps set self.assertEqual(partitioning.eps, 1.0) # set pareto_Y partitioning = NondominatedPartitioning(num_outcomes=2) Y = torch.zeros(1, 2, **tkwargs) partitioning.update(Y=Y) self.assertEqual(partitioning.eps, 1e-6 if dtype == torch.float else 1e-8) # test _update_pareto_Y partitioning.Y = -Y self.assertFalse(partitioning._update_pareto_Y()) # test m=2 arange = torch.arange(3, 9, **tkwargs) pareto_Y = torch.stack([arange, 11 - arange], dim=-1) Y = torch.cat( [ pareto_Y, torch.tensor([[8.0, 2.0], [7.0, 1.0]], ** tkwargs), # add some non-pareto elements ], dim=0, ) partitioning = NondominatedPartitioning(num_outcomes=2, Y=Y) sorting = torch.argsort(pareto_Y[:, 0], descending=True) self.assertTrue( torch.equal(pareto_Y[sorting], partitioning.pareto_Y)) ref_point = torch.zeros(2, **tkwargs) inf = float("inf") expected_cell_bounds = torch.tensor([ [ [8.0, 0.0], [7.0, 3.0], [6.0, 4.0], [5.0, 5.0], [4.0, 6.0], [3.0, 7.0], [0.0, 8.0], ], [ [inf, inf], [8.0, inf], [7.0, inf], [6.0, inf], [5.0, inf], [4.0, inf], [3.0, inf], ], ], **tkwargs) cell_bounds = partitioning.get_hypercell_bounds(ref_point) self.assertTrue(torch.equal(cell_bounds, expected_cell_bounds)) # test compute hypervolume hv = partitioning.compute_hypervolume(ref_point) self.assertEqual(hv, 49.0) # test error when reference is not worse than all pareto_Y with self.assertRaises(ValueError): partitioning.compute_hypervolume(pareto_Y.max(dim=0).values) # test error with partition_non_dominated_space_2d for m=3 partitioning = NondominatedPartitioning(num_outcomes=3, Y=torch.zeros( 1, 3, **tkwargs)) with self.assertRaises(BotorchTensorDimensionError): partitioning.partition_non_dominated_space_2d() # test m=3 pareto_Y = torch.tensor( [[1.0, 6.0, 8.0], [2.0, 4.0, 10.0], [3.0, 5.0, 7.0]], **tkwargs) partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y) sorting = torch.argsort(pareto_Y[:, 0], descending=True) self.assertTrue( torch.equal(pareto_Y[sorting], partitioning.pareto_Y)) ref_point = torch.tensor([-1.0, -2.0, -3.0], **tkwargs) expected_cell_bounds = torch.tensor([ [ [1.0, 4.0, 7.0], [-1.0, -2.0, 10.0], [-1.0, 4.0, 8.0], [1.0, -2.0, 10.0], [1.0, 4.0, 8.0], [-1.0, 6.0, -3.0], [1.0, 5.0, -3.0], [-1.0, 5.0, 8.0], [2.0, -2.0, 7.0], [2.0, 4.0, 7.0], [3.0, -2.0, -3.0], [2.0, -2.0, 8.0], [2.0, 5.0, -3.0], ], [ [2.0, 5.0, 8.0], [1.0, 4.0, inf], [1.0, 5.0, inf], [2.0, 4.0, inf], [2.0, 5.0, inf], [1.0, inf, 8.0], [2.0, inf, 8.0], [2.0, inf, inf], [3.0, 4.0, 8.0], [3.0, 5.0, 8.0], [inf, 5.0, 8.0], [inf, 5.0, inf], [inf, inf, inf], ], ], **tkwargs) cell_bounds = partitioning.get_hypercell_bounds(ref_point) # cell bounds can have different order num_matches = ((cell_bounds.unsqueeze(0) == expected_cell_bounds. unsqueeze(1)).all(dim=-1).any(dim=0).sum()) self.assertTrue(num_matches, 9) # test compute hypervolume hv = partitioning.compute_hypervolume(ref_point) self.assertEqual(hv, 358.0)