Performs the Hopcroft minimization algorithm
        Args:
            None
        Returns:
            DFA: The minimized input DFA

def hopcroft(self):
        """
        Performs the Hopcroft minimization algorithm
        Args:
            None
        Returns:
            DFA: The minimized input DFA
        """

        def _getset(testset, partition):
            """
            Checks if a set is in a partition
            Args:
                testset (set): The examined set
                partition (list): A list of sets
            Returns:
                bool: A value indicating if it is a member or not
            """
            for part in partition:
                if set(testset) == set(part):
                    return True
            return None

        def _create_transitions_representation(graph):
            """
            In order to speedup the transition iteration using
            the alphabet, the function creates an index
            Args:
                graph (DFA): The input dfa
                state (DFA state): The examined state
            Returns:
                dict: The generated transition map
            """
            return {x.stateid:{self.isyms.find(arc.ilabel): arc.nextstate \
                               for arc in x} for x in graph.states}

        def _create_reverse_transitions_representation(graph):
            """
            In order to speedup the transition iteration using
            the alphabet, the function creates an index
            Args:
                graph (DFA): The input dfa
                state (DFA state): The examined state
            Returns:
                dict: The generated transition map
            """
            return {x.stateid: {self.isyms.find(arc.ilabel): arc.nextstate \
                                for arc in x} for x in graph.states}

        def _reverse_to_source(target, group1):
            """
            Args:
                target (dict): A table containing the reverse transitions for each state
                group1 (list): A group of states
            Return:
                Set: A set of states for which there is a transition with the states of the group
            """
            new_group = []
            for dst in group1:
                new_group += target[dst]
            return set(new_group)

        def _get_group_from_state(groups, sid):
            """
            Args:
                sid (int): The state identifier
            Return:
                int: The group identifier that the state belongs
            """
            for index, selectgroup in enumerate(groups):
                if sid in selectgroup:
                    return index

        def _delta(graph, cur_state, char):
            """
            Function describing the transitions
            Args:
                graph (DFA): The DFA states
                cur_state (DFA state): The DFA current state
                char (str):: The char that will be used for the transition
            Return:
                DFA Node: The next state
            """
            for arc in cur_state.arcs:
                if graph.isyms.find(arc.ilabel) == char:
                    return graph[arc.nextstate]

        def _partition_group(bookeeping, group):
            """
            Args:
                group (list):  A group of states
            Return:
                tuple: A set of two groups
            """
            for (group1, group2) in bookeeping:
                if group & group1 != set() and not group.issubset(group1):
                    new_g1 = group & group1
                    new_g2 = group - group1
                    return (new_g1, new_g2)
                if group & group2 != set() and not group.issubset(group2):
                    new_g1 = group & group2
                    new_g2 = group - group2
                    return (new_g1, new_g2)
            assert False, "Unmatched group partition"

        def _object_set_to_state_list(objectset):
            """
            Args:
                objectset (list): A list of all the DFA states (as objects)
            Return:
                list: A list of all the DFA states (as identifiers)
            """
            return [state.stateid for state in objectset]

        def _get_accepted(graph):
            """
            Find the accepted states
            Args:
                graph (DFA): The DFA states
            Return:
                list: Returns the list of the accepted states
            """
            return [state for state in graph \
                    if  state.final != TropicalWeight(float('inf'))]

        graph = self

        # Find Q
        set_q = set(_object_set_to_state_list(graph.states))
        # We will work with states addresses here instead of states stateid for
        # more convenience
        set_f = set(_object_set_to_state_list(_get_accepted(graph)))
        # Perform P := {F, Q-F}
        set_nf = set_q.copy() - set_f.copy()
        groups = [set_f.copy(), set_nf.copy()]
        bookeeping = [(set_f, set_nf)]

        done = False
        while not done:
            done = True
            new_groups = []
            for selectgroup in groups:
                # _check for each letter if it splits the current group
                for character in self.alphabet:
                    # print 'Testing symbol: ', c
                    target = defaultdict(list)
                    target_states = defaultdict(int)
                    new_g = [set(selectgroup)]
                    for sid in selectgroup:
                        # _check if all transitions using c are going in a state
                        # in the same group. If they are going on a different
                        # group then split
                        deststate = _delta(graph, graph[sid], character)
                        destgroup = _get_group_from_state(groups,
                            deststate.stateid)
                        target[destgroup].append(sid)
                        target_states[destgroup] = deststate.stateid
                    if len(target) > 1:

                        inv_target_states = {
                            v: k for k, v in target_states.iteritems()}
                        new_g = [set(selectedstate) for selectedstate in target.values()]
                        done = False
                        # Get all the partitions of destgroups
                        queue = [set([x for x in target_states.values()])]
                        while queue:
                            top = queue.pop(0)
                            (group1, group2) = _partition_group(bookeeping, top)
                            ng1 = _reverse_to_source(
                                target, [inv_target_states[x] for x in group1])
                            ng2 = _reverse_to_source(
                                target, [inv_target_states[x] for x in group2])

                            bookeeping.append((ng1, ng2))

                            if len(group1) > 1:
                                queue.append(group1)
                            if len(group2) > 1:
                                queue.append(group2)
                        break
                new_groups += new_g

            # End of iteration for the k-equivalence
            # Assign new groups and check if any change occured
            groups = new_groups

        # Make a copy of the old states, and prepare the
        # automaton to host the minimum states

        oldstates = copy.deepcopy(self.states)
        self.states = []
        self.define()

        def findpart(stateid, partitions):
            """Searches for the groupt that the state identifier
            belongs to.
            Args:
                stateid (int): The state identifier
                partitions (list): The list of the groups
            Returns:
                set: The group that the stateid belongs to.
            """
            for group in partitions:
                if stateid in group:
                    return frozenset(group)
            return frozenset(set(            ))

        def add_state_if_not_exists(group, statesmap, final):
            """
            Adds a new state in the final dfa. It initialy checks if
            the group of states is already registered to the automaton.
            If it is registered, the state identifier is returned, or
            else, a new state is added.
            Args:
                group (frozenset):  The group that the state identifier belongs
                statesmap (dict):   A dictionary that maintains the state
                                    identifiers for each forzenset
                final (bool):       A value indicating if the current state is
                                    final
            Returns:
                int: The new state identifier
            """
            if group not in statesmap:
                sid = self.add_state()
                self[sid].final = final
                statesmap[group] = sid
            return statesmap[group]

        statesmap = {}
        self.states = []
        group = findpart(0, groups)
        sid = add_state_if_not_exists(frozenset(list(group)), statesmap,
                                      oldstates[0].final)
        self[sid].initial = True
        for group in groups:
            if len(group) == 0:
                continue
            sid = add_state_if_not_exists(frozenset(group), statesmap,
                                          oldstates[list(group)[0]].final)
            state = next(iter(group))
            for arc in oldstates[state]:
                dst_group = findpart(arc.nextstate, groups)
                dst_sid = add_state_if_not_exists(
                    dst_group, statesmap, oldstates[arc.nextstate].final)
                self.add_arc(sid, dst_sid, graph.isyms.find(arc.ilabel))