GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
1/**************************************************************************23coinc.c4Colin Ramsay ([email protected])511 Dec 0067ADVANCED COSET ENUMERATOR, Version 3.00189Copyright 200010Centre for Discrete Mathematics and Computing,11Department of Mathematics and12Department of Computer Science & Electrical Engineering,13The University of Queensland, QLD 4072.14(http://staff.itee.uq.edu.au/havas)1516This is the coincidence handling for the coset enumerator. Conceptually,17this is straightforward, but in practice the details can be a trifle18intimidating (mood-altering chemicals help). The current strategy is19simple; we process a (primary) coincidence, and any consequent (secondary)20coincidences, immediately & completely. (We may, or may not, stack21deductions, depending on the saved flag.) Thus, outside the coincidence22handling routines, the coincidence queue is empty. We never `defer'23processing primary coincidences and we never discard them. Processing24coincidences can cause a table collapse (index=1), or can result in the25enumeration completing (finite index). We detect the first of these26(returning 1), but not the second (since it would involve `speculative'27computation).2829It would be nice to decouple queueing a primary coincidence from processing30it. However, since the queue is stored in the table, queueing a coinc31means altering the table & (maybe) generating more coincidences. Further,32a table with queued coincidences is inconsistent, in the sense that entries33in the rows of non-redundant cosets can refer to redundant cosets. It34would be quite feasible to have a (small, fixed size) auxiliary queue where35we could store (some) primary coincs as they are discovered without36processing them immediately; but this would probably not be beneficial.3738Note that *during* coincidence handling, as noted above, the table is39inconsistent. So we have to continue processing until there are no more40coincs queued to ensure that the table will be consistent when we exit.41Thus we can't bail out early, with processing outstanding, except under42very special circumstances (eg, collapse to index=1). Even if we detect a43big collapse, and want to bail out (abandoning any stored deductions (we44could also stop queueing *new* coincidences!)), we need to process all45coincs before we can exit. Similarly, if all the cosets between knr or knh46& nextdf become redundant, then we know (if we choose to detect this state)47that we *will* finish. However, we need to continue to `fix up' the table48and to determine what the final index is (it could be *less* than the value49of nalive when guaranteed finishing was noted).5051Note that the coinc handling routines are predicated on the fact that the52table has at least two columns, and that the first two of these are an a &53A pair or an a/A & b/B pair. This ensures that, eg, if N.a = M, then the54entry for M.A = N is also within the first two columns. Note also that the55arguments to the various coincidence processing routines must be valid56coset numbers (ie, 1 <= x < nextdf). If not, all bets are off!5758**************************************************************************/5960#include "al0.h"6162/******************************************************************63During the special coincidence processing of columns 1 & 2, at most64two further coincidences can be pending at any one time. These are65stored in low1s/high1s & low2s/high2s. This macro saves a (new)66coincidence in a free slot. Note that clo & chi are >0, and that67low1s/low2s =0 indicate an empty slot.68******************************************************************/6970#define SAVE12(clo,chi) \71INCR(xsave12); \72if (clo != chi) \73{ \74if (clo > chi) \75{ SWAP(clo, chi); } \76if (low1s == clo && high1s == chi) \77{ INCR(s12dup); } \78else if (low2s == clo && high2s == chi) \79{ INCR(s12dup); } \80else \81{ \82INCR(s12new); \83if (low1s == 0) \84{ low1s = clo; high1s = chi; } \85else \86{ low2s = clo; high2s = chi; } \87} \88}8990/******************************************************************91CREP(path,rep) traces back through coincidence queue, starting at92path, to find which coset path is ultimately equal to; rep is set93to this value (we can have rep=path). COMPRESS(path,rep) resets94all cosets along path's path to point to rep, to speed up future95processing (we hope; cf. Union Find problem). We always have to96find reps during coincidence processing (so that we put information97in the correct place & move it as infrequently as possible), but98whether or not compressing the paths as stored in the coinc list is99beneficial is a moot point. Continually trying to compress paths100which are already `essentially' compressed may waste more time than101it saves! The pcomp flag allows compression to be turned off. At102a guess, if the enumeration is `large' and the number of secondary103coincs per primary coinc is `large', then compression is104beneficial; otherwise, it wastes more time than it saves.105106Note that these do *not* trace through, or disturb in any way, the107coincidence queue (which is stored in column 2), but merely108trace/reset the coset pointed to (in column 1) by those members of109the queue with which path is coincident.110111Note that, if we want to find path's current rep *and* compress its112path down to this, then it is more efficient to combine the113routines into one, as was done in ACE2. However, in _cols12() we114have to find both reps first, and then compress (if compression on)115both of them down to the smaller, so we couldn't use the combined116routine there.117******************************************************************/118119#define CREP(path,rep) \120INCR(xcrep); \121if ((i = COL1(path)) < 0) \122{ \123INCR(crepred); \124while ((j = COL1(-i)) < 0) \125{ \126INCR(crepwrk); \127i = j; \128} \129rep = -i; \130} \131else \132{ rep = path; }133134#define COMPRESS(path,rep) \135INCR(xcomp); \136l = path; \137while ((j = COL1(l)) < 0) \138{ \139INCR(compwrk); \140COL1(l) = -rep; \141l = -j; \142}143144/******************************************************************145static Logic al0_chk1(void)146147This routine is called only by al0_cols12, and only when nalive=1148and CT(1,1) & CT(1,2) are defined. al0_coinc() has already149collapsed all information in positions 1/2 (destroying the entries150there in the process); thus, if all other entries in coset 1's row151are defined (or are coincident with defined entries), then the152index must be 1; i.e., *all* the cosets are coincident and *all*153entries in row 1 are defined (as 1, or synonyms thereof).154155Note that this routine does not (and, indeed, cannot (simply,156anyway)) distinguish between coincidences consequent on the current157primary coincidence and those from a previous primary coincidence.158However, *provided* that all previous coincidences (that were159processed) were fully processed then any data (in any col>2) in any160row of the table is either valid or has been copied to a valid row.161So, any non-zero entry means that the corresponding col in row 1162*will* be non-zero.163******************************************************************/164165static Logic al0_chk1(void)166{167int i, j;168169for (j = 3; j <= ncol; j++)170{171if (CT(1,j) != 0)172{ continue; }173174/* If CT(1,j)==0, look down column j for *any* non-zero entry. */175176for (i = 2; i < nextdf; i++)177{178if (CT(i,j) != 0)179{ goto conti; }180}181return(FALSE); /* column j has no defined entry */182183conti: /* continue, to next column */184; /* prevent non-ANSI warning ! */185}186187/* Index *is* 1: set all entries in first row to 1 and bump knr/knh up to188nextdf (& nextdf down to 2). */189190for (i = 1; i <= ncol; i++)191{ CT(1,i) = 1; }192knr = knh = nextdf = 2;193194/* Wipe out the coincidence list and any outstanding pd's. Empty the195dedn stack & say there were no discards. The SG phase is unnecessary. */196197chead = ctail = 0;198toppd = botpd = 0;199topded = -1;200disded = FALSE;201sgdone = TRUE;202203return(TRUE);204}205206/******************************************************************207static Logic al0_cols12(int low, int high, Logic saved)208209Process cols 1 and 2 of cosets low = high and their consequences.210While handling the coincidences coming from the processing of the211first 2 columns and the possible coincidences arising from them, we212have at most 2 more unprocessed coincidences which we need to save213somewhere to have their columns 1 and 2 processed later. Thus we214set aside 4 locations (low1s, high1s; low2s, high2s) to store such215coincident cosets as may arise. Note that a total collapse (ie,216index=1) may occur, in which case we return TRUE (if not, FALSE).217This routine is only called from al0_coinc, as part of our strategy218of fully processing all coincidences immediately.219220Note that on the first pass thro the loop, low & high are the input221arguments. On subsequent passes (if any) they are consequences of222the data in cols 1/2 of an earlier pass. When we queue & process223coincidences, we always copy data from high nos to low nos and mark224the high nos as redundant & pointing to the low on the queue.225226In general, we enter our main loop with only one save slot (the one227we've just removed to process) empty. It may appear that228processing this can generate *two* more coincidences to be saved.229However, this is only true on the *first* pass through the loop,230when both slots are empty. On subsequent passes, the coincidence231being processed was generated by an earlier coincidence, and232processing this has removed an entry from it (via processing an233inverse entry). So at most *one* new coincidence can be generated.234******************************************************************/235236static Logic al0_cols12(int low, int high, Logic saved)237{238int i, j, l; /* for CREP()/COMPRESS() macros */239int low1s, low2s, high1s, high2s; /* consequent coincidences */240int inv1, inv2; /* column inverses */241int rlow, rhigh; /* reps of low/high */242int src, dst; /* source & dest'n for info move */243int low1, low2, high1, high2; /* original data from cols 1/2 */244int lowi; /* temp */245246INCR(xcols12);247248if (low == high) /* Paranoia prevents problems */249{ return(FALSE); }250251low1s = low2s = 0;252253inv1 = invcol[1]; /* Make these globals ? */254inv2 = invcol[2];255256while (TRUE)257{258CREP(low,rlow);259CREP(high,rhigh);260if (rlow <= rhigh)261{ src = rhigh; dst = rlow; }262else263{ src = rlow; dst = rhigh; }264265/* If the two reps are equal there's nothing to do (ie, no info to266move) & we jump over this if(). If not, we're in one of four states,267depending as low (high) is (is not) redundant. In any event, both src268& dst are *not* redundant, and data from cols 1/2 has to be moved from269src to dst (since queueing src as coincident overwrites this data).270Since a coset is queued (made redundant) as its data is processed, all271relevant data is processed once only & is moved to the smallest coset272currently known to be equivalent. If dst later becomes redundant this273is ok, since it will be queued, and later dequeued, *after* src. */274275if (src != dst)276{277/* Mark src coincident with dst and queue the coincidence, recording278the values of CT(src,1) & CT(src,2) before we destroy them! */279280high1 = COL1(src);281high2 = COL2(src);282283COL1(src) = -dst;284if (chead == 0)285{ chead = src; }286else287{ COL2(ctail) = src; }288ctail = src;289COL2(src) = 0;290291INCR(qcoinc);292293/* To check that the following is correct, you have to check the294cases where cols 1 & 2 are a/A & b/B or a & A separately. For each295of these, you have to consider all possible patterns of entries in296rows scr & dst (0, src, dst, X, Y), and check that the right thing is297always done. Note that we are guaranteed that at least one, but not298necessarily both, of low1s/high1s & low2s/high2s are free at this299point. This code could be rewritten to be *much* clearer; it would300be a lot longer, but whether or not it would be faster is moot.301302Note that at this point, CT(src,1) & CT(src,2) contain coinc queue303info and must *not* be altered; so we have to take care in the304handling of inverse entries and/or if any of low1s/high1s or305low2s/high2s equal src. */306307/* Look at the consequences of column 1 of rows src & dst. */308309if (high1 != 0)310{311/* Delete ct(high1, inv1) at this stage rather than replace by dst312to avoid having two occurrences of dst in the one column. */313314if (high1 != src)315{ CT(high1,inv1) = 0; }316else317{ high1 = dst; }318319if ((low1 = COL1(dst)) != 0) /* note the coincidence */320{ SAVE12(low1, high1); }321else /* note the deduction */322{323COL1(dst) = high1;324if (saved)325{ SAVED(dst,1); }326}327328if ((lowi = COL1(dst)) != 0 && CT(lowi,inv1) == 0 && lowi != src)329{ CT(lowi,inv1) = dst; }330}331332/* Look at the consequences of column 2 of rows src & dst. */333334if (high2 != 0)335{336/* Delete ct(high2, inv2) at this stage rather than replace by dst337to avoid having two occurrences of dst in the one column. */338339if (high2 != src)340{ CT(high2,inv2) = 0; }341else342{ high2 = dst; }343344if ((low2 = COL2(dst)) != 0) /* note the coincidence */345{ SAVE12(low2,high2); }346else /* note the deduction */347{348COL2(dst) = high2;349if (saved)350{ SAVED(dst,2); }351}352353if ((lowi = COL2(dst)) != 0 && CT(lowi,inv2) == 0 && lowi != src)354{ CT(lowi,inv2) = dst; }355}356357/* Adjust nalive & check to see if we've hit the jackpot. Also see358if we have to fire up a message. */359360if (--nalive == 1 && COL1(1) != 0 && COL2(1) != 0)361{362if (al0_chk1())363{ return(TRUE); }364}365366#ifdef AL0_CC367if (msgctrl && --msgnext == 0)368{369msgnext = msgincr;370ETINT;371fprintf(fop, "CC: a=%d r=%d h=%d n=%d;", nalive, knr, knh, nextdf);372MSGMID;373fprintf(fop, " d=%d\n", topded+1);374BTINT;375}376#endif377}378379/* Now compress both paths down to dst, if required. This *may*380speed up future calls to CREP (on ave). Also, if CREP is *not* used381in al0_coinc, it can dramatically decrease the amount of information382moved & deductions stacked when processing cols >=3 (ie, cded's). Of383course, lots of these stacked ded'ns will be redundant, but still. */384385if (pcomp)386{387COMPRESS(high,dst);388COMPRESS(low,dst);389}390391/* After processing high (=rhigh) = low (=rlow) ==> dst, we can remove392this, and any coincidences rendered redundant, from the stored pair.393Note that we must preserve the pair's order here, so that SAVE12 works394ok. Is it necessary to check *all* these cases? */395396if (low1s != 0)397{398if (low1s == high || low1s == low || low1s == src)399{ low1s = dst; }400if (high1s == high || high1s == low || high1s == src)401{ high1s = dst; }402403if (low1s == high1s)404{ low1s = 0; }405else if (low1s > high1s)406{ SWAP(low1s, high1s); }407}408409if (low2s != 0)410{411if (low2s == high || low2s == low || low2s == src)412{ low2s = dst; }413if (high2s == high || high2s == low || high2s == src)414{ high2s = dst; }415416if (low2s == high2s)417{ low2s = 0; }418else if (low2s > high2s)419{ SWAP(low2s, high2s); }420}421422/* Find the next coincident pair to process. */423424if (low1s != 0)425{426low = low1s;427low1s = 0;428high = high1s;429}430else if (low2s != 0)431{432low = low2s;433low2s = 0;434high = high2s;435}436else /* nothing left to do */437{ return(FALSE); }438}439}440441/******************************************************************442int al0_coinc(int low, int high, Logic saved)443444Process the primary coincidence low = high and its consequences.445This routine (well, al0_cols12 actually) uses the idea described by446Beetham ("Space saving in coset enumeration", Durham Proceedings447(Academic Press, 1984)) but not the data structure. It uses the448data structure used in CDHW ("Implementation and analysis of the449Todd-Coxeter algorithm", Mathematics of Computation, 1973), with450some modifications.451452If saved is TRUE, we save any deductions on the stack. (In the old453adaptive stategy we were free not to do this, or to detect a `big'454collapse `early' and stop recording deductions (& new coincs?) &455throw away any existing ones.) If we have a collapse to 1 in456al0_cols12, we return 1, having adjusted knr/knh/nextdf. We choose457*not* to do any speculative checking as to whether or not knr/knh458bumps into nextdf, which would imply a finite index (although not459necessarily =nalive), since this would not give an early result460frequently enough to justify its cost. So, apart from the collapse461to 1 case, we return -1 and do not change knr/knh/nextdf. However,462the cosets pointed to by knr/knh *can* become redundant, and it is463the caller's responsibility to check for this and take apporpriate464action.465466Since we fully process all primary coincidences as they occur, the467coincidence queue is guaranteed empty at entry and when we return.468We throw away any outstanding p.d.'s, since they're (probably)469invalid & it's too much trouble to sort it all out. We may exit470this routine with a large deduction stack, so we try to cull471redundant entries from this (if permitted by dedmode). However, we472can do little regarding duplicate entries, or entries of a473deduction & it's inverse.474******************************************************************/475476int al0_coinc(int low, int high, Logic saved)477{478int i, j; /* Temps / for macros */479int lowi, highi;480int chigh, clow, crep; /* current high, low & rep */481482/* The xcoinc statistic counts the number of calls to this function. We483drop out immediately if we're called `needlessly'. */484485INCR(xcoinc);486487if (low == high)488{ return(-1); }489490/* Process columns 1 and 2 of the primary coincidence. */491492if (al0_cols12(low,high,saved))493{ return(1); }494495/* While there are coincidences on the queue, process columns 3 to ncol496of the coincidence chigh=clow. Note that crep <= clow < chigh is497guaranteed. When chigh = clow was queued, clow was non-redundant and498the rep of chigh. This may no longer be true, so we could pick up the499current rep of clow (chigh *must* be left alone). Formally, there is no500problem if we do not do this, since if clow is now redundant it was501queued *after* chigh. So all we'd do is move info to clow, and then move502it again when clow is processed.503504The (optional) path compression code in 3.000 has been removed. */505506while (chead != 0)507{508chigh = chead;509crep = clow = -COL1(chigh);510chead = COL2(chead); /* dequeue coinc being processed */511512for (i = 3; i <= ncol; i++)513{514/* highi - column i entry of coset chigh */515if ((highi = CT(chigh, i)) == 0)516{ continue; }517j = invcol[i];518519/* Delete CT(highi,j) at this stage rather than replace by crep to520avoid having two occurrences of crep in the one column. */521522if (highi != chigh)523{ CT(highi,j) = 0; }524else525{ highi = crep; }526527/* lowi - column i entry for coset crep */528if ((lowi = CT(crep,i)) != 0)529{530if (lowi == chigh)531{ lowi = crep; }532533/* We have found a (possibly new) coincidence highi=lowi. */534535if (al0_cols12(lowi,highi,saved))536{ return(1); }537}538else539{ /* Mark new ded'n for later processing? */540CT(crep,i) = highi;541if (saved)542{ SAVED(crep,i); }543}544545if ((lowi = CT(crep, i)) != 0 && CT(lowi, j) == 0)546{ CT(lowi, j) = crep; }547}548}549550chead = ctail = 0; /* guaranteed empty coincidence list */551toppd = botpd = 0; /* pd's no longer valid */552553/* At this stage we may or may not have a `large' stack, and it may or554may not contain redundancies/duplicates/inverses. We have a choice of555many things to do with it ... At some point we might want to add some556special tracing code to find out just what's in the stack! */557558switch(dedmode)559{560case 1:561while(topded >= 0 && COL1(dedrow[topded]) < 0)562{ topded--; }563break;564565case 2:566/* Delete all entries referencing dead cosets from the list of567deductions, by `compacting' the stack. We make no attempt to cull568duplicate or `inverse' entries. */569570j = -1;571i = 0;572while (i <= topded && COL1(dedrow[i]) >= 0)573{ j++; i++; }574for ( ; i <= topded; i++)575{576if (COL1(dedrow[i]) >= 0)577{578dedrow[++j] = dedrow[i];579dedcol[j] = dedcol[i];580}581}582topded = j;583break;584}585586return(-1);587}588589590591