maxHeight = $maxHeight; } /** * Return the height of the tree i.e. the number of levels. * * @return int */ public function height() : int { return $this->root ? $this->root->height() : 0; } /** * Return the balance factor of the tree. A balanced tree will have * a factor of 0 whereas an imbalanced tree will either be positive * or negative indicating the direction and degree of the imbalance. * * @return int */ public function balance() : int { return $this->root ? $this->root->balance() : 0; } /** * Is the tree bare? * * @return bool */ public function bare() : bool { return !$this->root; } /** * Insert a root node and recursively split the dataset until a * terminating condition is met. * * @param Dataset $dataset */ public function grow(Dataset $dataset) : void { $this->root = Isolator::split($dataset); /** @var list */ $stack = [[$this->root, 1]]; while ($stack) { [$current, $depth] = array_pop($stack); [$left, $right] = $current->subsets(); $current->cleanup(); ++$depth; if ($left->empty() or $right->empty()) { $node = Depth::terminate($left->merge($right), $depth); $current->attachLeft($node); $current->attachRight($node); continue; } if ($depth >= $this->maxHeight) { $current->attachLeft(Depth::terminate($left, $depth)); $current->attachRight(Depth::terminate($right, $depth)); continue; } if ($left->numSamples() > self::MAX_LEAF_SIZE) { $node = Isolator::split($left); $current->attachLeft($node); $stack[] = [$node, $depth]; } else { $current->attachLeft(Depth::terminate($left, $depth)); } if ($right->numSamples() > self::MAX_LEAF_SIZE) { $node = Isolator::split($right); $current->attachRight($node); $stack[] = [$node, $depth]; } else { $current->attachRight(Depth::terminate($right, $depth)); } } } /** * Search the tree for a leaf node. * * @param list $sample * @return Depth|null */ public function search(array $sample) : ?Depth { $current = $this->root; while ($current) { if ($current instanceof Isolator) { $value = $current->value(); if (is_string($value)) { if ($sample[$current->column()] === $value) { $current = $current->left(); } else { $current = $current->right(); } } else { if ($sample[$current->column()] < $value) { $current = $current->left(); } else { $current = $current->right(); } } continue; } if ($current instanceof Depth) { return $current; } } return null; } }