Eliminate unnecessary Piecewise terms from Sympy expressions - sympy

Sympy often produces expressions like
Sum(Piecewise((2**(-N)*exp(-_k*beta)*binomial(N, _k), _k <= N), (0, True)), (_k, 0, N))
where the first condition of the Piecewise is always satisfied, since it is ensured by the summation range.
In this example I can eliminate the Piecewise using
X.refine(Q.le(X.bound_symbols[0], N))
which works because I know the form of the expression.
I would like to find a general solution that can eliminate redundant Piecewise case anywhere in an expression in most reasonable cases without knowing the structure of the expression in advance, since it needs to be applied to results of other simplifications. Something like "refine by implicit constraints on bound variables". None of the simplification functions seem able to do this.
Note that the summation variable is introduced by Sympy so I do not have control over its creation.

Related

Generating branch instructions from an AST for a conditional expression

I am trying to write a compiler for a domain-specific language, targeting a stack-machine based VM that is NOT a JVM.
I have already generated a parser for my language, and can readily produce an AST which I can easily walk. I also have had no problem converting many of the statements of my language into the appropriate instructions for this VM, but am facing an obstacle when it comes to the matter of handling the generation of appropriate branching instructions when complex conditionals are encountered, especially when they are combined with (possibly nested) 'and'-like or 'or' like operations which should use short-circuiting branching as applicable.
I am not asking anyone to write this for me. I know that I have not begun to describe my problem in sufficient detail for that. What I am asking for is pointers to useful material that can get me past this hurdle I am facing. As I said, I am already past the point of converting about 90% of the statements in my language into applicable instructions, but it is the handling of conditionals and generating the appropriate flow control instructions that has got me stumped. Much of the info that I have been able to find so far on generating code from an AST only seems to deal with the generation of code corresponding to simple imperative-like statements, but the handing of conditionals and flow control appears to be much more scarce.
Other than the short-circuiting/lazy-evaluation mechanism for 'and' and 'or' like constructs that I have described, I am not concerned with handling any other optimizations.
Every conditional control flow can be modelled as a flow chart (or flow graph) in which the two branches of the conditional have different targets. Given that boolean operators short-circuit, they are control flow elements rather than simple expressions, and they need to be modelled as such.
One way to think about this is to rephrase boolean operators as instances of the ternary conditional operator. So, for example, A and B becomes A ? B : false and A or B becomes A ? true : B [Note 1]. Note that every control flow diagram has precisely two output points.
To combine boolean expressions, just substitute into the diagram. For example, here's A AND (B OR C)
You implement NOT by simply swapping the meaning of the two out-flows.
If the eventual use of the boolean expression is some kind of conditional, such as an if statement or a conditional loop, you can use the control flow as is. If the boolean expression is to be saved into a variable or otherwise used as a value, you need to fill in the two outflows with code to create the relevant constant, usually a true or false boolean constant, or (in C-like languages) a 1 or 0.
Notes:
Another way to write this equivalence is A and B ⇒ A ? B : A; A or B ⇒ A ? A : B, but that is less useful for a control flow view, and also clouds the fact that the intent is to only evaluate each expression once. This form (modified to reuse the initial computation of A) is commonly used in languages with multiple "falsey" values (like Python).

How to simplify a C-style arithmetical expression containing variables during code generation?

I am trying to optimize expression evaluation in a compiler.
The arithmetical expressions are all C-style, and they may contain variables. I hope to simplify the expressions as much as possible.
For example, (3+100*A*B+100)*3+100 may be simplified to 409+300*A*B.
It mainly depends on the distributive law, the associative law and the commutative law.
The main difficulty I encounter is how to combine these arithmetical laws and traditional stack-scan evaluating algorithms.
Can anyone share experiences related to this or similar problems in the context of compiler building?
Compilers usually have some internal normalization rules like "constants to the left". This means a + 3 would be transformed into 3 + a, but not vice versa.
In your example,
(3+100*A*B+100)*3+100 would be normalized into
(3+100+(100*A*B))*3+100.
Now it is clear to optimize 3+100.
Another transformation might be a*C1+C2 into (a+(C2/C1))*C1 under the condition that C1 and C2 are constants. Intuitively, this normalizes "add before multiply".
Those normalizations are not optimizations. The intention is mostly to group constants together, so constant folding is more effective.
Apply constant folding combined with strength reduction during the code generation pass of the compilation. Most compiler texts will provide an algorithm to implement this.

DFA to regular expression time complexity

I am looking at the time complexity analysis of converting DFAs to regular expressions in the
"Introduction to the Automata Theory, Languages and Computation", 2nd edition, page 151, by Ullman et al. This method is sometimes referred to as the transitive closure method. I don't understand how they came up with the 4^n expression in the O((n^3)*(4^n)) time complexity.
I understand that the 4^n expression holds regarding space complexity, but, regarding time complexity, it seems that we are performing only four constant time operations for each pair of states at each iteration, using the results of the previous iterations. What am I exactly missing?
It's a crude bound on the complexity of an algorithm that isn't using the right data structures. I don't think that there's much to explain other than that the authors clearly did not care to optimize here, probably because their main point was that regular expressions are at least as expressive as DFAs and because they feel that it's pointless to optimize this exponential-time algorithm.
There are three nested loops of n iterations each; the regular expressions constructed during iteration k of the outer loop inductively have size O(4^k), since they are constructed from at most four regular expressions constructed during the previous iteration. If the algorithm copies these subexpressions and we overestimate the regular-expression size bound at O(4^n) for all iterations, then we get O(n^3 4^n).
Obviously we can do better. Without eliminating the copying, we can get O(sum_{k=1}^n n^2 4^k) = O(n^2 (n + 4^n)) by bounding the geometric sum properly. Moreover, as you point out, we don't need to copy at all, except at the end if we agree with templatetypedef that the output must be completely written out, giving a running time of O(n^3) to prepare the regular expression and O(4^n) to write it out. The space complexity for this version equals the time complexity.
I suppose your doubt is about the n3 Time Complexity.
Let us assume Rijk represents the set of all strings that transition the automata from state qi to qj without passing through any state higher than qk.
Then the iterative formula for Rijk is shown below,
Rijk = Rikk-1 (Rkkk-1)* Rkjk-1 + Rijk-1.
This technique is similar to the all-pairs shortest path problem. The only difference is that we are taking the union and concatenation of regular expressions instead of summing up distances. The Time Complexity of all-pairs shortest path problem is n3. So we can expect the same complexity for DFA to Regular Expression Conversion also. The same method can also be used to convert NFA and ε-NFA to corresponding Regular Expressions.
The main problem of transitive closure approach is that it creates very large regular expressions. This large length is due to the repeated union of concatenated terms.

Is there a way to negate a regular expression?

Given a regular expression R that describes a regular language (no fancy backreferences). Is there an algorithmic way to construct a regular expression R* that describes the language of all words except those described by R? It should be possible as Wikipedia says:
The regular languages are closed under the various operations, that is, if the languages K and L are regular, so is the result of the following operations: […] the complement ¬L
For example, given the alphabet {a,b,c}, the inverse of the language (abc*)+ is (a|(ac|b|c).*)?
As DPenner has already pointed out in the comments, the inverse of a regular expresion can be exponentially larger than the original expression. This makes inversing regular expressions unsuitable to implement negative partial expression syntax for searching purposes. Is there an algorithm that preserves the O(n*m) runtime characteristic (where n is the size of the regex and m is the length of the input) of regular expression matching and allows for negated subexpressions?
Unfortunately, the answer given by nhahdtdh in the comments is as good as we can do (so far). Whether a given regular expression generates all strings is PSPACE-complete. Since all problems in NP are in PSPACE-complete, an efficient solution to the universality problem would imply that P=NP.
If there were an efficient solution to your problem, would you be able to resolve the universality problem? Sure you would.
Use your efficient algorithm to generate a regular expression for the negation;
Determine whether the resulting regular expression generates the empty set.
Note that the problem "given a regular expression, does it generate the empty set" is fairly straightforward:
The regular expression {} generates the empty set.
(r + s) generates the empty set iff both r and s generate the empty set.
(rs) generates the empty set iff either r or s generates the empty set.
Nothing else generates the empty set.
Basically, it's pretty easy to tell whether a regular expression generates the empty set: just start evaluating the regular expression.
(Note that while the above procedure is efficient in terms of the output length, it might not be efficient in terms of the input length, if the output length is more than polynomially faster than the input length. However, if that were the case, we'd have the same result anyway, i.e., that your algorithm isn't really efficient, since it would take exponentially many steps to generate an exponentially longer output from a given input).
Wikipedia says: ... if there exists at least one regex that matches a particular set then there exist an infinite number of such expressions. We can deduct from this statement that there is an infinite number of expressions that describe the language of all words except those described by R.
Again, (as also #nhahtdh tried to explain) the simplest algorithm to address this question is to extend the scope of evaluation outside the context of the regular expression language itself. That is: match the strings you want to exclude (which represent a finite subset to work with) by using the original regular expression and then treat any failure to match as an actual match (out of an infinite set of other possibilities). So, if the result of the match is negative, your candidate strings are a subset of the valid solutions.

Are implied DO loops inefficient?

I have an array initialization based on an implied do loop, given an odd size N.
J=(N+1)/2
XLOC(1:N) = (/ (I-J, I=1,N) /)
In the context of F90+ is it recommended to use the (/ .. /) syntax, or is more efficient to use a FORALL statement.
Example: for N=19 then XLOC=(-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9)
How else would you initialize this array?
Edit 1
How would you initialize this array with more readable code?
For such a simple construct both are likely to lead to the same code because compilers are good at optimizing. The FORALL statement is not so much a looping statement but an initialization statement that has many restrictions that can inhibit optimizations. If a simple loop will work, I'd use it.
Also see this previous answer: Do Fortran 95 constructs such as WHERE, FORALL and SPREAD generally result in faster parallel code?
There is no reason they should be less efficient that actual do loops. If you find a case, where they are, report it as an missed optimization bug to your compiler vendor!