From cd7e4ace906222e2db38e40dc7173de006eefded Mon Sep 17 00:00:00 2001 From: jannetty Date: Thu, 19 Sep 2024 12:11:43 -0700 Subject: [PATCH] only call connect_voxels if splitpoint is not specified to split function in PottsLocation --- .../potts/env/location/PottsLocation.java | 83 +++++++------------ .../potts/env/location/PottsLocationTest.java | 29 +++++-- 2 files changed, 51 insertions(+), 61 deletions(-) diff --git a/src/arcade/potts/env/location/PottsLocation.java b/src/arcade/potts/env/location/PottsLocation.java index 133969da..df9712e0 100644 --- a/src/arcade/potts/env/location/PottsLocation.java +++ b/src/arcade/potts/env/location/PottsLocation.java @@ -262,9 +262,8 @@ public void update(int id, int[][][] ids, int[][][] regions) { * Splits the location voxels into two lists. *

* The split occurs along the direction with the shortest diameter, or a - * specified direction. The lists of voxels are guaranteed to be connected, - * and generally will be balanced in size. One of the splits is assigned - * to the current location and the other is returned. + * specified direction. One of the splits is assigned to the current + * location and the other is returned. * * @param random the seeded random number generator * @return a location with the split voxels @@ -276,8 +275,6 @@ public Location split(MersenneTwisterFast random) { /** * Splits the location voxels into two lists at the point specified by * offsetPercents and along the shortest diameter. - *

- * The lists of voxels are guaranteed to be connected and balanced. * * @param random the seeded random number generator * @param offsetPercents the percentage offset in each direction for the split point @@ -294,7 +291,7 @@ public Location split(MersenneTwisterFast random, ArrayList offsetPerce *

* The lists of voxels are guaranteed to be connected and balanced if no * direction is provided. If a direction is provided, the split occurs - * without balancing. + * without connecting or balancing. * * @param random the seeded random number generator * @param offsetPercents the percentage offset in each direction for the split point @@ -302,45 +299,26 @@ public Location split(MersenneTwisterFast random, ArrayList offsetPerce * @return a location with the split voxels */ public Location split(MersenneTwisterFast random, ArrayList offsetPercents, Direction direction) { - Voxel splitpoint = (offsetPercents != null) ? getSplitpoint(offsetPercents) : getSplitpoint(); - - // Check if the direction is specified, if not use default behavior with balancing - if (direction == null) { - return performSplit(random, splitpoint); // This will balance voxels - } else { - return performSplit(random, splitpoint, direction); // This will skip balancing - } - } + Voxel splitpoint; + boolean shouldBalance; - /** - * Performs the voxel split based on a given split point. - *

- * Splits the voxels of the location into two balanced, connected groups on - * either side of split point. Split occurs along the direction with - * the shortest diameter. - * - * @param random the seeded random number generator - * @param splitpoint the voxel that determines where the split occurs - * @return a {@code Location} containing the split voxels that are not assigned to the current location - */ - Location performSplit(MersenneTwisterFast random, Voxel splitpoint) { - return performSplit(random, splitpoint, getDirection(random), true); - } + // Case 1: If offsetPercents are provided, don't balance the voxels + if (offsetPercents != null) { + splitpoint = getSplitpoint(offsetPercents); + shouldBalance = false; + } + // Case 2: If direction is provided and no offsetPercents, balance the voxels + else if (direction != null) { + splitpoint = getSplitpoint(); + shouldBalance = true; + } + // Default Case: Neither offsetPercents nor direction are provided, balance the voxels + else { + splitpoint = getSplitpoint(); + shouldBalance = true; // This can be changed if needed + } - /** - * Performs the voxel split based on a given split point and direction. - *

- * Splits the voxels of the location into two connected groups on - * either side of split point. Balancing is skipped. - * - * @param random the seeded random number generator - * @param splitpoint the voxel that determines where the split occurs - * @param direction the direction of the split - * @return a {@code Location} containing the split voxels that are not assigned to the current location - */ - Location performSplit(MersenneTwisterFast random, Voxel splitpoint, Direction direction) { - // Calls performSplit without balancing - return performSplit(random, splitpoint, direction, false); + return performSplit(random, splitpoint, direction, shouldBalance); } /** @@ -348,12 +326,12 @@ Location performSplit(MersenneTwisterFast random, Voxel splitpoint, Direction di * and whether to balance the voxels. *

* Splits the voxels of the location into two connected groups on - * either side of split point. Balancing is optional based on the + * either side of split point. Balancing and connecting is optional based on the * shouldBalance parameter. * - * @param random the seeded random number generator + * @param random the seeded random number generator * @param splitpoint the voxel that determines where the split occurs - * @param direction the direction of the split + * @param direction the direction of the split (can be null) * @param shouldBalance indicates whether voxels should be balanced * @return a {@code Location} containing the split voxels that are not assigned to the current location */ @@ -361,18 +339,19 @@ Location performSplit(MersenneTwisterFast random, Voxel splitpoint, Direction di // Initialize lists of split voxels ArrayList voxelsA = new ArrayList<>(); ArrayList voxelsB = new ArrayList<>(); - + // Perform the split along the specified direction + if (direction == null) { + direction = getDirection(random); + } splitVoxels(direction, voxels, voxelsA, voxelsB, splitpoint, random); - - // Ensure that voxel split is connected - connectVoxels(voxelsA, voxelsB, this, random); - + // Only balance if 'shouldBalance' is true if (shouldBalance) { + connectVoxels(voxelsA, voxelsB, this, random); balanceVoxels(voxelsA, voxelsB, this, random); } - + // Select one split to keep for this location and return the other return (random.nextDouble() < 0.5) ? separateVoxels(voxelsA, voxelsB, random) : separateVoxels(voxelsB, voxelsA, random); } diff --git a/test/arcade/potts/env/location/PottsLocationTest.java b/test/arcade/potts/env/location/PottsLocationTest.java index 346a1af9..cbff8bf2 100644 --- a/test/arcade/potts/env/location/PottsLocationTest.java +++ b/test/arcade/potts/env/location/PottsLocationTest.java @@ -1,13 +1,18 @@ package arcade.potts.env.location; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.Mockito; +import arcade.potts.util.PottsEnums.Direction; +import arcade.potts.util.PottsEnums.Region; import ec.util.MersenneTwisterFast; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static arcade.core.ARCADETestUtilities.*; @@ -1307,28 +1312,27 @@ public void separateVoxels_validLists_updatesCenter() { @Test public void split_noOffsets_callsPerformSplit() { PottsLocation spy = spy(new PottsLocationMock(voxelListAB)); - doCallRealMethod().when(spy).performSplit(any(MersenneTwisterFast.class), any(Voxel.class)); + doCallRealMethod().when(spy).performSplit(any(MersenneTwisterFast.class), any(Voxel.class), any(Direction.class), anyBoolean()); spy.split(randomDoubleZero); verify(spy).getSplitpoint(); - verify(spy).performSplit(eq(randomDoubleZero), any(Voxel.class)); + verify(spy).performSplit(eq(randomDoubleZero), Mockito.any(Voxel.class), eq(null), eq(true)); } @Test public void split_withOffsets_callsPerformSplit() { ArrayList offsets = new ArrayList<>(Arrays.asList(50, 50, 50)); PottsLocation spy = spy(new PottsLocationMock(voxelListAB)); - doCallRealMethod().when(spy).performSplit(any(MersenneTwisterFast.class), any(Voxel.class)); + doCallRealMethod().when(spy).performSplit(any(MersenneTwisterFast.class), any(Voxel.class), any(Direction.class), anyBoolean()); spy.split(randomDoubleZero, offsets); verify(spy).getSplitpoint(offsets); - verify(spy).performSplit(eq(randomDoubleZero), any(Voxel.class)); - } + verify(spy).performSplit(eq(randomDoubleZero), Mockito.any(Voxel.class), eq(null), eq(false)); } @Test public void performSplit_noOffsets_splitsVoxelsCorrectly() { PottsLocation location = new PottsLocationMock(voxelListAB); MersenneTwisterFast random = new MersenneTwisterFast(12345); Voxel splitpoint = location.getSplitpoint(); - PottsLocation splitLocation = (PottsLocation) location.performSplit(random, splitpoint); + PottsLocation splitLocation = (PottsLocation) location.performSplit(random, splitpoint, null, true); assertNotNull(splitLocation); assertTrue(location.voxels.size() > 0); assertTrue(splitLocation.voxels.size() > 0); @@ -1341,17 +1345,24 @@ public void performSplit_noOffsets_splitsVoxelsCorrectly() { public void performSplit_withOffsets_splitsVoxelsCorrectly() { PottsLocation location = new PottsLocationMock(voxelListAB); MersenneTwisterFast random = new MersenneTwisterFast(12345); + // Add two voxels to the location bringing total to 9 + location.add(3, 3, 3); + location.add(4, 4, 4); ArrayList offsets = new ArrayList<>(Arrays.asList(33, 33, 33)); // 33% offsets Voxel splitpoint = location.getSplitpoint(offsets); - PottsLocation splitLocation = (PottsLocation) location.performSplit(random, splitpoint); + PottsLocation splitLocation = (PottsLocation) location.performSplit(random, splitpoint, null, false); assertNotNull(splitLocation); assertTrue(location.voxels.size() > 0); assertTrue(splitLocation.voxels.size() > 0); - assertEquals(voxelListAB.size(), location.voxels.size() + splitLocation.voxels.size()); + assertEquals(voxelListAB.size() + 2, location.voxels.size() + splitLocation.voxels.size()); // Check that one location is approximately 1/3 the size of the other int locationSize = location.voxels.size(); int splitLocationSize = splitLocation.voxels.size(); int totalSize = locationSize + splitLocationSize; - assertTrue(Math.abs(locationSize - totalSize / 3) <= 1 || Math.abs(splitLocationSize - totalSize / 3) <= 1); + double expectedOneThird = totalSize / 3.0; + double expectedTwoThirds = 2 * totalSize / 3.0; + boolean locationIsOneThird = Math.abs(locationSize - expectedOneThird) <= 1 && Math.abs(splitLocationSize - expectedTwoThirds) <= 1; + boolean locationIsTwoThirds = Math.abs(locationSize - expectedTwoThirds) <= 1 && Math.abs(splitLocationSize - expectedOneThird) <= 1; + assertTrue(locationIsOneThird || locationIsTwoThirds); } }