I would like to know if there is a straightforward algorithm for rearranging simple symbolic algebraic expressions. Ideally I would like to be able to rewrite any such expression with one variable alone on the left hand side. For example, given the input:
m = (x + y) / 2
... I would like to be able to ask about x in terms of m and y, or y in terms of x and m, and get these:
x = 2*m - y
y = 2*m - x
Of course we've all done this algorithm on paper for years. But I was wondering if there was a name for it. It seems simple enough but if somebody has already cataloged the various "gotchas" it would make life easier.
For my purposes I won't need it to handle quadratics.
(And yes, CAS systems do this, and yes I know I could just use them as a library. I would like to avoid such a dependency in my application. I really would just like to know if there are named algorithms for approaching this problem.)
What you want is equation solving algorithm(s). But i bet that this is huge topic. In general case, there may be:
system of equations
equations may be non-linear, thus additional algorithms such as equation factorization needed.
knowledge how to reverse functions is needed,- for example =>
sin(x) + 10 = z, solving for x we reverse sin(), which is arcsin(). (Not all functions may be reversible !)
finally some equations may be hard-solvable even for CAS, such as
sin(x)+x=y, solve for x.
Hard answer is - your best bet is to take source code of some CAS,- for example you can take a look at MAXIMA CAS source code which is written in LISP. And find code which is responsible for equation solving.
Easy answer - if all that you need is solving equation which is linear and is composed only from basic operators +-*/. Then you know answer already - use old good paper method
- think of what rules we used on paper, and just re-write these rules as symbolic algorithm which manipulates equation string.
good luck !
It seems like what you're interested in doing is maintaining a system of linear equations and then, at any time, being able to solve for one variable in terms of all the others. If you encode the relationships as a matrix, it seems like you could then reduce the matrix to some nice form (for example, reduced row echelon form) to get the "simplest" dependencies amongst the variables (for some nice definition of "simplest.") Once you have the data like this, you should be able to read off all the dependencies by just looking at some row that has a nonzero entry for the variable in question, then normalizing it so that the variable has coefficient one.
A note - in general, you won't always get a unique solution for each variable. For example, given the trivial equations
x = y
x = z
Then solving for z could yield either "z = x" or "z = y," depending on how much simplification you want. Or alternatively, in a setup like
x = 2y + 3w
x = 9z
Returning a value for x could hand back either expression, or their sum over two, or a whole bunch of other things that are all technically true but not necessarily useful. I'm not sure how you'd handle this, but depending on the form of your equations you can probably find a way to handle it.
Convert your expression into a data structure (tree) in reverse Polish notation. Your tree is made up of nodes, each node has an operation, a left and a right. Each of the left and right can be a symbol (eg: "x") or another node. For example:
(x + (a + b))
Would become:
(+ x (+ a b))
Or in JSON:
["+", "x", ["+", "a", "b"]]
Your original expression m = (x + y) / 2 would look like this:
m = ["/", ["+", "x", "y"], "2"]
One of your desired expressions (solving for x) looks like this:
x = ["-", ["*", "m", "2"], "y"]
Can you see that the tree of expressions has been turned inside out and each operator has been reversed? The "-" is the reverse of the "+" and now wraps the "*" which is a reverse of the "/". The:
["+", "x", "y"]
Becomes:
["-", (something), "y"]
Where (something) is a reversal of the outer expression recursively. To try to explain the process: a) recursively descend the expression tree until you find the node containing the symbol you want to solve for, b) make a new node containing a reverse of this nodes operation, c) replace the symbol you want to solve for with the reversed outer expression, recursively doing this as you progress back outwards.
There are various simple ways in which the initial equation can be modified, performing the correct modifications in the correct order will lead to the correct solution. So how about seeing this as a search or even a pathfinding problem?
Related
Context
I started teaching myself lambda calculus last night and I am trying to determine if what I understand so far is correct.
Understanding
SKK is equivalent to the Identity combinator, I.
Where L stands for lambda:
S = LxLyLz((xz)(yz))
K = LxLy(x)
K essentially takes the next 2 (lambda) terms and gives back the first of those. S seems a little more complicated in the untyped lambda calculus.
My Interpretation
SK(any-lambda-term) is also equivalent to I.
I.e. the application of the application of S to K to Any-lambda-term is equivalent to the Identity combinator:
((S K)(Any)) = I = S K K = ((S K)(K))
I am using the convention of “left-association” in my above notation, if that helps (And I tried to make that clear in the 4th term above with parentheses. Everything I have read so far seems to use this convention).
Reasoning
S K = LyLz((K z)(y z))
The next lambda term will be substituted for y, let the term be Y.
S K Y = Lz((K z)(Y z))
(Y z) is the application of Y to z, also a lambda term.
(K z)returns the constant function that returns z, given another term input: (Y z).
Is my interpretation true? If not, can you provide an explanation? I would greatly appreciate it. Particularly if a sort of order of operations can be explained—I regularly find myself confused when considering when to evaluate. Perhaps that will be refined with practice.
Your intuition is correct, but an intuition proves nothing (alas...)
So, how can we prove your statement? Simply by showing that SKK and SKS have the same behaviour. "Behaviour" is an informal notion, which is formally capture by "semantics": if SKK and SKS are equals, then they should always reduce to the same term, according to the SKI-calculus semantics.
Now, there is a deep question, which is: what are the SKI-calculus? Actually, there is not a single way to answer that. What you implicitly do in your question is that you express SKI in terms of λ terms and you rely on the semantics of the λ calculus. This is absolutly correct. An other way to do it could have been to define directly SKI semantics. For instance, if you look at the wikipedia page, you can see that the semantics are not defined with lambda terms (and the fact that it correspond to lambda term is a (nice and expected) side effect). In the rest of this answer, I'll take the same approach as you do, and convert SKI terms in λ terms. A good exercise for you is to redo the proof, using the proper SKI semantics.
So, let formalize your question: your question is whether, for any SKI term t, SKKt = SKSt? Well... Let's see.
SKKt is encoded as (λx.λy.λz.(xz)(yz))(λx.λy.x)(λx.λy.x)t in the λ-calculus. We now just have to reduce it to a normal form (I detail every step, each time I reduce the leftmost λ, even tho it is not the fastest strategy):
(λx.λy.λz.(xz)(yz))(λx.λy.x)(λx.λy.x)t
= (λy.λz.((λx.λy.x)z)(yz))(λx.λy.x)t
= (λz.((λx.λy.x)z)((λx.λy.x)z))t
= ((λx.λy.x)t)((λx.λy.x)t)
= (λy.t)((λx.λy.x)t)
= t
So, the encoding of SKKt in the λ calculus reduces to t (as a sidenote, we just proved that SKK is equivalent to I here). To conclude our proof, we have to reduce SKSt and see whether it also reduces to t.
SKSt is encoded as (λx.λy.λz.(xz)(yz))(λx.λy.x)(λx.λy.λz.(xz)(yz))t. Let reduce it. (I don't detail as much this time)
(λx.λy.λz.(xz)(yz))(λx.λy.x)(λx.λy.λz.(xz)(yz))t
= ((λx.λy.x) t)((λx.λy.λz.(xz)(yz)) t))
= (λy.t)((λx.λy.λz.(xz)(yz)) t))
= t
Hurrah! It also reduce to t, so indeed, SKS and SKK are equivalent. It seems that the third combinator is not important: that as soon as you have SK?, it is equivalent to I. As an exercise, you can easily prove it (same strategy, if it is the case, then for any terms t and s, SKts = s). As mentionned above, an other good exercise is to redo the proof without using the λ semantics, but the proper SKI semantics.
Finally, my answer should raise a new question to you: we have two semantics, one that encodes SKI terms into λ terms, and one that does not. The question you may have is: are the two semantics equivalent? What does it mean for two semantics to be equivalent? If you are only starting to teach yourself λ calculus, it may be a bit early to try to answer those questions right now, but you can keep it in a corner of your head for when you'll get more familiar with formal languages.
The following algorithm is a rough sketch of model checking with Computational Tree Logic (CTL):
It is stated that:
The model-checking problem for CTL is to verify for a given transition system TS and CTL formula Φ whether TS |= Φ... The basic procedure for CTL model checking is rather straightforward:
the set Sat(Φ) of all states satisfying Φ is computed recursively, and
it follows that TS |= Φ if and only if I ⊆ Sat(Φ)
where I is the set of initial states of TS...
The recursive computation of Sat(Φ) basically boils down to a bottom-up traversal of the parse tree of the CTL state formula Φ.
So you essentially (from my understanding), you provide the system with a CTL formula Φ, which is a parse tree, and then it searches through the states, and through the CTL parse tree, and checks if any state satisfies Φ.
The question is:
In the Sat(Φ) method, roughly what happens (the symbolic stuff). They say (2) below, where S is states and A is atomic propositions. Wondering how they actually check the states, given that the program isn't actually running. It is (at least I think) Symbolic Model Checking. Wondering if one could explain roughly how the state checking works. It seems like some sort of input generation has to occur, but at the same time I'm thinking maybe it shouldn't occur.
The reason for it being hard to understand for me is this. Say one of the assertions is for a function addTricky(x, y) which is implemented like this:
function addTricky(x, y) {
if (y >= 1) return 3
return x + y
}
Then I would have a Boolean expression in some logic that says "before addTricky : z = 0. after z = addTricky(x, y) : y >= 1 -> z = 3 ; y < 1; z = x + y".
Basically trying to get at the question of patterns. If Sat(Φ) is doing basically what I just did in that Boolean expression, I wonder if it ever calls/invokes the function addTricky, or if it can do it all symbolically somehow. I don't see how that works yet, wondering if the basics of how the symbolic execution works could be explained a bit. To me I keep imagining it doing some sort of Unit Testing, like plugging in addTricky(1, 1) for example, and checking all the possibilities. Maybe that is "explicit state exploration" vs. symbolic exploration, not sure.
Thank you so much for the help!
(1) For each node of the parse tree, i.e., for each subformula Ψ of Φ, the set Sat(Ψ) of states is computed for which Ψ holds.
(2) Sat(a) = {s ∈ S | a ∈ L(s)}, for any a ∈ A
I think there are two parts to your question: 1) How to go from a software function to a transition system and 2) how is the transition system used to check satisfaction.
1) A transition system is basically an extension of a finite state automaton. If you have a function like you described, you first need to transform it into a transition system. This can be done, for example, by introducing states for each executable line of your code, and transitions between those states that follow the conditions of your code. At the transition system level you do not have the concept of function call, therefore you need to take care of this during the translation e.g., by in lining function definitions. This step is independent on how you verify the transition system. As you can imagine this can lead to pretty large transition systems.
There are other approaches, that are not based on transition systems, that simulate the execution of the program and collect symbolic constraints along the way. Symbolic execution is such an example.
2) Let's say that you inline your addTricky function and get something along these lines
L0: z=0
if (y>=1)
L1: z=3
else
L2: z=x+y
A possible TS is:
(L0: z=0) --[y >= 1]--> (L1: z=3)
|
[y<1]
\/
(L2: z=x+y)
You have 3 executable statements and this leads to a TS whose symboiic states (S) are:
L0: Z=0; X=?; Y=?
L1: Z=3; X=?; Y>=1
L2: Z=X+Y; X=?; Y<1
where ? means any value. The power of this approach is that you can compactly represent all the values of X and Y in a single symbolic state.
This looks like such an easy problem but still can't figure it out. How do I prove ¬(¬a = a)?
No given premises.
I got this so far (in Fitch):
This is a subproof where I assume the negation of my goal and then try to reach the absurd/contradiction so I can state the negation of my assumption, which would be my goal.
Thanks in advance!
Looking at your screenshot I'd say your =Intro introduces a variable a (that is, a is an object of the domain, rather than a predicate).
I say this because
in all books I've read, the =Intro rule is used for objects rather than predicates, and
for predicates, equals is expressed as "if and only if" which is typically written as ↔ and not =.
So, in other words, the only sensible interpretation of ¬(¬a = a) is that = binds harder than ¬, and the whole formula should be interpreted as ¬(¬(a = a)).
Now you should be able to
introduce a = a
assume the contrary: ¬(a = a)
arrive at a contradiction, ⊥, based on 1. and 2.
Use ¬Intro on 2 and 3 to get ¬(¬(a = a)).
Given a set of constraints, I would like to efficiently generate the set of values.
Suppose I have a few constraints on my Thungus[1]:
goodThungus(X) :-
X > 100,
X < 1000.
sin(X) = 0.
Now, I can check a Thungus by asking:
goodThungus(500).
I would like to generate all good Thungi. I'm not sure how to do that; I'm really not sure about how to do it efficiently.
Note: this of course has to be a computable generation.
[1] Arbitrary object selected for this example.
What you are asking for can't be done in the full general case: imagine doing f(X) = 0 where f is a function for which the roots cannot be analytically determined, for example. Or suppose f(X) is the function "does the program X halt?". No computer is going to solve that for you.
Your options are basically to either:
Limit the set of constraints to things that you can reason about. e.g. inequalities are good because you can identify ranges, then do intersections and unions on ranges efficiently etc.
Limit the set of values to a small enough number that you can test them individually against each of the constraints
UPDATE: For the kind of constraints stated in the question (ranges of real values and real-valued functions that can be analytically solved and have a finite number of solutions within any range) I would suggest the following approach:
Write a generating function that can iteratively return solutions for you function within a given range... this will need to be done analytically e.g. exploiting the fact that sin(X)=0 implies X=n*pi where n is any integer.
Do interval arithmetic and bounding on your range constraints to work out the range(s) that need to be scanned (in the example you would want the range 100 < X < 1000)
Apply your generating function to each of the target ranges in order to create all of the possible solutions.
I'll preface my suggestion by stating that I'm no expert in using numerical constraint logic programming systems, but here goes...
On the surface, I'd think that solving this kind of problem in PROLOG would be best suited to a numerical constraint logic programming system, perhaps such as CLP(R) (for reals) in SWI-PROLOG; unfortunately, the specific problem you've asked for is seeking to solve for a set of constraints including a non-linear constraint, which seems to be not well or widely supported amongst PROLOG implementations; instead, they seem to deal mainly with linear constraints and often have limited support for non-linear constraints such as X = sin(Y), for example.
Take SWI-PROLOG's CLP(R) library, and the following example program:
:- use_module(library(clpr)).
report_xsq_zeros :-
findall(X, {0 = (X * X) - 10}, Results),
write_ln(Results).
report_sin_zeros :-
findall(X, {0 = sin(X)}, Results),
write_ln(Results).
Now, executing report_xsq_zeros gives us:
?- report_xsq_zeros.
[3.16228, -3.16228]
true.
Here, the system correctly computed the zeros of the quadratic x^2 - 10, which are indeed approximately 3.16228 and -3.16228, where the range of X was unbounded. However, when we execute report_sin_zeros, we get:
?- report_sin_zeros.
[0.0]
true.
We see that the system only computed a single zero of the function sin(X), even though the range of X was indeed also unbounded. Perhaps this is because it is recognized that there are an infinite number of solutions here (though I'm only guessing...). If we were to program what you've asked for:
report_sin_zeros :-
findall(X, {X > 100, X < 1000, 0 = sin(X)}, Results),
write_ln(Results).
We get no results, as the underlying system only computed a single zero for sin(X) as shown earlier (i.e., binding X to 0.0 which lies outside the stated range):
?- report_sin_zeros.
[]
true.
I conclude that I've either not demonstrated proper usage of SWI-PL CLP(R) (I suggest you look into it yourself), or it won't solve your specific (non-linear) problem. Other CLP(R) implementations may behave differently to SWI-PROLOG CLP(R), but I don't have them installed so I can't check, but you could try SICSTUS CLP(R) or others; the syntax looks similar.
He is searching any X in [100..1000] for that sin(x) = 0. But this is a pure mathematical problem, and not meant for relational logical deduction / backtracking. simple Prolog is not suited for this?
I am having problems for converting following algo in ocaml To implement this algorithm i used Set.Make(String) functor actualy in and out are 2 functions Can any one give me percise code help in ocaml for following
This is actually Algo for Live Variables[PDF] ..Help would be appreciated greatly
for all n, in[n] = out[n] = Ø
w = { set of all nodes }
repeat until w empty
n = w.pop( )
out[n] = ∪ n’ ∈ succ [n] in[n’]
in[n] = use[n] ∪ (out[n] — def [n])
if change to in[n],
for all predecessors m of n, w.push(m)
end
for all n, in[n] = out[n] = Ø
w = { set of all nodes }
repeat until w empty
n = w.pop( )
out[n] = ∪ n’ ∈ succ [n] in[n’]
in[n] = use[n] ∪ (out[n] — def [n])
if change to in[n],
for all predecessors m of n, w.push(m)
end
It's hard for me to tell what is exactly going on here. I think there is some alignment issues with your text --repeat until w empty should be repeating the next 5lines, right? And how are in and out functions, they look like arrays to me? Aside from those deficiencies I'll tackle some general rules I have followed.
I've had to translate a number of numerical methods in C and Fortran algorithms to functional languages and I have some suggestions for you.
0) Define the datatypes being used. This will really help you with the next step (spoiler: looking for functional constructs). Once you know the datatypes you can more accurately define the functional constructs you will eventually apply.
1) Look for functional constructs. fold, recursion, and maps, and when to use them. For example, the for all predecessors m is a fold (unsure if that it would fold over a tree or list, but none-the-less, a fold). While loops are a good place for recursion --but don't worry about making it a tail call, you can modify the parameters later to adhere to those requirements. Don't worry about being 100% pure. Remove enough impure constructs (references or arrays) to get a feel for the algorithm in a functional way.
2) Write any part of the algorithm that you can. Leaving functions blank, add dummy values, and just implement what you know --then you can ask SO better, more informed questions.
3) Re-write it. There is a good chance you missed some functional constructs or used an array or reference where you now realize you can use a list or set or by passing an accumulator. You may have defined a list, but later you realize you cannot randomly access it (well, it would be pretty detrimental to), or it needs to be traversed forward and back (a good place for a zipper). Either way, when you finally get it you'll know, and you should have a huge ear-to-ear grin on your face.