<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>Sujal - SymPy Posts</title> <description>SymPy related posts from blank</description> <link>https://sujalkumar06.github.io/</link> <atom:link href="https://sujalkumar06.github.io/sympy-feed.xml" rel="self" type="application/rss+xml"/> <item> <title>Week 1: Implementation of Backtracking logic</title> <description>&lt;p&gt;Continuing with the implementation of the already discussed function in Week 0. The implementation was done in the &lt;a href=&quot;https://github.com/sympy/sympy/pull/29806&quot;&gt;PR#29806&lt;/a&gt;&lt;/p&gt; &lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt; &lt;p&gt;The implementation of the backtracking function is pretty straight-forward and intutive. The code for it being&lt;/p&gt; &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;backtrack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bound_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Cannot backtrack, bound_history stack is empty&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old_bound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;upper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bound_history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upper&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old_bound&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lower&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old_bound&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assign&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_assign_snapshot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_sat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Apart from the function declaration and defination, I had to add two state variables to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LRASolver&lt;/code&gt; class, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bound_history&lt;/code&gt; which is a stack and keeps a track of previous bounds and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;last_assign_snapshot&lt;/code&gt; which is the assignment of the variables in the last valid state.&lt;/p&gt; &lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt; &lt;p&gt;A major part of the PR was adding tests to see if the backtracking logic is working correctly. I particularly added 5 tests.&lt;/p&gt; &lt;h3 id=&quot;test_example_from_paper&quot;&gt;test_example_from_paper()&lt;/h3&gt; &lt;p&gt;This particular test is the exact same example as discussed in the 4.6 section of the &lt;a href=&quot;https://link.springer.com/chapter/10.1007/11817963_11&quot;&gt;paper&lt;/a&gt;. I have specifically added variable bounds and assignments checks for each state.&lt;/p&gt; &lt;p&gt;Constraints:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$x \le -4$&lt;/li&gt; &lt;li&gt;$x \ge -8$&lt;/li&gt; &lt;li&gt;$-x + y \le 1$&lt;/li&gt; &lt;li&gt;$x + y \ge -3$&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;test_backtracking_single_variable&quot;&gt;test_backtracking_single_variable()&lt;/h3&gt; &lt;p&gt;In this particular test, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; catches the error with the bounds on the variable and there is no need to explicitly call the expensive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; to see if the state is valid or not.&lt;/p&gt; &lt;p&gt;Constraints:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$x \le -4$&lt;/li&gt; &lt;li&gt;$x \ge -8$&lt;/li&gt; &lt;li&gt;$x \ge -2$&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;test_backtracking_multiple_variables&quot;&gt;test_backtracking_multiple_variables()&lt;/h3&gt; &lt;p&gt;This particular test is somewhat similar to the test from the paper just with a set of different constraints.&lt;/p&gt; &lt;p&gt;Constraints:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$2x + 3y \le 12$&lt;/li&gt; &lt;li&gt;$x \ge 3$&lt;/li&gt; &lt;li&gt;$y \ge 3$&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;test_backtracking_single_variable_multiple_backtracks&quot;&gt;test_backtracking_single_variable_multiple_backtracks()&lt;/h3&gt; &lt;p&gt;This particular test is made to check if the solver can come back to a particular state after multiple backtracks successfully. In this particular test, similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_backtracking_single_variable()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; catches the error with the bounds of the variable and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; is not necessary to be called.&lt;/p&gt; &lt;p&gt;Constraints:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$x \le 10$&lt;/li&gt; &lt;li&gt;$x \ge 0$&lt;/li&gt; &lt;li&gt;$x \ge 5$&lt;/li&gt; &lt;li&gt;$x \le 2$&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;test_backtracking_multiple_variables_multiple_backtracks&quot;&gt;test_backtracking_multiple_variables_multiple_backtracks()&lt;/h3&gt; &lt;p&gt;This test is similar to the previous test but with multiple variables. Here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; should be called to &lt;strong&gt;pivot&lt;/strong&gt; the variables.&lt;/p&gt; &lt;p&gt;Constraints:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$x \le 10$&lt;/li&gt; &lt;li&gt;$x \ge 0$&lt;/li&gt; &lt;li&gt;$y \ge 0$&lt;/li&gt; &lt;li&gt;$x \ge 5$&lt;/li&gt; &lt;li&gt;$y \ge 5$&lt;/li&gt; &lt;li&gt;$x + y \le 4$&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;issues-faced&quot;&gt;Issues faced&lt;/h3&gt; &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EncodedCNF&lt;/code&gt; sorts the hashes created for the constraints, which is an issue, as everytime the tests are ran, a new environment is created, so in some instances the tests used to fail while in some it used to pass as I am manually tracing the logic for backtracking.&lt;/p&gt; &lt;p&gt;A fix for this was use the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_prop&lt;/code&gt; so it sequentially adds the constraints thus preserving the order. Another way to reduce this nondeterminism as suggested by Tilo was to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing_mode=True&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LRASolver&lt;/code&gt;. By doing these, the tests are consistent for now.&lt;/p&gt; &lt;h2 id=&quot;final-remarks&quot;&gt;Final Remarks&lt;/h2&gt; &lt;p&gt;The &lt;a href=&quot;https://github.com/sympy/sympy/pull/29806&quot;&gt;PR#29806&lt;/a&gt; has been merged! The next work should be to integrate this backtracking logic with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dpll2.py&lt;/code&gt;&lt;/p&gt; &lt;p&gt;Another major task in hand is to start with the Phase 1 of the proposal that will be discussed in the next meet. Looking foward to it. :D&lt;/p&gt; &lt;p&gt;Sujal Kumar&lt;/p&gt; </description> <pubDate>Sun, 31 May 2026 07:00:00 +0000</pubDate> <link>https://sujalkumar06.github.io/blog/2026/week-1/</link> <guid isPermaLink="true">https://sujalkumar06.github.io/blog/2026/week-1/</guid> </item> <item> <title>Week 0: The Start of GSoC</title> <description>&lt;p&gt;Starting with something new, a little excited as well as nervous with stuff. The incoming blogs will be me describing details of my project and stuff I did over the course of my period in GSoC’26 in SymPy.&lt;/p&gt; &lt;h2 id=&quot;pre-gsoc&quot;&gt;Pre-GSoC&lt;/h2&gt; &lt;p&gt;I have been contributing to SymPy for some time and was interested in contributing to the assumptions module. I had few merged PRs in the model (with a little different vision with respect to my project).&lt;/p&gt; &lt;p&gt;Mostly it involved me fixing bugs with recursive handlers in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ask&lt;/code&gt;. A lot of it was also adding appropriate tests to counter various cases and check if the algorithm is working correctly. Soon, I had a talk with one of the collaborators Tilo, discussing various aspects I can make assumptions system better. &lt;a href=&quot;https://groups.google.com/g/sympy/c/SU1PJ7xfzZc/m/8RyxzeuIBwAJ&quot;&gt;The conversation&lt;/a&gt;. The discussion gave me a much clearer idea on what to work on over the summer and thus framing my proposal. &lt;a href=&quot;https://github.com/SujalKumar06/GSoC-2026-Proposal-for-SymPy&quot;&gt;Link to my proposal&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Results were announced on 30th April, I got in with 4 of my co-contributors and their projects under &lt;a href=&quot;https://summerofcode.withgoogle.com/programs/2026/organizations/sympy&quot;&gt;SymPy&lt;/a&gt; for 2026. I had my final exams for 4th semester were going on, so I did not start immediately. My mentors are Tilo and Aaron. I had my first meet on 18th May, 9:30 PM (Yeah a time which was suitable for both my mentors and me :D)&lt;/p&gt; &lt;h2 id=&quot;the-research-paper&quot;&gt;The Research Paper&lt;/h2&gt; &lt;p&gt;The initial meet had us discussing what could be a good start for me to get going with the existing SAT Solvers in SymPy. One idea was to implement the backtracking function from the paper &lt;a href=&quot;https://link.springer.com/chapter/10.1007/11817963_11&quot;&gt;A Fast Linear-Arithmetic Solver for DPLL(T)&lt;/a&gt;, this is the original paper on which the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lra_solver&lt;/code&gt; has been developed. The backtracking function has been explained in the section 4.4 of the paper.&lt;/p&gt; &lt;p&gt;Let’s try to understand the research paper:&lt;/p&gt; &lt;p&gt;At a high level DPLL(T) separates the heavy lifting into two parts: a boolean SAT solver that handles the logical structure, and a Theory solver (Linear Real Arithmetic, or LRA) that checks if the current set of mathematical assertions is actually feasible.&lt;/p&gt; &lt;p&gt;The solver tracks three main things at all times:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;The Tableau:&lt;/strong&gt; A matrix that defines the relationships between variables. Variables are split into two camps: &lt;strong&gt;Basic&lt;/strong&gt; (dependent) and &lt;strong&gt;Non-basic&lt;/strong&gt; (independent).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;The Bounds:&lt;/strong&gt; Every variable has a lower and upper bound (initially $-\infty$ and $\infty$).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;The Assignment ($\beta$):&lt;/strong&gt; A mapping that gives every variable a specific numerical value to ensure the equations are always balanced.&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;assert&quot;&gt;Assert&lt;/h3&gt; &lt;p&gt;When a constraint is fed into the solver, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; operation comes in. It doesn’t look in the matrix and doesn’t care about how variables are related.&lt;/p&gt; &lt;p&gt;It does a superficial check looking at the bounds of the specific variable. It updates the bound in $O(1)$ time and immediately checks for trivial contradictions.&lt;/p&gt; &lt;p&gt;For example - if the solver already knows $x \ge 5$, and we assert $x \le 2$, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; flags the inconsistency instantly.&lt;/p&gt; &lt;h3 id=&quot;check&quot;&gt;Check&lt;/h3&gt; &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; only looks at variable in isolation, it might end up passing systemic conflicts.&lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; looks at the matrix to see if any basic variables violate their bounds. If a basic variable is out of bounds, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; attempts to fix it by &lt;strong&gt;pivoting&lt;/strong&gt; meaning to swapping a basic variable with a non-basic variable to push the values back into a valid range.&lt;/p&gt; &lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; needs to adjust a variable but every single non-basic variable in that row is already maxed out at its own restrictive bound, the solver is trapped. It has mathematically proven that no valid assignment exists, and it raises a systemic conflict.&lt;/p&gt; &lt;h3 id=&quot;backtrack&quot;&gt;Backtrack&lt;/h3&gt; &lt;p&gt;This is what I had to majorly focus on and implement. Let’s try to understand how it works.&lt;/p&gt; &lt;p&gt;When a conflict is raised, the solver has to back up. If it had to rebuild its entire matrix from scratch every time it hit a dead end, the solver would be impossibly slow.&lt;/p&gt; &lt;p&gt;Section 4.4 of the paper outlines an elegant solution: tracking the state history so that backtracking takes $O(1)$ time.&lt;/p&gt; &lt;p&gt;This is achieved using a state stack. Every time a bound is updated, the previous state is pushed onto the stack. When a conflict is detected, the solver simply pops the most recent update, restores the previous bound, and rolls back the variable assignments ($\beta$) to their last valid state snapshot.&lt;/p&gt; &lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt; &lt;p&gt;Section 4.6 (Figure 5) of the paper provides a specific sequence using two slack variables ($s_1$ and $s_2$):&lt;/p&gt; &lt;ol&gt; &lt;li&gt;$x \le -4$&lt;/li&gt; &lt;li&gt;$x \ge -8$&lt;/li&gt; &lt;li&gt;$-x + y \le 1$ &lt;em&gt;(which asserts $s_1 \le 1$)&lt;/em&gt;&lt;/li&gt; &lt;li&gt;$x + y \ge -3$ &lt;em&gt;(which asserts $s_2 \ge -3$)&lt;/em&gt;&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;The first three rules are asserted correctly. When rule 4 is fed into the system, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Assert&lt;/code&gt; passes it completely fine because $s_2$ has no previous restrictive bounds.&lt;/p&gt; &lt;p&gt;But when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check&lt;/code&gt; runs, it is forced to pivot the matrix. During the pivot, it realizes that both $x$ and $s_1$ are maxed out, causing an inconsistency.&lt;/p&gt; &lt;p&gt;We backtrack after this failure and it proves the solver can safely unroll the matrix pivots in $O(1)$ complexity.&lt;/p&gt; &lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt; &lt;p&gt;The implementation will be done in the next week, following the next meet. Something to look forward to! :D&lt;/p&gt; &lt;p&gt;Sujal Kumar&lt;/p&gt; </description> <pubDate>Sun, 24 May 2026 07:00:00 +0000</pubDate> <link>https://sujalkumar06.github.io/blog/2026/week-0/</link> <guid isPermaLink="true">https://sujalkumar06.github.io/blog/2026/week-0/</guid> </item> </channel> </rss>