Using prefix automaton

To balance the cost of maintaining the reduced rewriting system with the cost of rewrites KBPrefix completion algorithm employs PrefixAutomaton for both purposes. There are two major advantages of using the automaton:

  • PrefixAutomaton can be created from a non-reduced rewriting system;
  • the KBPrefix reduction is an iterative process which can be interrupted before a fully reduced system is found;

Unfortunately rewrites with PrefixAutomaton are considerably slower than the ones using IndexAutomaton.

KnuthBendix.KBPrefixType
KBPrefix

Use PrefixAutomaton so that we don't need to maintain the reducedness of the rewriting system all the time. We use on the other hand the non-deterministic prefix automaton for (slower) rewrites.

source

You can run it on a rws by calling

knuthbendix(KnuthBendix.Settings(KnuthBendix.KBPrefix()), rws)

The default Settings are as follows


julia> KnuthBendix.Settings(KnuthBendix.KBPrefix())KnuthBendix.Settings{KnuthBendix.KBPrefix}: • algorithm : KnuthBendix.KBPrefix() • max_rules : 65536 • reduce_delay : 500 • confluence_delay : 500 • max_length_lhs : 9223372036854775807 • max_length_rhs : 9223372036854775807 • max_length_overlap : 9223372036854775807 • verbosity : 0 • collect_dropped : false

You may alter the default parameters you may pass any number of keywords to the constructor.

Reduction

Reduction of a rewriting system using PrefixAutomaton in julia-flavoured pseudocode looks as follows.

function reduce!(::KBPrefix, rws::RewritingSystem; kwargs...)
    while true
        pfxA = PrefixAutomaton(rws)
        redundant = 0
        altered = 0
        for rule in rules(rws)
            lhs, rhs = rule
            lhs_r = rewrite(lhs, pfxA, skipping=rule)
            rhs_r = rewrite(rhs, pfxA)
            lhs_r == lhs && rhs_r == rhs && continue # rule is reduced
            if lhs_r == rhs_r
                # rule is redundant
                redundant += 1
                deactivate!(rule)
            else # either lhs_r ≠ lhs or rhs_r ≠ rhs
                # rule is not reduced but it still carries some information
                # which does not follow from other rwrules
                altered += 1
                lhs_r, rhs_r = simplify!(lhs_r, rhs_r, ordering(rws))
                Words.store!(rule, lhs_r=>rhs_r)
            end
        end
        altered == 0 && redundant == 0 && break # rws is reduced now
        # some early breaking conditions based on kwargs may be placed here
    end
    remove_inactive!(rws) # clean up the rewriting system
    return rws
end

A single pass of the reduction looks as follow. We iterate over the rules of the rewriting system checking for one of the three possibilities.

  • Rule is reduced w.r.t. the remaining rewriting system. This happens when the only way to rewrite the rules' left hand side is to apply the rule to itself.
  • Rule is redundant w.r.t. the remaining rewriting system. This happens when the rule follows from the remaining rewriting rules, i.e. rewriting both sides of the rule (without applying the rule itself) leads to the same word.
  • The rule is neither reduced nor redundant. This happens when either of the sides of the rule can be rewritten w.r.t. the remaining rewriting system, but the results are different.

When the rule is reduced we keep it, when it is redundant we mark it as inactive and in the third case we alter the rule accordingly. The rewriting system is reduced when a fixed point (i.e. a set of rewriting rules which does not change) is reached.

Since PrefixAutomaton can be constructed from a non-reduced rewriting system there is no need to actually reach the fixed point! During KBPrefix completion the only place where a fully reduced rewriting system is needed is the early confluence check based on IndexAutomaton. Thus usually we can perform an early break of the reduction while loop e.g. after a certain number of passes or when the rewriting system did not change too much in the last pass. Even if a redundant rule survives the partial reduction this time, it will be sweeped away when partial reduction is run again.

This sometimes allows for great speed-ups in discovering new rules at the cost of operating non-reduced (hence larger) rewriting system.