google / tangent

Source-to-Source Debuggable Derivatives in Pure Python
Apache License 2.0
2.31k stars 434 forks source link

AttributeError: 'NoneType' object has no attribute 'sum' #73

Open mrocklin opened 6 years ago

mrocklin commented 6 years ago

I'm probably doing something silly here, but I was confused by this error:

In [1]: import tangent

In [2]: import numpy as np

In [3]: def f(x):
   ...:     return np.exp(x).sum() + 1
   ...: 
   ...: df = tangent.grad(f)
   ...: 
   ...: 
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-2246896ce926> in <module>()
      2     return np.exp(x).sum() + 1
      3 
----> 4 df = tangent.grad(f)

~/workspace/tangent/tangent/grad_util.py in grad(func, wrt, optimized, preserve_result, check_dims, verbose)
    386       check_dims=check_dims,
    387       input_derivative=INPUT_DERIVATIVE.DefaultOne,
--> 388       verbose=verbose)
    389 
    390 

~/workspace/tangent/tangent/grad_util.py in autodiff(func, wrt, optimized, motion, mode, preserve_result, check_dims, input_derivative, verbose)
    290   # Generate the derivative
    291   node, namespace = autodiff_tree(func, wrt, motion, mode, preserve_result,
--> 292                                   check_dims, verbose)
    293 
    294   if mode == 'reverse' and motion == 'joint':

~/workspace/tangent/tangent/grad_util.py in autodiff_tree(func, wrt, motion, mode, preserve_result, check_dims, verbose)
    144 
    145   node, required = autodiff_ast(func, wrt, motion, mode, preserve_result,
--> 146                                 check_dims, verbose)
    147   final.body.extend(node.body)
    148 

~/workspace/tangent/tangent/grad_util.py in autodiff_ast(func, wrt, motion, mode, preserve_result, check_dims, verbose)
     88         for the returned function to run.
     89   """
---> 90   node = annotate.resolve_calls(func)
     91   node = desugar.explicit_loop_indexes(node)
     92   fence.validate(node, inspect.getsource(func))

~/workspace/tangent/tangent/annotate.py in resolve_calls(func)
    108   """
    109   node = quoting.parse_function(func)
--> 110   ResolveCalls(func).visit(node)
    111   return node
    112 

~/Software/miniconda/lib/python3.6/ast.py in visit(self, node)
    251         method = 'visit_' + node.__class__.__name__
    252         visitor = getattr(self, method, self.generic_visit)
--> 253         return visitor(node)
    254 
    255     def generic_visit(self, node):

~/Software/miniconda/lib/python3.6/ast.py in generic_visit(self, node)
    259                 for item in value:
    260                     if isinstance(item, AST):
--> 261                         self.visit(item)
    262             elif isinstance(value, AST):
    263                 self.visit(value)

~/Software/miniconda/lib/python3.6/ast.py in visit(self, node)
    251         method = 'visit_' + node.__class__.__name__
    252         visitor = getattr(self, method, self.generic_visit)
--> 253         return visitor(node)
    254 
    255     def generic_visit(self, node):

~/workspace/tangent/tangent/annotate.py in visit_FunctionDef(self, node)
     43 
     44   def visit_FunctionDef(self, node):
---> 45     self.generic_visit(node)
     46     anno.setanno(node, 'func', self.func)
     47 

~/Software/miniconda/lib/python3.6/ast.py in generic_visit(self, node)
    259                 for item in value:
    260                     if isinstance(item, AST):
--> 261                         self.visit(item)
    262             elif isinstance(value, AST):
    263                 self.visit(value)

~/Software/miniconda/lib/python3.6/ast.py in visit(self, node)
    251         method = 'visit_' + node.__class__.__name__
    252         visitor = getattr(self, method, self.generic_visit)
--> 253         return visitor(node)
    254 
    255     def generic_visit(self, node):

~/Software/miniconda/lib/python3.6/ast.py in generic_visit(self, node)
    261                         self.visit(item)
    262             elif isinstance(value, AST):
--> 263                 self.visit(value)
    264 
    265 

~/Software/miniconda/lib/python3.6/ast.py in visit(self, node)
    251         method = 'visit_' + node.__class__.__name__
    252         visitor = getattr(self, method, self.generic_visit)
--> 253         return visitor(node)
    254 
    255     def generic_visit(self, node):

~/Software/miniconda/lib/python3.6/ast.py in generic_visit(self, node)
    261                         self.visit(item)
    262             elif isinstance(value, AST):
--> 263                 self.visit(value)
    264 
    265 

~/Software/miniconda/lib/python3.6/ast.py in visit(self, node)
    251         method = 'visit_' + node.__class__.__name__
    252         visitor = getattr(self, method, self.generic_visit)
--> 253         return visitor(node)
    254 
    255     def generic_visit(self, node):

~/workspace/tangent/tangent/annotate.py in visit_Call(self, node)
     64                     node.id, self.func.__name__))
     65 
---> 66     func = resolve(node.func)
     67     # If the user has used the @tangent.trace decorator,
     68     # then we'll switch to tracing the function.

~/workspace/tangent/tangent/annotate.py in resolve(node)
     51     def resolve(node):
     52       if isinstance(node, gast.Attribute):
---> 53         return getattr(resolve(node.value), node.attr)
     54       if isinstance(node, gast.Name):
     55         if node.id in self.namespace:

AttributeError: 'NoneType' object has no attribute 'sum'
alexbw commented 6 years ago

Hey, we have an implicit contract that all functions must be called via modules, not as object methods. Try np.sum(np.exp(x)) + 1. We haven't gotten around to enforcing this automatically or intelligently.

mrocklin commented 6 years ago

Thanks. That does seem to work.

Is this documented? Can I recommend that this also be included in an intelligent error message directing users to the right behavior?

On Mon, May 21, 2018 at 11:15 AM, Alex Wiltschko notifications@github.com wrote:

Hey, we have an implicit contract that all functions must be called via modules, not as objects methods. Try np.sum(np.exp(x)) + 1. We haven't gotten around to enforcing this automatically or intelligently.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/google/tangent/issues/73#issuecomment-390685147, or mute the thread https://github.com/notifications/unsubscribe-auth/AASszKeElZSYFViT4Wis3j1AtR39kvAZks5t0toSgaJpZM4UHIN9 .

alexbw commented 6 years ago

You're correct that we need to make this specific thing better and clearer. I'll think on how best to do that. On Mon, May 21, 2018 at 11:18 AM Matthew Rocklin notifications@github.com wrote:

Thanks. That does seem to work.

Is this documented? Can I recommend that this also be included in an intelligent error message directing users to the right behavior?

On Mon, May 21, 2018 at 11:15 AM, Alex Wiltschko <notifications@github.com

wrote:

Hey, we have an implicit contract that all functions must be called via modules, not as objects methods. Try np.sum(np.exp(x)) + 1. We haven't gotten around to enforcing this automatically or intelligently.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/google/tangent/issues/73#issuecomment-390685147, or mute the thread < https://github.com/notifications/unsubscribe-auth/AASszKeElZSYFViT4Wis3j1AtR39kvAZks5t0toSgaJpZM4UHIN9

.

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/google/tangent/issues/73#issuecomment-390685981, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJ4j7xw74I5y3yOIDzSkj7_B9OroSOSks5t0trEgaJpZM4UHIN9 .