/** * Calculate equivalent nodes * * Calculate groups of nodes, which are contained in their respective * transitive reflexive closure in the automaton. * * Returns an automaton consisting of nodes, which each links to such a * node group. * * @param slCountingSingleOccurenceAutomaton $automaton * @return void */ protected function calculateEquivalencyAutomaton(slAutomaton $automaton) { $nodeValues = $automaton->getNodes(); $nodes = array_keys($nodeValues); $nodeCount = count($nodes); $equivalent = array(); $skip = array(); // Find equivalence classes in automaton for ($i = 0; $i < $nodeCount; ++$i) { if (isset($skip[$i])) { continue; } $this->equivalenceClasses[$nodes[$i]] = array($nodes[$i]); $equivalent[$nodes[$i]] = $nodes[$i]; for ($j = $i + 1; $j < $nodeCount; ++$j) { if (in_array($nodes[$i], $automaton->transitiveClosure($nodes[$j])) && in_array($nodes[$j], $automaton->transitiveClosure($nodes[$i]))) { $this->equivalenceClasses[$nodes[$i]][] = $nodes[$j]; $equivalent[$nodes[$j]] = $nodes[$i]; // The mutal containment in eachs reflexive transitive // closure is obviously symetric $skip[$j] = true; } } } // Readd edges between equivalency classes based on source automaton $equivalencyAutomaton = new slAutomaton(); foreach ($this->equivalenceClasses as $name => $nodes) { $equivalencyAutomaton->addNode($name); foreach ($nodes as $node) { foreach ($automaton->getOutgoing($node) as $dst) { if ($name !== $equivalent[$dst]) { $equivalencyAutomaton->addEdge($name, $equivalent[$dst]); } } } } return $equivalencyAutomaton; }
/** * Add a directed edge to the graph * * @param string $src * @param string $dst * @return void */ public function addEdge($src, $dst, $label = true) { parent::addEdge($src, $dst); $this->reverseEdges[(string) $dst][(string) $src] = $label; }