6
Lazy Evaluation: Principles Postulates: Any computation is activated if and only if it is necessary for one or more other computations. This situation is named as necessity of computation. An active computation is stopped if and only if its necessity vanishes (for example it has been satisfied). The computation, as a whole, is activated forcibly as a request (necessity) to obtain the results of computational system execution. Consequences: The activation of all computational units is driven by a dataflow started when a necessity of computation as a whole arises. A control flow of the computation is not considered as a priori defined process. It is formed up dynamically by the necessities of computations only.

8
Analysis of cat File_F | grep WWW | head -1 Lazy and Eager Evaluation Eager variant of execution: cat File_Fgrep WWWhead -1 stdout stdin stdout stdin stdout One string All strings of File_F Strings with ‘WWW’ Lazy variant of execution: cat File_Fgrep WWWhead -1 stdout stdin stdout stdin stdout One string One string with ‘WWW’ For strings of File_F UNIX pipeline may be considered as optimization of lazy evaluation (in this case)! Suppose that the needed string was not detected at first We omit intermediate steps here F A nice question: is this version more correct? Note: It is the same object in both cases!

15
FC++ library High order functions — functions with functional arguments FC++ library is a general framework for functional programming Polymorphic functions—passing them as arguments to other functions and returning them as results. Support higher-order polymorphic operators like compose(): a function that takes two functions as arguments and returns a (possibly polymorphic) result Large part of the Haskell Support for lazy evaluation transforming FC++ data structures ⇔ data structures of the C++ Standard Template Library (STL) operators for promoting normal functions into FC++ functoids. Finally, the library supplies “indirect functoids”: run-time variables that can refer to any functoid with a given monomorphic type signature.

17
Memoization Imperative scheme 1.We need to call expressions explicitly only 2.Procedure calls depend on the context 3.Strong sequence of computation units (hard for parallelization) 4.If we want to memoize previous results we should do this explicitly 5.Memoization process is controlled by programmer 6.Only manual transforming to a suitable scheme of data representation is possible 7.Circle head and circle body are joined monolithicly 8.Controlflow centric approach 9.Require a difficult technique for def- use chains analysis Functional scheme 1.Expressions do not depend on context 2.Context independent procedure calls 3.The sequence of computation units is chosen by execution system (more flexible for parallelization) 4.Automatic memoization 5.Memoization process is not controlled, but filters are allowed (indirect control) 6.Stack technique of program representation and execution is not appropriate 7.Constructions like reduce and composition of functions allow considering circle head and body independently 8.Dataflow centric approach 9.Suitable for def-use chains analysis Fibonacci example: F (n) = F (n-1) + F (n-2) It is a classical bad case for imperative computations. Why? Previous functional programs are easier for development and understanding. Why?

18
Lazy Evaluation in Boost/uBLAS: Vectors/Matrices Expressions Example: A + prod (B,V) –Assignment activate evaluation –Indexes define evaluation of expression tree: if we write the example, we initiate the following computation for all i { A [i] + ∑ k (B [i,k] *V[k]) } (1) “ for all ” means a compatibility of computations (order of computations is chosen by the execution system) It is possible (but not necessary) to have the following representation: A + [ ∑ k (B [i,k] *V[k])] (” [ ] “denotes a vector constructor) Necessity of computation is defined by this fragment for each i (may be dynamically) Postulate that “ = “ operator always leads to appearance of necessity of computations: its left hand side should be computed and assigned to the right hand side: D = A + prod (B,V); Common expression tree is used for computations for each i in (1) –Types coordination is hold: Correct vectors/matrices expression includes constituents with types allowed by operators of expression (including prod and others)

19
Boost/uBLAS: vectors/matrices expressions: temporary and memoization problems Let us consider x = A*x expression (this is an error from the functional style viewpoint, but correct for operational C++) –Naive implementation is for all i { x [i] = ∑ k (A [i,k] * x[k]); } It is not correct! –The suitable implementation should use a temporary t : for all i { t [i] = ∑ k (A [i,k] *x[k]); }x = t; The last assignment should not be a copy of the value, but a reference coping and deleting the previous value of x. Let us consider A * ( B * x). –If we don’t use the lazy evaluation, we obtain an n 2 complexity (C1*n 2 for B * x plus C2*n 2 for other multiplication). –But in a straightforward lazy case the obtained complexity would be n 3. It is not a problem for a real functional language implementation because of a value propagation technique (automatic memoization) Instead of the value propagation technique in C++ implementation, we can provide a temporary. Its assignment breaks the expression We use temporaries in both cases. But what part of information should be really saved?

20
Boost/uBLAS: style of programming Object-oriented style –Standard technique and patterns should be used C++ style (as addition to the previous) –Standard template technique Vectors/matrices expressions (as addition to the previous) –Tendency to use matrix and vector objects instead of variables with indexes –Tendency to write expressions instead of simple statements –Use uBLAS primitives as specializations of general templates –Don’t use direct classes extensions by multilevel inheritance

21
Boost/uBLAS. Example task: Jacobi method Let us consider the Jacobi method of solving the linear system –We are able to write it using such formulas as –Instead of this, we should use it in matrix terms as –As the result, we obtain the following program (next slide)

25
Boost/uBLAS: style of programming (limitations) Frequently a rejection of the vectors/matrices style is required A problem of styles compatibility Not closed set of operators with matrices and vectors are presented –Instead of vectors/matrices operator “*” one should use a generic function prod (mv1, mv2) provided for all needed cases –uBLAS Vectors and Matrices operators are not presented as an algebraic system (for instance “ -1 ”, “|| ||” are not offered because of the problems of vectors/matrices expression lazy evaluation) An acceptable approach may be proposed as follow: –The results of “ -1 ”, “|| ||” and so on computation are considered as attributes of matrix and vector classes –These attributes are computed out of the expression computation by outside control if it is necessity –Expression’s constituents with these operators are replaced by extraction needed items from corresponded attributes The direction to Vectors and Matrices expression is very promising

26
The myths about lazy evaluations 1.Lazy evaluations is possible only in functional languages 2.Lazy evaluations and functional languages may be applied only in artificial intelligence area 3.We are able to realize lazy evaluations using an arbitrary programming language 4.Using lazy evaluations decreases performance Examples above indicate that it is not right As we have seen it is not right: high order functions using is very prospective in many cases This statement depends on quality of algorithms programming only Language states a lot of limitation for lazy evaluations using* * C++ is subjected to criticism from this point of view by blitz++ project developers