Given a desired truth table, this program exhaustively searches
through combinations of NAND gates to find ways to produce this truth
table.  I'm sure e.g. VHDL synthesizers have better implementations of
this; on my 300MHz laptop, this takes about 2 seconds to run through
all the 5-gate circuits, which involves examining 549 067 candidate
circuits, and 75 seconds to run through all the 6-gate circuits.  (I'm
still running through all the 7-gate circuits as I write this.)  It
took some work to get even to this level of performance, but it still
runs through a lot of redundant work --- circuits that can't possibly
work, multiple permutations of the same circuit.

/*
 * search through combinations of NAND gates for a combinatorial
 * circuit producing a particular truth table
 *
 * BUGS:
 * - produces suboptimal output (3 gates) for circuits with always-0
 *   or always-1 output, and also (2 gates) for circuits whose output is
 *   simply one of their inputs, other than the last input.
 */

#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct {
  int ninputs, ngates; /* number of circuit inputs and number of gates */
  int input[1][2];  /* not really [1]... really [ngates+ninputs] */
  /* each input[a][b] contains input #b for gate #a.  gates < ninputs are dummy gates
   * whose output is a circuit input. */
} circuit;

circuit *newcircuit(int ninputs, int ngates) {
  circuit *cp = malloc(sizeof(*cp) + (ninputs + ngates - 1) * 2 * sizeof(int));
  if (!cp) return 0;
  cp->ninputs = ninputs;
  cp->ngates = ngates;
  return cp;
}

int evalcircuit(circuit *cp, int *inputs) {
  int noutputs = cp->ninputs + cp->ngates;
  int outputs[noutputs];  /* gcc extension to C: variable-size arrays */
  int ii;
  for (ii = 0; ii < cp->ninputs; ii++) outputs[ii] = inputs[ii];
  for (ii = cp->ninputs; ii < noutputs; ii++)
    outputs[ii] = !(outputs[cp->input[ii][0]] && outputs[cp->input[ii][1]]);
  return outputs[noutputs - 1];
}

char varname(int ii) { return 'A' + ii; }

int gate_output_used_more_than_once(circuit *cp, int gatenum) {
  int ii, jj;
  int uses = 0;
  for (ii = cp->ninputs; ii < cp->ninputs + cp->ngates; ii++) {
    for (jj = 0; jj < 2; jj++)
      if (cp->input[ii][jj] == gatenum) uses++;
  }
  return (uses > 1);
}

void print_gate_expression_or_var(circuit *cp, int gatenum);
void print_gate_expression(circuit *cp, int gatenum) {
  printf("-(");
  print_gate_expression_or_var(cp, cp->input[gatenum][0]);
  printf("*");
  print_gate_expression_or_var(cp, cp->input[gatenum][1]);
  printf(")");
}

void print_gate_expression_or_var(circuit *cp, int gatenum) {
  if (gatenum < cp->ninputs || gate_output_used_more_than_once(cp, gatenum)) {
    printf("%c", varname(gatenum));
  } else {
    print_gate_expression(cp, gatenum);
  }
}

void nicely_print_circuit(circuit *cp) {
  int ii;
  int output = cp->ninputs + cp->ngates - 1;
  for (ii = cp->ninputs; ii <= output; ii++)
    if (ii == output || gate_output_used_more_than_once(cp, ii)) {
      printf("%c = ", varname(ii));
      print_gate_expression(cp, ii);
      printf("\n");
    }
}

void printcircuit(circuit *cp) {
  int ii;
  for (ii = cp->ninputs; ii < cp->ninputs + cp->ngates; ii++)
    printf("%c = -(%c*%c)\n", 
           varname(ii), varname(cp->input[ii][0]), varname(cp->input[ii][1]));
}

void printentry(void *cd, circuit *cp, int *inputs, int output) {
  int ii;
  for (ii = 0; ii != cp->ninputs; ii++)
    printf("%d ", inputs[ii]);
  printf("-> %d\n", output);
}

void printentry_brief(void *cd, circuit *cp, int *inputs, int output) {
  printf("%d", output);
}

void enumtable(circuit *cp, void *client_data, 
               void (*cb)(void *client_data, 
                          circuit *cp, 
                          int *inputs, 
                          int output)
               ) {
  int inputs[cp->ninputs];
  int ii;
  for (ii = 0; ii < cp->ninputs; ii++) inputs[ii] = 0;
  nextline: for (;;) {
    (*cb)(client_data, cp, inputs, evalcircuit(cp, inputs));
    for (ii = cp->ninputs - 1; ii >= 0; ii--)
      if (!inputs[ii]) {
        for (; ii < cp->ninputs; ii++) 
          inputs[ii] = !inputs[ii];
        goto nextline;  /* we just horked ii */
      }
    return;
  }
}

void reset_circuit(circuit *cp) {
  int ii;
  for (ii = 0; ii < cp->ngates; ii++) {
    cp->input[cp->ninputs + ii][0] = 0;
    cp->input[cp->ninputs + ii][1] = 0;
  }
}

/* go to the lexically next valid circuit and return 1, 
 * or return 0 if that's not possible. */
int increment_circuit(circuit *cp) {
  /* in valid circuits, each input[ii][x] < ii, and of course >= 0. */
  /* We also enforce the condition that input[ii][1] <= input[ii][0],
   * because NAND is commutative; considering both -(B*A) and -(A*B)
   * doesn't help. */
  int ii;
  int min;
  for (ii = cp->ninputs + cp->ngates - 1; ii >= cp->ninputs; ii--) {
    min = ii - 1;
    if (cp->input[ii][0] < min) min = cp->input[ii][0];
    if (cp->input[ii][1] != min) {
      cp->input[ii][1]++;
    zero_lower_order_gates:
      for (ii++; ii < cp->ninputs + cp->ngates; ii++) {
        cp->input[ii][0] = 0;
        cp->input[ii][1] = 0;
      }
      return 1;
    }
    if (cp->input[ii][0] != ii - 1) {
      cp->input[ii][0]++;
      cp->input[ii][1] = 0;
      goto zero_lower_order_gates;
    }
  }
  return 0;
}

/* it isn't *really* necessary to put this in a struct.  Maybe I've
   been programming Python too much... */
typedef struct {
  int counter;
  char *pattern;
  jmp_buf mismatch;
} search_result;

search_result the_search_result;

void match(void *client_data, circuit *cp, int *inputs, int output) {
  search_result *sr = client_data;
  char pattern_element = sr->pattern[sr->counter];
  sr->counter++;
  if (pattern_element == 'x') return;  /* don't care */
  if (output != pattern_element - '0') longjmp(sr->mismatch, -1);
}

int test_circuit(circuit *cp, char *pattern) {
  search_result *sr = &the_search_result;
  sr->counter = 0;
  sr->pattern = pattern;

  if (setjmp(sr->mismatch) == 0) {
    enumtable(cp, sr, &match);
  } else {
    return 0;  /* found a mismatch */
  }
  return 1;
}

int inputs_for_pattern(char *pattern) {
  char *s;
  int len;
  int log2 = 0;
  for (s = pattern; *s; s++)
    if ((*s != '0') && (*s != '1') && (*s != 'x')) return 0;
  len = s - pattern;
  while (len > 1) {
    if (len & 1) return 0;
    log2 ++;
    len >>= 1;
  }
  return log2;
}

int max_gates_for_inputs(int inputs) { 
  /* this function may not be useful to implement, as the program
     takes too long to run in practice for more than a few gates. */
  return 100; 
}

int main(int argc, char **argv) {
  circuit *cp = 0;
  int ninputs, ngates, maxgates, done;
  if (argc != 2) {
    fprintf(stderr, 
            "%s: Usage: %s pattern\n"
            "pattern is a pattern of 0's, 1's, and x's.\n", 
            argv[0], argv[0]);
    return 1;
  }
  if (!(ninputs = inputs_for_pattern(argv[1]))) {
    fprintf(stderr,
            "%s: Don't understand pattern '%s' of length %d.\n"
            "Pattern lengths should be powers of 2; they "
            "consist of 0's, 1's, and x's.\n",
            argv[0], argv[1], strlen(argv[1]));
    return 2;
  }
  {
    int ii;
    printf("Inputs: ");
    for (ii = 0; ii < ninputs; ii++)
      printf("%c ", varname(ii));
    printf("\n");
  }
  maxgates = max_gates_for_inputs(ninputs);
  done = 0;
  for (ngates = 0; ngates <= maxgates; ngates++) {
    printf("Trying with %d gates...\n", ngates);
    free(cp);
    cp = newcircuit(ninputs, ngates);
    if (!cp) {  /* out of memory, probably; how likely is that?! */
      fprintf(stderr, "For %d inputs and %d gates", ninputs, ngates);
      perror("newcircuit");
      return 3;
    }
    reset_circuit(cp);
    do {
      if (test_circuit(cp, argv[1])) {
        nicely_print_circuit(cp);
        enumtable(cp, 0, &printentry);
        done = 1;
      }
    } while (increment_circuit(cp));
    if (done) return 0;  /* no need to try more complex circuits... */
  }
  assert(0);  /* should never happen */
}


-- 
<[EMAIL PROTECTED]>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Edsger Wybe Dijkstra died in August of 2002.  The world has lost a great
man.  See http://advogato.org/person/raph/diary.html?start=252 and
http://www.kode-fu.com/geek/2002_08_04_archive.shtml for details.

Reply via email to