*/ abstract public function fitDataProvider(): array; /** * @param array $center * @return array */ abstract protected function random(array $center, float $radius): array; // ------------------------------------------------------------------------ // tests /** * @dataProvider fitDataProvider */ public function testFit( SpaceInterface $space, float $radius, PointCollectionInterface $points, PointCollectionInterface $initialCentroids, PointCollectionInterface $expectedCentroids, ): void { $algorithm = $this->makeAlgorithm( $this->mockInitScheme( $this->makeClusters($points, $initialCentroids) ) ); $result = iterator_to_array( $algorithm->fit($points, count($expectedCentroids)) ); foreach ($expectedCentroids as $i => $expectedCentroid) { $this->assertLessThan( $radius, $algorithm->getDistanceBetween( $expectedCentroid, $result[$i]->getCentroid() ) ); if ( is_array($expectedCentroid->getData()) && isset($expectedCentroid->getData()['count']) ) { $this->assertCount( $expectedCentroid->getData()['count'], $result[$i]->getPoints() ); } } } // ------------------------------------------------------------------------ // helpers /** * @param array> $centers * @return ScenarioData */ protected function makeScenarioData( SpaceInterface $space, array $centers, float $radius, int $count ): array { $points = new PointCollection($space); for ($i = 0; $i < count($centers); $i++) { for ($j = 0; $j < $count; $j++) { $point = $space->makePoint($this->random($centers[$i], $radius)); $points->attach($point); } } $initialCentroids = new PointCollection($space); for ($i = 0; $i < count($centers); $i++) { $point = $space->makePoint($centers[$i]); $initialCentroids->attach($point); } $expectedCentroids = new PointCollection($space); for ($i = 0; $i < count($centers); $i++) { $point = $space->makePoint($centers[$i]); $point->setData(['count' => $count]); $expectedCentroids->attach($point); } return compact( 'space', 'radius', 'points', 'initialCentroids', 'expectedCentroids' ); } protected function makeClusters( PointCollectionInterface $points, PointCollectionInterface $centroids ): ClusterCollectionInterface { $clusters = new ClusterCollection($points->getSpace()); foreach ($centroids as $n => $centroid) { // attach all points to the first cluster $clusters->attach(new Cluster($centroid, $n == 0 ? $points : null)); } return $clusters; } protected function mockInitScheme( ClusterCollectionInterface $clusters ): InitializationSchemeInterface { /** @var InitializationSchemeInterface */ $initScheme = Mockery::mock(InitializationSchemeInterface::class); /** @phpstan-ignore-next-line */ $initScheme ->shouldReceive('initializeClusters') ->with(PointCollectionInterface::class, Mockery::type('integer')) ->andReturn($clusters); return $initScheme; } }