from devito import configuration
configuration['opt'] = 'noop'
configuration['language'] = 'C'

"""
Explanation: In this tutorial we will show how to access and navigate the Iteration/Expression Tree (IET) rooted in an Operator.
Part I - Top Down
Let's start with a fairly trivial example. First of all, we disable all performance-related optimizations, to maximize the simplicity of the created IET as well as the readability of the generated code.
End of explanation
"""


from devito import Grid, TimeFunction

grid = Grid(shape=(3, 3))
u = TimeFunction(name='u', grid=grid)

"""
Explanation: Then, we create a TimeFunction with 3 points in each of the space Dimensions x and y.
End of explanation
"""


from devito import Eq, Operator

eq = Eq(u.forward, u+1)
op = Operator(eq)

"""
Explanation: We now create an Operator that increments by 1 all points in the computational domain.
End of explanation
"""


op.input

op.output

"""
Explanation: An Operator is an IET node that can generate, JIT-compile, and run low-level code (e.g., C). Just like all other types of IET nodes, it's got a number of metadata attached. For example, we can query an Operator to retrieve the input/output Functions.
End of explanation
"""


print(op)

"""
Explanation: If we print op, we can see how the generated code looks like.
End of explanation
"""


from devito.tools import pprint
pprint(op)

"""
Explanation: An Operator is the root of an IET that typically consists of several nested Iterations and Expressions – two other fundamental IET node types. The user-provided SymPy equations are wrapped within Expressions. Loop nest embedding such expressions are constructed by suitably nesting Iterations.
The Devito compiler constructs the IET from a collection of Clusters, which represent a higher-level intermediate representation (not covered in this tutorial).
The Devito compiler also attaches to the IET key computational properties, such as sequential, parallel, and affine, which are derived through data dependence analysis.
We can print the IET structure of an Operator, as well as the attached computational properties, using the utility function pprint.
End of explanation
"""


op._headers

op._includes

op.body

"""
Explanation: In this example, op is represented as a &lt;Callable Kernel&gt;. Attached to it are metadata, such as _headers and _includes, as well as the body, which includes the children IET nodes. Here, the body is the concatenation of an PointerCast and a List object.
End of explanation
"""


print(op.body[0])  # Printing the PointerCast

print(op.body[1])  # Printing the List

"""
Explanation: We can explicitly traverse the body until we locate the user-provided SymPy equations.
End of explanation
"""


t_iter = op.body[1].body[0]
t_iter

"""
Explanation: Below we access the Iteration representing the time loop.
End of explanation
"""


t_iter.limits

"""
Explanation: We can for example inspect the Iteration to discover what its iteration bounds are.
End of explanation
"""


expr = t_iter.nodes[0].body[0].body[0].nodes[0].nodes[0].body[0]
expr.view

"""
Explanation: And as we keep going down through the IET, we can eventually reach the Expression wrapping the user-provided SymPy equation.
End of explanation
"""


from devito.ir.iet import Expression, FindNodes
exprs = FindNodes(Expression).visit(op)
exprs[0].view

"""
Explanation: Of course, there are mechanisms in place to, for example, find all Expressions in a given IET. The Devito compiler has a number of IET visitors, among which FindNodes, usable to retrieve all nodes of a particular type. So we easily 
can get all Expressions within op as follows
End of explanation
"""


