\myheading{Referential transparency} \leveldown{} This is important -- a variable can be assigned only once. Like in Verilog/VHDL, Haskell. Like in \ac{SSA}: \begin{framed} \begin{quotation} One important property that holds for all varieties of SSA, including the simplest definition above, is referential transparency: i.e., since there is only a single definition for each variable in the program text, a variable’s value is independent of its position in the program. \end{quotation} \end{framed} ( \href{http://ssabook.gforge.inria.fr/latest/book.pdf}{Static Single Assignment Book} ) This is like immutability. Hence, the order of variable assignments doesn't matter at all. You see, how I do this in my MK85 toy solver: \ref{MK85_adder} (\verb|add_FA()| function). Hence, an expression like $x=x+1$ wouldn't work. How can you synthesize it? \iffalse \begin{lstlisting} +-------+ +-------+ |const 1| ---> | | +-------+ | adder | ---+---> x x ---+--> | | | | +-------+ | +-----------------+ \end{lstlisting} \fi \begin{figure}[H] \centering \includesvg[width=0.3\textwidth]{\CURPATH/5.svg} \end{figure} You see, such a digital circuit would be senseless. However, you still can feed this expression to my MK85 toy solver (which is dumb enough to process such an expression) and a circuit like this would be synthesized, but the result would be UNSAT. \myheading{Optimization} All this can help in optimization. For example, in my \verb|SAT_lib.py| Python library I'm using across this book in SAT examples, each gate generated only once for the specific inputs: \begin{lstlisting}[style=custompy] def AND(self, v1:int, v2:int) -> int: if (v1, v2) in self.AND_already_generated: return self.AND_already_generated[(v1,v2)] out=self.create_var() self.AND_Tseitin(v1, v2, out) self.AND_already_generated[(v1,v2)]=out return out \end{lstlisting} You can use \textit{cache} or \textit{memoization} words here. In my toy MK85 SMT solver I'm going further: I cache expressions as strings. Maybe this is a crude hack, but it works: \begin{lstlisting}[style=customc] struct ctx { ... std::unordered_map gen_cache; }; ... struct SMT_var* gen(struct ctx* ctx, struct expr* e) { ... std::string tmp=cpp_expr_to_string(e); auto tmp2=ctx->gen_cache.find(tmp); if (tmp2!=ctx->gen_cache.end()) { if (verbose) printf ("found this expression in gen_cache\n"); return tmp2->second; } else { if (verbose) printf ("not found this expression in gen_cache\n"); }; ... case EXPR_BINARY: if (verbose) printf ("%s:%d (after EXPR_BINARY) end\n", __FUNCTION__, __LINE__); rt=gen_binary(ctx, e); rt->e=e; ctx->gen_cache[tmp]=rt; return rt; ... \end{lstlisting} This is like \textit{common subexpression elimination}. It would be way harder doing it in Algol-like programming languages. I added this optimization to MK85 when it failed to generate the \textit{bit reverse} function (\ref{bitrev}) -- because the expression \textit{explode} on each iteration drastically. \levelup{}