{"id":1143,"date":"2025-01-13T07:02:38","date_gmt":"2025-01-13T07:02:38","guid":{"rendered":"https:\/\/mailitics.com\/index.php\/2025\/01\/13\/using-constraint-programming-to-solve-math-theorems-1781611878d0\/"},"modified":"2025-01-13T07:02:38","modified_gmt":"2025-01-13T07:02:38","slug":"using-constraint-programming-to-solve-math-theorems-1781611878d0","status":"publish","type":"post","link":"https:\/\/mailitics.com\/index.php\/2025\/01\/13\/using-constraint-programming-to-solve-math-theorems-1781611878d0\/","title":{"rendered":"Using Constraint Programming to Solve Math Theorems"},"content":{"rendered":"<p>    Using Constraint Programming to Solve Math Theorems<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<h4>Case study: the quasigroups existence problem<\/h4>\n<h3>TLDR<\/h3>\n<p>Some mathematical theorems can be solved by combinatorial exploration. In this article, we focus on the problem of the existence of some quasigroups. We will demonstrate the existence or non existence of some quasigroups using <a href=\"https:\/\/github.com\/yangeorget\/nucs\">NuCS<\/a>. NuCs is a fast constraint solver written 100% in Python that I am currently developing as a side project. It is released under the <a href=\"https:\/\/github.com\/yangeorget\/nucs\/blob\/main\/LICENSE.md\">MIT\u00a0license<\/a>.<\/p>\n<h3>Some definitions<\/h3>\n<p>Let&#8217;s start by defining some useful vocabulary.<\/p>\n<h4>Groups<\/h4>\n<p>Quoting wikipedia:<\/p>\n<blockquote><p>In mathematics, a <strong>group<\/strong> is a set with an operation that associates an element of the set to every pair of elements of the set (as does every binary operation) and satisfies the following constraints: the operation is associative, it has an identity element, and every element of the set has an inverse\u00a0element.<\/p><\/blockquote>\n<p>The set of integers (positive and negative) together with the addition form a group. There are many of kind of groups, for example the manipulations of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rubik%27s_Cube\">Rubik&#8217;s\u00a0Cube<\/a>.<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/440\/1%2AA6HWIqlPHR0Ek6rz1H7mFg.png?ssl=1\"><figcaption><a href=\"https:\/\/en.wikipedia.org\/wiki\/Group_(mathematics)#\/media\/File:Rubik's_cube.svg\">Source: Wikipedia<\/a><\/figcaption><\/figure>\n<h4>Latin squares<\/h4>\n<blockquote><p>A Latin square is an <em>n<\/em> \u00d7 <em>n<\/em> array filled with <em>n<\/em> different symbols, each occurring exactly once in each row and exactly once in each\u00a0column.<\/p><\/blockquote>\n<p>An example of a 3\u00d73 Latin square\u00a0is:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/172\/1%2AwBputE2ca-WpAlVAO0_pzA.png?ssl=1\"><figcaption>Designed by the\u00a0author<\/figcaption><\/figure>\n<blockquote><p>For example, a Sudoku is a <em>9&#215;9<\/em> Latin square with additional properties.<\/p><\/blockquote>\n<h4>Quasigroups<\/h4>\n<blockquote><p>An order <em>m<\/em> quasigroup is a Latin square of size <em>m<\/em>. That is, a <em>m\u00d7m<\/em> multiplication table (we will note \u2217 the multiplication symbol) in which each element occurs once in every row and\u00a0column.<\/p><\/blockquote>\n<p>The multiplication law does not have to be associative. If it is, the quasigroup is a\u00a0group.<\/p>\n<p>In the rest of this article, we will focus on the problem of the existence of some particular quasigroups. The quasigroups we are interested in are idempotent, that is <strong><em>a<\/em>\u2217<em>a=a<\/em><\/strong> for every element\u00a0<em>a<\/em>.<\/p>\n<p>Moreover, they have additional properties:<\/p>\n<ul>\n<li>QG3.m problems are order m quasigroups for which <strong>(a\u2217b)\u2217(b\u2217a)=a<\/strong>.<\/li>\n<li>QG4.m problems are order m quasigroups for which <strong>(b\u2217a)\u2217(a\u2217b)=a<\/strong>.<\/li>\n<li>QG5.m problems are order m quasigroups for which <strong>((b\u2217a)\u2217b)\u2217b=a<\/strong>.<\/li>\n<li>QG6.m problems are order m quasigroups for which <strong>(a\u2217b)\u2217b=a\u2217(a\u2217b)<\/strong>.<\/li>\n<li>QG7.m problems are order m quasigroups for which <strong>(b\u2217a)\u2217b=a\u2217(b\u2217a)<\/strong>.<\/li>\n<\/ul>\n<p>In the following, for a quasigroup of order <em>m<\/em>, we note <em>0,\u00a0\u2026, m-1<\/em> the values of the quasigroup (we want the values to match with the indices in the multiplication table).<\/p>\n<h3>Modelling the quasigroup problem<\/h3>\n<h4>Latin square\u00a0models<\/h4>\n<p>We will model the quasigroup problem by leveraging the latin square problem. The former comes in 2\u00a0flavors:<\/p>\n<ul>\n<li>the <strong>LatinSquareProblem<\/strong>,<\/li>\n<li>the <strong>LatinSquareRCProblem<\/strong>.<\/li>\n<\/ul>\n<p>The LatinSquareProblem simply states that the values in all the rows and columns of the multiplication table have to be different:<\/p>\n<pre>self.add_propagators([(self.row(i), ALG_ALLDIFFERENT, []) for i in range(self.n)])<br>self.add_propagators([(self.column(j), ALG_ALLDIFFERENT, []) for j in range(self.n)])<\/pre>\n<p>This model defines, for each row <em>i<\/em> and column <em>j<\/em>, the value <em>color(i, j) <\/em>of the cell. We will call it the <strong>color<\/strong> model. Symmetrically, we can\u00a0define:<\/p>\n<ul>\n<li>for each row <em>i<\/em> and color <em>c<\/em>, the column <em>column(i, c)<\/em>: we call this the <strong>column<\/strong>\u00a0model,<\/li>\n<li>for each color <em>c <\/em>and<em> <\/em>column <em>j<\/em>, the row <em>row(c, j)<\/em>: we call this the <strong>row<\/strong>\u00a0model.<\/li>\n<\/ul>\n<p>Note that we have the following properties:<\/p>\n<ul>\n<li><em>row(c, j) = i &lt;=&gt; color(i, j) =\u00a0c<\/em><\/li>\n<\/ul>\n<p>For a given<em> <\/em>column<em> j<\/em>,<em> row(., j) <\/em>and<em> color(., j) <\/em>are inverse permutations.<\/p>\n<ul>\n<li><em>row(c, j) = i &lt;=&gt; column(i, c) =\u00a0j<\/em><\/li>\n<\/ul>\n<p>For a given<em> <\/em>color<em> c<\/em>,<em> row(c,\u00a0.) <\/em>and<em> column(., c) <\/em>are inverse permutations.<\/p>\n<ul>\n<li><em>color(i, j) = c &lt;=&gt; column(i, c) =\u00a0j<\/em><\/li>\n<\/ul>\n<p>For a given<em> <\/em>row<em> i<\/em>,<em> color(i,\u00a0.) <\/em>and<em> column(i,\u00a0.) <\/em>are inverse permutations.<\/p>\n<p>This is exactly what is implemented by the LatinSquareRCProblem with the help of the ALG_PERMUTATION_AUX propagator (note that a less optimized version of this propagator was also used in my <a href=\"https:\/\/medium.com\/towards-data-science\/how-to-tackle-an-optimization-problem-with-constraint-programming-9ae77b4d803d\">previous article<\/a> about the Travelling Salesman Problem):<\/p>\n<pre>def __init__(self, n: int):<br>    super().__init__(list(range(n)))  # the color model<br>    self.add_variables([(0, n - 1)] * n**2)  # the row model<br>    self.add_variables([(0, n - 1)] * n**2)  # the column model<br>    self.add_propagators([(self.row(i, M_ROW), ALG_ALLDIFFERENT, []) for i in range(self.n)])<br>    self.add_propagators([(self.column(j, M_ROW), ALG_ALLDIFFERENT, []) for j in range(self.n)])<br>    self.add_propagators([(self.row(i, M_COLUMN), ALG_ALLDIFFERENT, []) for i in range(self.n)])<br>    self.add_propagators([(self.column(j, M_COLUMN), ALG_ALLDIFFERENT, []) for j in range(self.n)])<br>    # row[c,j]=i &lt;=&gt; color[i,j]=c<br>    for j in range(n):<br>        self.add_propagator(([*self.column(j, M_COLOR), *self.column(j, M_ROW)], ALG_PERMUTATION_AUX, []))<br>    # row[c,j]=i &lt;=&gt; column[i,c]=j<br>    for c in range(n):<br>        self.add_propagator(([*self.row(c, M_ROW), *self.column(c, M_COLUMN)], ALG_PERMUTATION_AUX, []))<br>    # color[i,j]=c &lt;=&gt; column[i,c]=j<br>    for i in range(n):<br>        self.add_propagator(([*self.row(i, M_COLOR), *self.row(i, M_COLUMN)], ALG_PERMUTATION_AUX, []))<\/pre>\n<h4>Quasigroup model<\/h4>\n<p>Now we need to implement our additional properties for our quasigroups.<\/p>\n<p>Idempotence is simply implemented by:<\/p>\n<pre>for model in [M_COLOR, M_ROW, M_COLUMN]:<br>    for i in range(n):<br>        self.shr_domains_lst[self.cell(i, i, model)] = [i, i]<\/pre>\n<p>Let&#8217;s now focus on QG5.m. We need to implement <em>((b\u2217a)\u2217b)\u2217b=a<\/em>:<\/p>\n<ul>\n<li>this translates into: <em>color(color(color(j, i), j), j) =\u00a0i,<\/em>\n<\/li>\n<li>or equivalently: <em>row(i, j) = color(color(j, i),\u00a0j).<\/em>\n<\/li>\n<\/ul>\n<p>The last expression states that the <em>color(j,i)th<\/em> element of the <em>jth<\/em> column is <em>row(i, j)<\/em>. To enforces this, we can leverage the ALG_ELEMENT_LIV propagator (or a more specialized ALG_ELEMENT_LIV_ALLDIFFERENT optimized to take into account the fact that the rows and columns contain elements that are alldifferent).<\/p>\n<pre>for i in range(n):<br>    for j in range(n):<br>        if j != i:<br>            self.add_propagator(<br>                (<br>                    [*self.column(j), self.cell(j, i), self.cell(i, j, M_ROW)],<br>                    ALG_ELEMENT_LIV_ALLDIFFERENT,<br>                    [],<br>                )<br>            )<\/pre>\n<p>Similarly, we can model the problems <a href=\"https:\/\/github.com\/yangeorget\/nucs\/blob\/main\/nucs\/examples\/quasigroup\/quasigroup_problem.py\">QG3.m, QG4.m, QG6.m,\u00a0QG7.m<\/a>.<\/p>\n<h3>Experiments<\/h3>\n<p>Note that this problem is very hard since the size of the search space is m\u1d50\u1d50. For <em>m=10<\/em>, this is\u00a0<em>1e+100<\/em>.<\/p>\n<p>The following experiments are performed on a MacBook Pro M2 running Python 3.13, Numpy 2.1.3, Numba 0.61.0rc2 and NuCS 4.6.0. Note that the recent versions of NuCS are relatively faster than older ones since Python, Numpy and Numba have been upgraded.<\/p>\n<p>The following proofs of existence\/non existence are obtained in <strong>less than a\u00a0second<\/strong>:<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1%2AznmS07WcsvFkwbWZ15bydw.png?ssl=1\"><figcaption>Experiments with small instances<\/figcaption><\/figure>\n<p>Let&#8217;s now focus on <strong>QG5.m<\/strong> only where the first open problem is\u00a0<strong>QG5.18<\/strong>.<\/p>\n<figure><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1024\/1%2AJiFv9aJUFFxzvLXGui8Zgg.png?ssl=1\"><figcaption>Experiments with QG5 (in the second line, we use a MultiprocessingSolver)<\/figcaption><\/figure>\n<p>Going further would require to rent a powerful machine on a cloud provider during a few days at\u00a0least!<\/p>\n<h3>Conclusion<\/h3>\n<p>As we have seen, some mathematical theorems can be solved by combinatorial exploration. In this article, we studied the problem of the existence\/non existence of quasigroups. Among such problems, some open ones seem to be accessible, which is very stimulating.<\/p>\n<p>Some ideas to improve on our current approach to quasigroups existence:<\/p>\n<ul>\n<li>refine the model which is still fairly\u00a0simple<\/li>\n<li>explore more sophisticated heuristics<\/li>\n<li>run the code on the cloud (using docker, for\u00a0example)<\/li>\n<\/ul>\n<p>Some useful links to go further with\u00a0NuCS:<\/p>\n<ul>\n<li>the source code: <a href=\"https:\/\/github.com\/yangeorget\/nucs\">https:\/\/github.com\/yangeorget\/nucs<\/a>\n<\/li>\n<li>the documentation: <a href=\"https:\/\/nucs.readthedocs.io\/en\/latest\/index.html\">https:\/\/nucs.readthedocs.io\/en\/latest\/index.html<\/a>\n<\/li>\n<li>the Pip package: <a href=\"https:\/\/pypi.org\/project\/NUCS\/\">https:\/\/pypi.org\/project\/NUCS\/<\/a>\n<\/li>\n<\/ul>\n<p>If you enjoyed this article about NuCS, please clap <strong>50<\/strong> times\u00a0!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/medium.com\/_\/stat?event=post.clientViewed&amp;referrerSource=full_rss&amp;postId=1781611878d0\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<hr>\n<p><a href=\"https:\/\/towardsdatascience.com\/using-constraint-programming-to-solve-math-theorems-1781611878d0\">Using Constraint Programming to Solve Math Theorems<\/a> was originally published in <a href=\"https:\/\/towardsdatascience.com\/\">Towards Data Science<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>\n<\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><br \/>\n    Yan Georget<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/medium.com\/m\/global-identity-2?redirectUrl=https%3A%2F%2Ftowardsdatascience.com%2Fusing-constraint-programming-to-solve-math-theorems-1781611878d0\">Go to original source<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using Constraint Programming to Solve Math Theorems Case study: the quasigroups existence problem TLDR Some mathematical theorems can be solved by combinatorial exploration. In this article, we focus on the problem of the existence of some quasigroups. We will demonstrate the existence or non existence of some quasigroups using NuCS. NuCs is a fast constraint [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[62,1266,599,913,157,621],"tags":[1268,1267,292],"class_list":["post-1143","post","type-post","status-publish","format-standard","hentry","category-aimldsaimlds","category-constraints","category-mathematics","category-numpy","category-python","category-python-programming","tag-element","tag-quasigroups","tag-some"],"_links":{"self":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1143"}],"collection":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/comments?post=1143"}],"version-history":[{"count":0,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/posts\/1143\/revisions"}],"wp:attachment":[{"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/media?parent=1143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/categories?post=1143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mailitics.com\/index.php\/wp-json\/wp\/v2\/tags?post=1143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}