\myheading{Recalculating micro-spreadsheet using Z3Py} \label{spreadsheet} \leveldown{} \renewcommand{\CURPATH}{equations/spreadsheet} There is a nice exercise\footnote{The blog post in Russian: \url{http://thesz.livejournal.com/280784.html}}: write a program to recalculate micro-spreadsheet, like this one: \lstinputlisting{\CURPATH/test1} The result must be: \lstinputlisting{\CURPATH/test1_result} As it turns out, though overkill, this can be solved using MK85 with little effort: \lstinputlisting[style=custompy]{\CURPATH/spreadsheet_MK85.py} ( \url{\RepoURL/\CURPATH/spreadsheet_MK85.py} ) All we do is just creating pack of variables for each cell, named A0, B1, etc, of integer type. All of them are stored in \emph{cells[]} dictionary. Key is a string. Then we parse all the strings from cells, and add to list of constraints \emph{A0=123} (in case of number in cell) or \emph{A0=B1+C2} (in case of expression in cell). There is a slight preparation: string like \emph{A0+B2} becomes \emph{cells["A0"]+cells["B2"]}. Then the string is evaluated using Python \emph{eval()} method, which is highly dangerous \footnote{\url{http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice}}: imagine if end-user could add a string to cell other than expression? Nevertheless, it serves our purposes well, because this is a simplest way to pass a string with expression into MK85. \myheading{Z3} The source code almost the same: \lstinputlisting[style=custompy]{\CURPATH/spreadsheet_Z3_1.py} ( \url{\RepoURL/\CURPATH/spreadsheet_Z3_1.py} ) \myheading{Unsat core} \label{spreadsheet_unsat} Now the problem: what if there is circular dependency? Like: \lstinputlisting{\CURPATH/test_circular} Two first cells of the last row (C0 and C1) are linked to each other. Our program will just tells ``unsat'', meaning, it couldn't satisfy all constraints together. We can't use this as error message reported to end-user, because it's highly unfriendly. Our question now is: which variables "spoiling" the whole spreadsheet and "to be dropped" to get everything solvable? We can fetch \emph{unsat core}, i.e., list of variables which Z3 finds conflicting. \begin{lstlisting} ... s=Solver() s.set(unsat_core=True) ... # add constraint: s.assert_and_track(e, coord_to_name(cur_R, cur_C)) ... if result=="sat": ... else: print s.unsat_core() \end{lstlisting} ( \url{\RepoURL/\CURPATH/spreadsheet_Z3_2.py} ) We must explicitly turn on unsat core support and use \emph{assert\_and\_track()} instead of \emph{add()} method, because this feature slows down the whole process, and is turned off by default. That works: \begin{lstlisting} % python 2.py test_circular unsat [C0, C1] \end{lstlisting} Perhaps, these variables could be removed from the 2D array, marked as \emph{unresolved} and the whole spreadsheet could be recalculated again. ... but. Needless to say, that this is a circular dependency, but not a problem (\texttt{C1+123} and \texttt{C0-123}): \lstinputlisting{\CURPATH/test_circular2} My solvers can find at least two (correct) solutions: \lstinputlisting{\CURPATH/test_circular2.res1} \lstinputlisting{\CURPATH/test_circular2.res2} Funny, but Microsoft Excel can't handle such circular (but correct) dependencies: \begin{figure}[H] \centering \includegraphics[width=0.7\textwidth]{\CURPATH/excel.png} \caption{Microsoft Excel} \end{figure} What about LibreOffice Calc? \begin{figure}[H] \centering \includegraphics[width=0.7\textwidth]{\CURPATH/libreoffice.png} \caption{LibreOffice Calc} \end{figure} Error 522 is\footnote{\url{https://help.libreoffice.org/6.4/en-GB/text/scalc/05/02140000.html}}: ``Circular reference'', ``Formula refers directly or indirectly to itself and the Iterations option is not set under Tools - Options - LibreOffice Calc - Calculate.''. OK, I set this option on (to 0) and got Error 523: ``The calculation procedure does not converge'', ``Function missed a targeted value, or iterative references do not reach the minimum change within the maximum number of steps set.'' Setting \emph{Iterations} option to 1 seems to solve that problem: \begin{figure}[H] \centering \includegraphics[width=0.7\textwidth]{\CURPATH/libreoffice_2.png} \caption{LibreOffice Calc} \end{figure} \myheading{Stress test} How to generate a large random spreadsheet? What we can do. First, create random \ac{DAG}, like this one: \begin{figure}[H] \centering \includegraphics[width=\textwidth]{\CURPATH/1.png} \caption{Random \ac{DAG}} \end{figure} Arrows will represent information flow. So a vertex (node) which has no incoming arrows to it (indegree=0), can be set to a random number. Then we use topological sort to find dependencies between vertices. Then we assign spreadsheet cell names to each vertex. Then we generate random expression with random operations/numbers/cells to each cell, with the use of information from topological sorted graph. \input{\CURPATH/math} Here is an output from \emph{Grid[]}: \input{\CURPATH/grid.tex} Using this script, I can generate random spreadsheet of $26 \cdot 500=13000$ cells, which seems to be processed by Z3 in couple of seconds. \myheading{The files} The files, including Mathematica notebook: \url{\RepoURL/\CURPATH}. \levelup{}