So, these are some tips and tricks for using the library that might be helpful.
The crate consists of two main section
- perm
- group
The first section deals mostly with permutations, which are the basic data structure used. The second is where most of the interesting algorithms lies, which deal with groups generated by finitely many permutations.
The main object you will probably need to understand is the Group
object. It stores a group as a finite list of generators, and it can be used for all sort of operations. Note that Group
is actually a Group<P>
, where P
is of type DefaultPermutation
. The rationale for this is that we would like in general to be not too verbose to specify a group, but allow the user to specify which permutation they might want. The Group
object provides a library of commonly used group types, such as trivial
, klein_4
, dihedral_2n
, cyclic
, alternating
, symmetric
, which are all instantieted in the DefaultPermutation
type. Also, it is always possible to create a Group<P>
from an iterator of P
using FromIterator
or from an array of P
using Group::new
.
We suggest that if the user desires to use other kind of permutation for those groups they create them and use the Group::map
operation in the following way:
Group::symmetric(32).map(SyncPermutation::from)
Once a group object, say G
, is created there are lot of methods that can be used.
This are simple methods to massage the generating set of G
deduplicate
, make sure the generating set does not contain duplicatesrandom_generators
, creates a new generating set, iteratively adding points and computing stabchains until the size matchesrandom_n_generators
, tries to create a new generating set ofn
elements, note this might not terminateconjugate_gens
, conjugate all generators by a given element.
Ways to combine groups to get other groups, at the moment we only have product
, which takes two groups and computes the cartesian product of the groups.
The methods:
rng
rng_with_source
are used to generate random elements in the group. The second one of course takes in a randomness source that can be used for different needs. They return aRandPerm
object, which can be queried usingRandPerm::random_permutation
to get as many permutations as wished (of course limited by the size of the group).
It is very important to be able to compute orbits and transversals in permutation groups. We provide these in three methods:
orbit
, computes the orbit of a pointtransversal
, computes the transversal of a pointfactored_transversal
, computes the transversal of a point, stored in a factored manner.
Each method returns a struct with all the information easily queriable.
Each of these has an of_action
version, which takes any element of a type that implements Action
and allows to compute a general action. For example using MultiplicativeAction::default()
will apply those methods with the multiplicative action.
Finally, here is core of the module, the stabchain operations.
We start with some glossary. A selector is a type that implements BaseSelector
. It is used to control how the base is selected in a stabilizer chain
construction. There are three selectors and one adaptor that can be used to combine selectors:
LmpSelector
andFmpSelector
respectively select the last and first moved point by the permutationFixedBaseSelector
select a user defined basePartialSelector
can be used to combine any two of the above selectors, it will use the first selector for the the initial prefix and then switch to the second selector. For examplePartialSelector::new(FixedBaseSelector::new(&base[0..3]), 3, LmpSelector::default());
can be used to select the first 3 points of the fixed base and then switch to the default selection of using last moved point.
Now a strategy is a type that implements BuilderStrategy
. It is supposed to be a lightweight struct that specifies how the stabilizer chain should be constructed. There are three main ways strategies:
NaiveBuilderStrategy
computes a stabilizer chain using the deterministic algorithm and building full transversal. Should be fastest for small groups.IFTBuilderStrategy
computes a stabilizer chain using the deterministic algorithm using a factored transversal. Slower than the previous one but should be more memory efficientRandomBuilderStrategyNaive
uses the random algorithm to compute stabilizer chainsRandomBuilderStrategyShallow
uses the random algorithm with an optimization that makes the trees shallower
Each strategy can be created by passing in an action and a selector, and the random ones takes additionally some parameters that will be used in order to set up the constants for the algorithm.
The methods that are exposed on G
are as following:
stabchain
computes a stabilizer chain using the default strategy and the default selectorstabchain_base
computes a stabilizer chain using the default strategy and a fixed basestabchain_with_selector
computes a stabilizer chain using the default strategy and a given selectorstabchain_with_strategy
computes a stabilizer chain using a given strategy (that will also include a given selector)
As with permutation, we have provided DefaultStrategy
and DefaultSelector
as type alias for the default strategy and selectors.
The main export of this module is the Permutation
trait, which is an "interface" for a general group element. As such you can get an identity element, compute the multiplication of two permutations of the same type and the inverse of any permutation. What distinguishes such an object from a standard group element is that we can apply it to a point, so that we can use it to defines Action
on a desired set. We provide many implementation of this permutation trait, namely:
DefaultPermutation
The default chosen type for a permutation. The idea is that it will be chosen to be the most advantageous and it can be changed at will. It defaults toStandardPermutation
.StandardPermutation
A permutation which stores a list of images in a reference counted array. Good all size fits all, not thread safe.SyncPermutation
Same asStandardPermutation
, but the reference counting is done atomically, making it the best choice for multithreaded applications.WordPermutation
, a permutation that does lazy multiplication and application, can be useful for some operations such as in the random stabchain algorithmBasedPermutation
, a permutation that stores an offset, and will fix all points before that offset. Very useful if you are dealing with things like products which are implemented by shifting permutationMapPermutation
, a terrible permutation that stores the images as an HashMap. If it fixes many point it is very memory efficient, but benchmarks show that it is very slow so it is almost never the best choice.
Together with those we have some permutations that are to be used mostly for exporting and userfacing tasks, and as such they do not have computation capabilities. These are:
ClassicPermutation
which creates a permutation on[1..n]
from one on[0..n)
.CyclePermutation
which computes the cycle representation of a permutationExportablePermutation
which can be serialized with serde
All permutation types should be easily (some times not super efficiently) converted to one another, so for example you can seamlessly switch between StandardPermutation
and SyncPermutation
when needing to do something threaded.
So, the Action
trait is used in order to specify different actions for the same permutation group. The idea is that often we can would like to apply the same algorithm with different kind of actions, and we can pass an instance of this interface to select at compile time what that action will be. We provide implementations for the following three:
SimpleApplication
, uses theapply
method inPermutation
, so it computes an action on the point setConjugation
, acts on the permutation group itself, and computes the conjugateMultiplication
, acts on the permutation group itself by multiplying (on the right?)