/* schreier.c - procedures for manipulating a permutation group using * the random schreier algorithm. There is a separate file schreier.txt * which describes the usage. * * Written for nauty and traces, Brendan McKay 2010-2013.
*/
staticvoid
testispermutation(int id, int *p, int n) /* For debugging purposes, crash with a message if p[0..n-1] is
not a permutation. */
{ int i,m;
DYNALLSTAT(set,seen,seen_sz);
for (i = 0; i < n; ++i) if (p[i] < 0 || p[i] > n) break;
if (i < n)
{
fprintf(stderr,">E Bad permutation (id=%d): n=%d p[%d]=%d\n",
id,n,i,p[i]); exit(1);
}
m = SETWORDSNEEDED(n);
DYNALLOC1(set,seen,seen_sz,m,"malloc seen");
EMPTYSET(seen,m);
for (i = 0; i < n; ++i)
{ if (ISELEMENT(seen,p[i]))
{
fprintf(stderr, ">E Bad permutation (id=%d): n=%d p[%d]=%d is a repeat\n",
id,n,i,p[i]); exit(1);
}
ADDELEMENT(seen,p[i]);
}
}
int
schreier_fails(int nfails) /* Set the number of consecutive failures for filtering; * A value of <= 0 defaults to SCHREIERFAILS.
* The function value is the previous setting. */
{ int prev;
void
freeschreier(schreier **gp, permnode **gens) /* Free schreier structure and permutation ring. Assume this is everything. */ /* Use NULL for arguments which don't need freeing. */
{
schreier *sh,*nextsh;
permnode *p,*nextp;
if (gp && *gp)
{
nextsh = *gp; while (nextsh)
{
sh = nextsh;
nextsh = sh->next;
sh->next = schreier_freelist;
schreier_freelist = sh;
}
*gp = NULL;
}
if (gens && *gens)
{
p = *gens; do
{
nextp = p->next;
p->next = permnode_freelist;
permnode_freelist = p;
p = nextp;
} while (p != *gens);
*gens = NULL;
}
}
permnode*
findpermutation(permnode *pn, int *p, int n) /* Return a pointer to permutation p in the circular list,
* or NULL if it isn't present. */
{
permnode *rn; int i;
if (!pn) return NULL;
rn = pn; do
{ for (i = 0; i < n; ++i) if (rn->p[i] != p[i]) break; if (i == n) return rn;
rn = rn->next;
} while (rn != pn);
void
addpermutation(permnode **ring, int *p, int n) /* Add new permutation to circular list, marked.
* and return pointer to it in *ring. */
{
permnode *pn,*rn;
staticvoid
addpermutationunmarked(permnode **ring, int *p, int n) /* Add new permutation to circular list, not marked.
* and return pointer to it in *ring. */
{
TESTP(3,p,n);
addpermutation(ring,p,n);
(*ring)->mark = 0;
}
boolean
addgenerator(schreier **gp, permnode **ring, int *p, int n) /* Add new permutation to group, unless it is discovered to be * already in the group. It is is possible to be in the group * and yet this fact is not discovered. * Return TRUE if the generator (or an equivalent) is added or the
* group knowledge with the current partial base is improved. */
{
TESTP(2,p,n); return filterschreier(*gp,p,ring,FALSE,-1,n);
}
boolean
condaddgenerator(schreier **gp, permnode **ring, int *p, int n) /* Add new permutation to group, unless it is discovered to be * already in the group. It is is possible to be in the group * and yet this fact is not discovered, but this version will * always notice if this permutation precisely is present. * Return TRUE if the generator (or an equivalent) is added or the
* group knowledge with the current partial base is improved. */
{
TESTP(4,p,n); if (findpermutation(*ring,p,n)) returnFALSE; else return filterschreier(*gp,p,ring,FALSE,-1,n);
}
staticvoid
clearvector(permnode **vec, permnode **ring, int n) /* clear vec[0..n-1], freeing permnodes that have no other references
* and are not marked */
{ int i;
for (i = 0; i < n; ++i) if (vec[i])
{ if (vec[i] != ID_PERMNODE)
{
--(vec[i]->refcount); if (vec[i]->refcount == 0 && !vec[i]->mark)
{
*ring = vec[i];
delpermnode(ring);
}
}
vec[i] = NULL;
}
}
void
newgroup(schreier **sh, permnode **ring, int n) /* Make the trivial group, allow for ring to be set elsewhere */
{
*sh = newschreier(n);
initschreier(*sh,n); if (ring) *ring = NULL;
}
staticvoid
applyperm(int *wp, int *p, int k, int n) /* Apply the permutation p, k times to each element of wp */
{ int i,j,cyclen,kk,m;
TESTP(1,p,n);
if (k <= 5)
{ if (k == 0) return; elseif (k == 1) for (i = 0; i < n; ++i) wp[i] = p[wp[i]]; elseif (k == 2) for (i = 0; i < n; ++i) wp[i] = p[p[wp[i]]]; elseif (k == 3) for (i = 0; i < n; ++i) wp[i] = p[p[p[wp[i]]]]; elseif (k == 4) for (i = 0; i < n; ++i) wp[i] = p[p[p[p[wp[i]]]]]; elseif (k == 5) for (i = 0; i < n; ++i) wp[i] = p[p[p[p[p[wp[i]]]]]];
} elseif (k <= 19)
{ #if !MAXN
DYNALLOC1(int,workpermA,workpermA_sz,n,"applyperm"); #endif for (i = 0; i < n; ++i) workpermA[i] = p[p[p[i]]]; for (; k >= 6; k -= 6) for (i = 0; i < n; ++i) wp[i] = workpermA[workpermA[wp[i]]]; if (k == 1) for (i = 0; i < n; ++i) wp[i] = p[wp[i]]; elseif (k == 2) for (i = 0; i < n; ++i) wp[i] = p[p[wp[i]]]; elseif (k == 3) for (i = 0; i < n; ++i) wp[i] = workpermA[wp[i]]; elseif (k == 4) for (i = 0; i < n; ++i) wp[i] = p[workpermA[wp[i]]]; elseif (k == 5) for (i = 0; i < n; ++i) wp[i] = p[p[workpermA[wp[i]]]];
} else
{
m = SETWORDSNEEDED(n); #if !MAXN
DYNALLOC1(int,workpermA,workpermA_sz,n,"applyperm");
DYNALLOC1(int,workpermB,workpermB_sz,n,"applyperm");
DYNALLOC1(set,workset2,workset2_sz,m,"applyperm"); #endif
EMPTYSET(workset2,m);
/* We will construct p^k in workpermB one cycle at a time. */
for (i = 0; i < n; ++i)
{ if (ISELEMENT(workset2,i)) continue; if (p[i] == i)
workpermB[i] = i; else
{
cyclen = 1;
workpermA[0] = i; for (j = p[i]; j != i; j = p[j])
{
workpermA[cyclen++] = j;
ADDELEMENT(workset2,j);
}
kk = k % cyclen; for (j = 0; j < cyclen; ++j)
{
workpermB[workpermA[j]] = workpermA[kk]; if (++kk == cyclen) kk = 0;
}
}
} for (i = 0; i < n; ++i) wp[i] = workpermB[wp[i]];
}
}
static boolean
filterschreier(schreier *gp, int *p, permnode **ring,
boolean ingroup, int maxlevel, int n) /* Filter permutation p up to level maxlevel of gp. * Use ingroup=TRUE if p is known to be in the group, otherwise * at least one equivalent generator is added unless it is proved * (nondeterministically) that it is in the group already. * maxlevel < 0 means no limit, maxlevel=0 means top level only, etc.
* Return TRUE iff some change is made. */
{ int i,j,j1,j2,lev; int ipwr;
schreier *sh; int *orbits,*pwr;
permnode **vec,*curr;
boolean changed,lchanged,ident; #if !MAXN
DYNALLOC1(int,workperm,workperm_sz,n,"filterschreier"); #endif
++filtercount;
memcpy(workperm,p,n*sizeof(int));
if (*ring && p == (*ring)->p)
{
ingroup = TRUE;
curr = *ring;
} else
curr = NULL;
/* curr is the location of workperm in ring, if anywhere */
sh = gp;
changed = FALSE; if (maxlevel < 0) maxlevel = n+1;
for (lev = 0; lev <= maxlevel; ++lev)
{ for (i = 0; i < n; ++i) if (workperm[i] != i) break;
ident = (i == n); if (ident) break;
lchanged = FALSE;
orbits = sh->orbits;
vec = sh->vec;
pwr = sh->pwr; for (i = 0; i < n; ++i)
{
j1 = orbits[i]; while (orbits[j1] != j1) j1 = orbits[j1];
j2 = orbits[workperm[i]]; while (orbits[j2] != j2) j2 = orbits[j2];
if (j1 != j2)
{
lchanged = TRUE; if (j1 < j2) orbits[j2] = j1; else orbits[j1] = j2;
}
} if (lchanged) for (i = 0; i < n; ++i) orbits[i] = orbits[orbits[i]];
if (lchanged) changed = TRUE;
if (sh->fixed >= 0)
{ for (i = 0; i < n; ++i) if (vec[i] && !vec[workperm[i]])
{
changed = TRUE;
ipwr = 0; for (j = workperm[i]; !vec[j] ; j = workperm[j]) ++ipwr;
boolean
expandschreier(schreier *gp, permnode **ring, int n) /* filter random elements until schreierfails failures.
* Return true if it ever expanded. */
{ int i,j,nfails,wordlen,skips;
boolean changed;
permnode *pn; #if !MAXN
DYNALLOC1(int,workperm2,workperm2_sz,n,"expandschreier"); #endif
int*
getorbits(int *fix, int nfix, schreier *gp, permnode **ring, int n) /* Get a pointer to the orbits for this partial base. The pointer * remains valid until pruneset(), getorbits(), getorbitsmin() * or grouporder() is called with an incompatible base (neither a * prefix nor an extension). The contents of the array pointed to * MUST NOT BE MODIFIED by the calling program.
*/
{ int k;
schreier *sh,*sha;
sh = gp; for (k = 0; k < nfix; ++k)
{ if (sh->fixed != fix[k]) break;
sh = sh->next;
}
int
getorbitsmin(int *fix, int nfix, schreier *gp, permnode **ring, int **orbits, int *cell, int ncell, int n, boolean changed) /* If the basis elements fix[0..nfix-1] are minimal in their orbits, * as far as we know, return value nfix and set *orbits to point * to orbits fixing fix[0..nfix-1]. If fix[i] is seen to be not * minimal for some i <= nfix-1, return i and set *orbits to point * to orbits fixing fix[0..i-1]. If the partial base is already * known, or fix[0..nfix-1] can already be seen to be non-minimal, * do this work without more filtering. This shortcut is turned * off if changed==TRUE. Otherwise, filter until schreierfails * failures. * The pointer returned remains valid until pruneset(), getorbits(), * getorbitsmin() or grouporder() is called with an incompatible base * (neither a prefix nor an extension). The contents of the array * pointed to MUST NOT BE MODIFIED by the calling program. * If cell != NULL, return early if possible when cell[0..ncell-1] * are all in the same orbit fixing fix[0..nfix-1]. Otherwise * cell,ncell play no part in the computation.
*/
{
schreier *sh,*sha; int *fixorbs; int i,j,k,icell,nfails,wordlen,skips;
permnode *pn; #if !MAXN
DYNALLOC1(int,workperm2,workperm2_sz,n,"expandschreier"); #endif
sh = gp;
k = 0; if (!changed) for (k = 0; k < nfix; ++k)
{ if (sh->orbits[fix[k]] != fix[k])
{
*orbits = sh->orbits; return k;
} if (sh->fixed != fix[k]) break;
sh = sh->next;
}
void
pruneset(set *fixset, schreier *gp, permnode **ring, set *x, int m, int n) /* Remove from x any point not minimal for the orbits for this base. * If the base is already known, just provide the orbits without * more filtering. Otherwise, filter until schreierfails failures.
*/
{ int i,k;
schreier *sh,*sha; int *orbits;
#if !MAXN
DYNALLOC1(set,workset,workset_sz,m,"pruneset"); #endif for (i = 0; i < m; ++i) workset[i] = fixset[i];
sh = gp; while (sh->fixed >= 0 && ISELEMENT(workset,sh->fixed))
{
DELELEMENT(workset,sh->fixed);
sh = sh->next;
}
k = nextelement(workset,m,-1); if (k < 0)
orbits = sh->orbits; else
{
sh->fixed = k;
clearvector(sh->vec,ring,n);
sh->vec[k] = ID_PERMNODE;
for (sha = sh->next; sha ; sha = sha->next)
clearvector(sha->vec,ring,n);
while ((k = nextelement(workset,m,k)) >= 0)
{ if (!sh->next) sh->next = newschreier(n);
sh = sh->next;
initschreier(sh,n);
sh->fixed = k;
sh->vec[k] = ID_PERMNODE;
} if (!sh->next) sh->next = newschreier(n);
sh = sh->next;
initschreier(sh,n);
sh->fixed = -1;
if (*ring) expandschreier(gp,ring,n);
orbits = sh->orbits;
}
for (k = -1; (k = nextelement(x,m,k)) >= 0; ) if (orbits[k] != k) DELELEMENT(x,k);
}
void
grouporder(int *fix, int nfix, schreier *gp, permnode **ring, double *grpsize1, int *grpsize2, int n) /* process the base like in getorbits(), then return the product of the * orbits along the base, using the largest orbit at the end if the * base is not complete.
*/
{
schreier *sh; int i,j,k,fx; int *orb;
for (i = 0, sh = gp; i < nfix; ++i, sh = sh->next)
{
orb = sh->orbits;
fx = orb[sh->fixed];
k = 0; for (j = fx; j < n; ++j) if (orb[j] == fx) ++k;
MULTIPLY(*grpsize1,*grpsize2,k);
}
orb = sh->orbits;
k = 1; for (i = 0; i < n; ++i) if (orb[i] == i)
workperm[i] = 1; else
{
++workperm[orb[i]]; if (workperm[orb[i]] > k) k = workperm[orb[i]];
}
MULTIPLY(*grpsize1,*grpsize2,k);
}
/***************************************************************************** * * * schreier_check() checks that this file is compiled compatibly with the * * given parameters. If not, call exit(1). * * *
*****************************************************************************/
void
schreier_check(int wordsize, int m, int n, int version)
{ if (wordsize != WORDSIZE)
{
fprintf(ERRFILE,"Error: WORDSIZE mismatch in schreier.c\n"); exit(1);
}
#if MAXN if (m > MAXM)
{
fprintf(ERRFILE,"Error: MAXM inadequate in schreier.c\n"); exit(1);
}
if (n > MAXN)
{
fprintf(ERRFILE,"Error: MAXN inadequate in schreier.c\n"); exit(1);
} #endif
if (version < NAUTYREQUIRED)
{
fprintf(ERRFILE,"Error: schreier.c version mismatch\n"); exit(1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.