/* * Revision Control Information * * $Source$ * $Author$ * $Revision$ * $Date$ * */ /* * module: compl.c * purpose: compute the complement of a multiple-valued function * * The "unate recursive paradigm" is used. After a set of special * cases are examined, the function is split on the "most active * variable". These two halves are complemented recursively, and then * the results are merged. * * Changes (from Version 2.1 to Version 2.2) * 1. Minor bug in compl_lifting -- cubes in the left half were * not marked as active, so that when merging a leaf from the left * hand side, the active flags were essentially random. This led * to minor impredictability problem, but never affected the * accuracy of the results. */ #include "espresso.h" ABC_NAMESPACE_IMPL_START #define USE_COMPL_LIFT 0 #define USE_COMPL_LIFT_ONSET 1 #define USE_COMPL_LIFT_ONSET_COMPLEX 2 #define NO_LIFTING 3 static bool compl_special_cases(); static pcover compl_merge(); static void compl_d1merge(); static pcover compl_cube(); static void compl_lift(); static void compl_lift_onset(); static void compl_lift_onset_complex(); static bool simp_comp_special_cases(); static bool simplify_special_cases(); /* complement -- compute the complement of T */ pcover complement(T) pcube *T; /* T will be disposed of */ { register pcube cl, cr; register int best; pcover Tbar, Tl, Tr; int lifting; static int compl_level = 0; if (debug & COMPL) debug_print(T, "COMPLEMENT", compl_level++); if (compl_special_cases(T, &Tbar) == MAYBE) { /* Allocate space for the partition cubes */ cl = new_cube(); cr = new_cube(); best = binate_split_select(T, cl, cr, COMPL); /* Complement the left and right halves */ Tl = complement(scofactor(T, cl, best)); Tr = complement(scofactor(T, cr, best)); if (Tr->count*Tl->count > (Tr->count+Tl->count)*CUBELISTSIZE(T)) { lifting = USE_COMPL_LIFT_ONSET; } else { lifting = USE_COMPL_LIFT; } Tbar = compl_merge(T, Tl, Tr, cl, cr, best, lifting); free_cube(cl); free_cube(cr); free_cubelist(T); } if (debug & COMPL) debug1_print(Tbar, "exit COMPLEMENT", --compl_level); return Tbar; } static bool compl_special_cases(T, Tbar) pcube *T; /* will be disposed if answer is determined */ pcover *Tbar; /* returned only if answer determined */ { register pcube *T1, p, ceil, cof=T[0]; pcover A, ceil_compl; /* Check for no cubes in the cover */ if (T[2] == NULL) { *Tbar = sf_addset(new_cover(1), cube.fullset); free_cubelist(T); return TRUE; } /* Check for only a single cube in the cover */ if (T[3] == NULL) { *Tbar = compl_cube(set_or(cof, cof, T[2])); free_cubelist(T); return TRUE; } /* Check for a row of all 1's (implies complement is null) */ for(T1 = T+2; (p = *T1++) != NULL; ) { if (full_row(p, cof)) { *Tbar = new_cover(0); free_cubelist(T); return TRUE; } } /* Check for a column of all 0's which can be factored out */ ceil = set_save(cof); for(T1 = T+2; (p = *T1++) != NULL; ) { INLINEset_or(ceil, ceil, p); } if (! setp_equal(ceil, cube.fullset)) { ceil_compl = compl_cube(ceil); (void) set_or(cof, cof, set_diff(ceil, cube.fullset, ceil)); set_free(ceil); *Tbar = sf_append(complement(T), ceil_compl); return TRUE; } set_free(ceil); /* Collect column counts, determine unate variables, etc. */ massive_count(T); /* If single active variable not factored out above, then tautology ! */ if (cdata.vars_active == 1) { *Tbar = new_cover(0); free_cubelist(T); return TRUE; /* Check for unate cover */ } else if (cdata.vars_unate == cdata.vars_active) { A = map_cover_to_unate(T); free_cubelist(T); A = unate_compl(A); *Tbar = map_unate_to_cover(A); sf_free(A); return TRUE; /* Not much we can do about it */ } else { return MAYBE; } } /* * compl_merge -- merge the two cofactors around the splitting * variable * * The merge operation involves intersecting each cube of the left * cofactor with cl, and intersecting each cube of the right cofactor * with cr. The union of these two covers is the merged result. * * In order to reduce the number of cubes, a distance-1 merge is * performed (note that two cubes can only combine distance-1 in the * splitting variable). Also, a simple expand is performed in the * splitting variable (simple implies the covering check for the * expansion is not full containment, but single-cube containment). */ static pcover compl_merge(T1, L, R, cl, cr, var, lifting) pcube *T1; /* Original ON-set */ pcover L, R; /* Complement from each recursion branch */ register pcube cl, cr; /* cubes used for cofactoring */ int var; /* splitting variable */ int lifting; /* whether to perform lifting or not */ { register pcube p, last, pt; pcover T, Tbar; pcube *L1, *R1; if (debug & COMPL) { (void) printf("compl_merge: left %d, right %d\n", L->count, R->count); (void) printf("%s (cl)\n%s (cr)\nLeft is\n", pc1(cl), pc2(cr)); cprint(L); (void) printf("Right is\n"); cprint(R); } /* Intersect each cube with the cofactored cube */ foreach_set(L, last, p) { INLINEset_and(p, p, cl); SET(p, ACTIVE); } foreach_set(R, last, p) { INLINEset_and(p, p, cr); SET(p, ACTIVE); } /* Sort the arrays for a distance-1 merge */ (void) set_copy(cube.temp[0], cube.var_mask[var]); qsort((char *) (L1 = sf_list(L)), L->count, sizeof(pset), (int (*)()) d1_order); qsort((char *) (R1 = sf_list(R)), R->count, sizeof(pset), (int (*)()) d1_order); /* Perform distance-1 merge */ compl_d1merge(L1, R1); /* Perform lifting */ switch(lifting) { case USE_COMPL_LIFT_ONSET: T = cubeunlist(T1); compl_lift_onset(L1, T, cr, var); compl_lift_onset(R1, T, cl, var); free_cover(T); break; case USE_COMPL_LIFT_ONSET_COMPLEX: T = cubeunlist(T1); compl_lift_onset_complex(L1, T, var); compl_lift_onset_complex(R1, T, var); free_cover(T); break; case USE_COMPL_LIFT: compl_lift(L1, R1, cr, var); compl_lift(R1, L1, cl, var); break; case NO_LIFTING: break; default: ; } FREE(L1); FREE(R1); /* Re-create the merged cover */ Tbar = new_cover(L->count + R->count); pt = Tbar->data; foreach_set(L, last, p) { INLINEset_copy(pt, p); Tbar->count++; pt += Tbar->wsize; } foreach_active_set(R, last, p) { INLINEset_copy(pt, p); Tbar->count++; pt += Tbar->wsize; } if (debug & COMPL) { (void) printf("Result %d\n", Tbar->count); if (verbose_debug) cprint(Tbar); } free_cover(L); free_cover(R); return Tbar; } /* * compl_lift_simple -- expand in the splitting variable using single * cube containment against the other recursion branch to check * validity of the expansion, and expanding all (or none) of the * splitting variable. */ static void compl_lift(A1, B1, bcube, var) pcube *A1, *B1, bcube; int var; { register pcube a, b, *B2, lift=cube.temp[4], liftor=cube.temp[5]; pcube mask = cube.var_mask[var]; (void) set_and(liftor, bcube, mask); /* for each cube in the first array ... */ for(; (a = *A1++) != NULL; ) { if (TESTP(a, ACTIVE)) { /* create a lift of this cube in the merging coord */ (void) set_merge(lift, bcube, a, mask); /* for each cube in the second array */ for(B2 = B1; (b = *B2++) != NULL; ) { INLINEsetp_implies(lift, b, /* when_false => */ continue); /* when_true => fall through to next statement */ /* cube of A1 was contained by some cube of B1, so raise */ INLINEset_or(a, a, liftor); break; } } } } /* * compl_lift_onset -- expand in the splitting variable using a * distance-1 check against the original on-set; expand all (or * none) of the splitting variable. Each cube of A1 is expanded * against the original on-set T. */ static void compl_lift_onset(A1, T, bcube, var) pcube *A1; pcover T; pcube bcube; int var; { register pcube a, last, p, lift=cube.temp[4], mask=cube.var_mask[var]; /* for each active cube from one branch of the complement */ for(; (a = *A1++) != NULL; ) { if (TESTP(a, ACTIVE)) { /* create a lift of this cube in the merging coord */ INLINEset_and(lift, bcube, mask); /* isolate parts to raise */ INLINEset_or(lift, a, lift); /* raise these parts in a */ /* for each cube in the ON-set, check for intersection */ foreach_set(T, last, p) { if (cdist0(p, lift)) { goto nolift; } } INLINEset_copy(a, lift); /* save the raising */ SET(a, ACTIVE); nolift : ; } } } /* * compl_lift_complex -- expand in the splitting variable, but expand all * parts which can possibly expand. * T is the original ON-set * A1 is either the left or right cofactor */ static void compl_lift_onset_complex(A1, T, var) pcube *A1; /* array of pointers to new result */ pcover T; /* original ON-set */ int var; /* which variable we split on */ { register int dist; register pcube last, p, a, xlower; /* for each cube in the complement */ xlower = new_cube(); for(; (a = *A1++) != NULL; ) { if (TESTP(a, ACTIVE)) { /* Find which parts of the splitting variable are forced low */ INLINEset_clear(xlower, cube.size); foreach_set(T, last, p) { if ((dist = cdist01(p, a)) < 2) { if (dist == 0) { fatal("compl: ON-set and OFF-set are not orthogonal"); } else { (void) force_lower(xlower, p, a); } } } (void) set_diff(xlower, cube.var_mask[var], xlower); (void) set_or(a, a, xlower); free_cube(xlower); } } } /* * compl_d1merge -- distance-1 merge in the splitting variable */ static void compl_d1merge(L1, R1) register pcube *L1, *R1; { register pcube pl, pr; /* Find equal cubes between the two cofactors */ for(pl = *L1, pr = *R1; (pl != NULL) && (pr != NULL); ) switch (d1_order(L1, R1)) { case 1: pr = *(++R1); break; /* advance right pointer */ case -1: pl = *(++L1); break; /* advance left pointer */ case 0: RESET(pr, ACTIVE); INLINEset_or(pl, pl, pr); pr = *(++R1); default: ; } } /* compl_cube -- return the complement of a single cube (De Morgan's law) */ static pcover compl_cube(p) register pcube p; { register pcube diff=cube.temp[7], pdest, mask, full=cube.fullset; int var; pcover R; /* Allocate worst-case size cover (to avoid checking overflow) */ R = new_cover(cube.num_vars); /* Compute bit-wise complement of the cube */ INLINEset_diff(diff, full, p); for(var = 0; var < cube.num_vars; var++) { mask = cube.var_mask[var]; /* If the bit-wise complement is not empty in var ... */ if (! setp_disjoint(diff, mask)) { pdest = GETSET(R, R->count++); INLINEset_merge(pdest, diff, full, mask); } } return R; } /* simp_comp -- quick simplification of T */ void simp_comp(T, Tnew, Tbar) pcube *T; /* T will be disposed of */ pcover *Tnew; pcover *Tbar; { register pcube cl, cr; register int best; pcover Tl, Tr, Tlbar, Trbar; int lifting; static int simplify_level = 0; if (debug & COMPL) debug_print(T, "SIMPCOMP", simplify_level++); if (simp_comp_special_cases(T, Tnew, Tbar) == MAYBE) { /* Allocate space for the partition cubes */ cl = new_cube(); cr = new_cube(); best = binate_split_select(T, cl, cr, COMPL); /* Complement the left and right halves */ simp_comp(scofactor(T, cl, best), &Tl, &Tlbar); simp_comp(scofactor(T, cr, best), &Tr, &Trbar); lifting = USE_COMPL_LIFT; *Tnew = compl_merge(T, Tl, Tr, cl, cr, best, lifting); lifting = USE_COMPL_LIFT; *Tbar = compl_merge(T, Tlbar, Trbar, cl, cr, best, lifting); /* All of this work for nothing ? Let's hope not ... */ if ((*Tnew)->count > CUBELISTSIZE(T)) { sf_free(*Tnew); *Tnew = cubeunlist(T); } free_cube(cl); free_cube(cr); free_cubelist(T); } if (debug & COMPL) { debug1_print(*Tnew, "exit SIMPCOMP (new)", simplify_level); debug1_print(*Tbar, "exit SIMPCOMP (compl)", simplify_level); simplify_level--; } } static bool simp_comp_special_cases(T, Tnew, Tbar) pcube *T; /* will be disposed if answer is determined */ pcover *Tnew; /* returned only if answer determined */ pcover *Tbar; /* returned only if answer determined */ { register pcube *T1, p, ceil, cof=T[0]; pcube last; pcover A; /* Check for no cubes in the cover (function is empty) */ if (T[2] == NULL) { *Tnew = new_cover(1); *Tbar = sf_addset(new_cover(1), cube.fullset); free_cubelist(T); return TRUE; } /* Check for only a single cube in the cover */ if (T[3] == NULL) { (void) set_or(cof, cof, T[2]); *Tnew = sf_addset(new_cover(1), cof); *Tbar = compl_cube(cof); free_cubelist(T); return TRUE; } /* Check for a row of all 1's (function is a tautology) */ for(T1 = T+2; (p = *T1++) != NULL; ) { if (full_row(p, cof)) { *Tnew = sf_addset(new_cover(1), cube.fullset); *Tbar = new_cover(1); free_cubelist(T); return TRUE; } } /* Check for a column of all 0's which can be factored out */ ceil = set_save(cof); for(T1 = T+2; (p = *T1++) != NULL; ) { INLINEset_or(ceil, ceil, p); } if (! setp_equal(ceil, cube.fullset)) { p = new_cube(); (void) set_diff(p, cube.fullset, ceil); (void) set_or(cof, cof, p); set_free(p); simp_comp(T, Tnew, Tbar); /* Adjust the ON-set */ A = *Tnew; foreach_set(A, last, p) { INLINEset_and(p, p, ceil); } /* Compute the new complement */ *Tbar = sf_append(*Tbar, compl_cube(ceil)); set_free(ceil); return TRUE; } set_free(ceil); /* Collect column counts, determine unate variables, etc. */ massive_count(T); /* If single active variable not factored out above, then tautology ! */ if (cdata.vars_active == 1) { *Tnew = sf_addset(new_cover(1), cube.fullset); *Tbar = new_cover(1); free_cubelist(T); return TRUE; /* Check for unate cover */ } else if (cdata.vars_unate == cdata.vars_active) { /* Make the cover minimum by single-cube containment */ A = cubeunlist(T); *Tnew = sf_contain(A); /* Now form a minimum representation of the complement */ A = map_cover_to_unate(T); A = unate_compl(A); *Tbar = map_unate_to_cover(A); sf_free(A); free_cubelist(T); return TRUE; /* Not much we can do about it */ } else { return MAYBE; } } /* simplify -- quick simplification of T */ pcover simplify(T) pcube *T; /* T will be disposed of */ { register pcube cl, cr; register int best; pcover Tbar, Tl, Tr; int lifting; static int simplify_level = 0; if (debug & COMPL) { debug_print(T, "SIMPLIFY", simplify_level++); } if (simplify_special_cases(T, &Tbar) == MAYBE) { /* Allocate space for the partition cubes */ cl = new_cube(); cr = new_cube(); best = binate_split_select(T, cl, cr, COMPL); /* Complement the left and right halves */ Tl = simplify(scofactor(T, cl, best)); Tr = simplify(scofactor(T, cr, best)); lifting = USE_COMPL_LIFT; Tbar = compl_merge(T, Tl, Tr, cl, cr, best, lifting); /* All of this work for nothing ? Let's hope not ... */ if (Tbar->count > CUBELISTSIZE(T)) { sf_free(Tbar); Tbar = cubeunlist(T); } free_cube(cl); free_cube(cr); free_cubelist(T); } if (debug & COMPL) { debug1_print(Tbar, "exit SIMPLIFY", --simplify_level); } return Tbar; } static bool simplify_special_cases(T, Tnew) pcube *T; /* will be disposed if answer is determined */ pcover *Tnew; /* returned only if answer determined */ { register pcube *T1, p, ceil, cof=T[0]; pcube last; pcover A; /* Check for no cubes in the cover */ if (T[2] == NULL) { *Tnew = new_cover(0); free_cubelist(T); return TRUE; } /* Check for only a single cube in the cover */ if (T[3] == NULL) { *Tnew = sf_addset(new_cover(1), set_or(cof, cof, T[2])); free_cubelist(T); return TRUE; } /* Check for a row of all 1's (implies function is a tautology) */ for(T1 = T+2; (p = *T1++) != NULL; ) { if (full_row(p, cof)) { *Tnew = sf_addset(new_cover(1), cube.fullset); free_cubelist(T); return TRUE; } } /* Check for a column of all 0's which can be factored out */ ceil = set_save(cof); for(T1 = T+2; (p = *T1++) != NULL; ) { INLINEset_or(ceil, ceil, p); } if (! setp_equal(ceil, cube.fullset)) { p = new_cube(); (void) set_diff(p, cube.fullset, ceil); (void) set_or(cof, cof, p); free_cube(p); A = simplify(T); foreach_set(A, last, p) { INLINEset_and(p, p, ceil); } *Tnew = A; set_free(ceil); return TRUE; } set_free(ceil); /* Collect column counts, determine unate variables, etc. */ massive_count(T); /* If single active variable not factored out above, then tautology ! */ if (cdata.vars_active == 1) { *Tnew = sf_addset(new_cover(1), cube.fullset); free_cubelist(T); return TRUE; /* Check for unate cover */ } else if (cdata.vars_unate == cdata.vars_active) { A = cubeunlist(T); *Tnew = sf_contain(A); free_cubelist(T); return TRUE; /* Not much we can do about it */ } else { return MAYBE; } } ABC_NAMESPACE_IMPL_END