Continuing with the implementation of the already discussed function in Week 0. The implementation was done in the PR#29806
The implementation of the backtracking function is pretty straight-forward and intutive. The code for it being
def backtrack(self): if not self.bound_history: raise ValueError("Cannot backtrack, bound_history stack is empty") xi, old_bound, upper = self.bound_history.pop() if upper: xi.upper = old_bound else: xi.lower = old_bound for var in self.all_var: var.assign = self.last_assign_snapshot[var] self.is_sat = True self.result = None Apart from the function declaration and defination, I had to add two state variables to the LRASolver class, bound_history which is a stack and keeps a track of previous bounds and last_assign_snapshot which is the assignment of the variables in the last valid state.
A major part of the PR was adding tests to see if the backtracking logic is working correctly. I particularly added 5 tests.
This particular test is the exact same example as discussed in the 4.6 section of the paper. I have specifically added variable bounds and assignments checks for each state.
Constraints:
In this particular test, Assert catches the error with the bounds on the variable and there is no need to explicitly call the expensive Check to see if the state is valid or not.
Constraints:
This particular test is somewhat similar to the test from the paper just with a set of different constraints.
Constraints:
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 test_backtracking_single_variable(), Assert catches the error with the bounds of the variable and Check is not necessary to be called.
Constraints:
This test is similar to the previous test but with multiple variables. Here, Check should be called to pivot the variables.
Constraints:
EncodedCNF 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.
A fix for this was use the function add_prop so it sequentially adds the constraints thus preserving the order. Another way to reduce this nondeterminism as suggested by Tilo was to testing_mode=True in the LRASolver. By doing these, the tests are consistent for now.
The PR#29806 has been merged! The next work should be to integrate this backtracking logic with dpll2.py
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
Sujal Kumar
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.
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).
Mostly it involved me fixing bugs with recursive handlers in ask. 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. The conversation. The discussion gave me a much clearer idea on what to work on over the summer and thus framing my proposal. Link to my proposal.
Results were announced on 30th April, I got in with 4 of my co-contributors and their projects under SymPy 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)
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 A Fast Linear-Arithmetic Solver for DPLL(T), this is the original paper on which the lra_solver has been developed. The backtracking function has been explained in the section 4.4 of the paper.
Let’s try to understand the research paper:
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.
The solver tracks three main things at all times:
When a constraint is fed into the solver, the Assert operation comes in. It doesn’t look in the matrix and doesn’t care about how variables are related.
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.
For example - if the solver already knows $x \ge 5$, and we assert $x \le 2$, Assert flags the inconsistency instantly.
Assert only looks at variable in isolation, it might end up passing systemic conflicts.
Check looks at the matrix to see if any basic variables violate their bounds. If a basic variable is out of bounds, Check attempts to fix it by pivoting meaning to swapping a basic variable with a non-basic variable to push the values back into a valid range.
If Check 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.
This is what I had to majorly focus on and implement. Let’s try to understand how it works.
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.
Section 4.4 of the paper outlines an elegant solution: tracking the state history so that backtracking takes $O(1)$ time.
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.
Section 4.6 (Figure 5) of the paper provides a specific sequence using two slack variables ($s_1$ and $s_2$):
The first three rules are asserted correctly. When rule 4 is fed into the system, Assert passes it completely fine because $s_2$ has no previous restrictive bounds.
But when Check 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.
We backtrack after this failure and it proves the solver can safely unroll the matrix pivots in $O(1)$ complexity.
The implementation will be done in the next week, following the next meet. Something to look forward to! :D
Sujal Kumar
Over the last few years I’ve often tried to make a contribution to LLVM-project but failed to land any contribution in, mostly because I engaged myself in some contract work last year, and because of which I couldn’t devote myself to focusing on it.
I feel I’ve come full-circle back LLVM contribution. I think I should be paving the way forward to keep myself away from all the distractions (AI taking over software development, whether what I’m taking up will lead to paid work or not etc.) and try as much as I can on two simple goals (atleast for the next 2 months or so):
Learn C++
Overall the last 1 year, I think I’ve degraded as a software engineer, and something I’m sure I work on it. I intend to focus on learning a of C++ concepts from C++ reference and learn cpp, there are many concepts which I have never been able to think clearly about, the idea is to majorly use the above C++ reference to learn C++, and taking help from an AI-agent as less as possible. I’m one of those software engineer who thinks that using AI substantially isn’t good to move forward as a software engineer, and that’s been my stance from the last 4 years (I’m not sure if it’s just pure denial of it’s capabilities though).
Contribution to LLVM project
I think one of the biggest hurdle one faces when starting to work on LLVM project is figuring out how to build LLVM, and since my laptop isn’t very powerful (Macbook Air M4 - 256 GB), I need to be careful with using it’s resources, I’ve been maintaining this LLVM debug build listing the way I built LLVM for difference issues, and it help’s me make sure I document my way of building LLVM (with various configuration options), so I can re-use specific feature if needed.
Currently, I’m looking at an issue https://github.com/llvm/llvm-project/issues/121952 from the LLVM project, which I’m able to reproduce locally and I’m trying to follow the comment left by one of the maintainer of the project. I’ve already understood many things that I wasn’t aware of before approaching this issue (e.g. Fuzzer), and many C++ features that I haven’t used in a while and re-visiting those helps.
I’ll try my best to keep this blog of mine continuously updated, and which will probably help me in writing better blog posts as well. See ya mate.
Hello everyone, the GSoC is getting close to the end.
Hello everyone, a month has passed since the last update.
Hello everyone, PR #28115(Open) is closed to being merged.
|
|
|
Hello everyone, I’m in my second week of GSoC and I’ve almost finished writing the transfer function classes #28115(Open).
Hello everyone, the Community Bonding period has concluded, and I want to share my progress with SymPy.
Hello everyone.
LLVM Intermediate Representation is an a low-level programming language (more lower than C++, Python etc.).