N8_Coder
N8_Coder

Reputation: 803

Evaluation order of chained numpy statements

I would like to understand/find out in which order chained numpy statements are evaluated. For example:

x = A.dot(B).dot(C)  

Is it (A.dot(B)).dot(C) or A.dot(B.dot(C)) or even (A.dot(C)).dot(B)?
Is there any way to see what happens in memory, what the interpreter's execution plan is or is there a general rule?

Upvotes: 1

Views: 199

Answers (1)

James Pringle
James Pringle

Reputation: 1079

Please always give a reproducible example so that others can easily try things out on their computers. I do not know what A, B, or C are.

When methods are chained as you have it, the .dot() applies to the expression that immediately precedes it. You have A.dot(B).dot(C), so the .dot(B) applies to A, and .dot(C) applies to the result of A.dot(B). In other words, it is equivalent to (A.dot(B)).dot(C). In order to see what the interpreter is doing, try the ast module:

import ast
ast.dump(ast.parse("""A.dot(B).dot(C)"""))

and with some formatting, you get

Module(
    body=[
        Expr(
            value=Call(
                func=Attribute(
                    value=Call(
                        func=Attribute(
                            value=Name(id='A', ctx=Load()), 
                            attr='dot', 
                            ctx=Load()
                        ), 
                        args=[Name(id='B', ctx=Load())], 
                        keywords=[]
                    ), 
                    attr='dot', 
                    ctx=Load()
                ), 
                args=[Name(id='C', ctx=Load())], keywords=[])
            )
    ], type_ignores=[]
)

(note in Python 3.9, you can use the indent keyword on ast.dump to do the formatting automatically, i.e. ast.dump(ast.parse("""A.dot(B).dot(C)"""), indent=4)).

From the parsed version, you can see the inner most code is calling A.dot(B) first, then applying .dot(C) to that result.

To remove ambiguity, you can use np.dot() instead, i.e.

np.dot(np.dot(A, B), C)

to move from infix to prefix notation, as it were, to remove the ambiguity you see.

See the docs for numpy's dot here.

Upvotes: 1

Related Questions