Source-to-Source Debuggable Derivatives in Pure Python

Overview

Tangent

Build Status Join the chat at https://gitter.im/google/tangent

Tangent is a new, free, and open-source Python library for automatic differentiation.

Existing libraries implement automatic differentiation by tracing a program's execution (at runtime, like PyTorch) or by staging out a dynamic data-flow graph and then differentiating the graph (ahead-of-time, like TensorFlow). In contrast, Tangent performs ahead-of-time autodiff on the Python source code itself, and produces Python source code as its output. Tangent fills a unique location in the space of machine learning tools.

Autodiff Tool Space

As a result, you can finally read your automatic derivative code just like the rest of your program. Tangent is useful to researchers and students who not only want to write their models in Python, but also read and debug automatically-generated derivative code without sacrificing speed and flexibility.

Tangent works on a large and growing subset of Python, provides extra autodiff features other Python ML libraries don't have, has reasonable performance, and is compatible with TensorFlow and NumPy.

This project is an experimental release, and is under active development. As we continue to build Tangent, and respond to feedback from the community, there might be API changes.

Usage

Note: An interactive notebook with all the code in this page can be found here.

Tangent has a one-function API:

import tangent
df = tangent.grad(f)

If you want to print out derivatives at the time Tangent generates the derivative function:

import tangent
df = tangent.grad(f, verbose=1)

Here's Tangent in action in the IPython console.

Live Derivatives with Tangent

Installing and running

Installation

The easiest way to install Tangent is to use pip.

pip install tangent

We'll have a conda package soon.

Automatic Differentiation

Under the hood, tangent.grad grabs the source code of the Python function you pass it (using inspect.getsource, which is available in the Python standard library), converts the source code into an abstract syntax tree (AST) using ast.parse (also built into the Python standard library), and walks the syntax tree in reverse order.

Tangent has a library of recipes for the derivatives of basic arithmetic (+,-,/,**,*), pieces of syntax (ast.For, ast.If, ast.While) and TensorFlow Eager functions (tf.reduce_sum, tf.exp, tf.matmul, ... ). For each piece of syntax it encounters (for example, c = a + b is a single AST node ast.Assign), tangent.grad looks up the matching backward-pass recipe, and adds it to the end of the derivative function. This reverse-order processing gives the technique its name: reverse-mode automatic differentiation.

TF Eager

Tangent supports differentiating functions that use TensorFlow Eager functions that are composed together.

def f(W,x):
  h1 = tf.matmul(x,W)
  h2 = tf.tanh(h1)
  out = tf.reduce_sum(h2)
  return out

dfdW = tangent.grad(f)

SCT on TF Eager

Subroutines

When model code becomes long, using subroutines makes code more readable and reusable. Tangent handles taking derivatives of models that have user-defined functions.

SCT on Subroutines

Control Flow

Tangent has recipes for auto-generating derivatives for code that contains if statements and loops:

SCT on Conditionals

You'll notice above that we have to modify the user's code to keep track of information that we will need in the backward pass. For instance, we need to save which branch of an if-statement was followed in the forward pass, so that we run the correct branch in the backward pass. We save this information from the forward pass by pushing it onto a stack, which we then pop off in the backward pass. This is an important data structure in ahead-of-time autodiff.

For loops require a little more bookkeeping. Tangent has to save the number of iterations of the loop on the stack. Also, loops usually overwrite the values of variables inside the loop body. In order to generate a correct derivative, Tangent has to keep track of all of the overwritten values, and restore them in the backward pass in the correct order.

SCT on Loops

Custom Gradients

Tangent uses Python's built-in machinery to introspect and transform the abstract syntax tree (AST) of parsed source code at runtime. For each piece of supported Python syntax, we have implemented a rule indicating how to rewrite an AST node into its backward pass equivalent, or "adjoint". We have defined adjoints for function calls to NumPy and TF Eager methods, as well as larger pieces of syntax, such as if-statements and for-loops. The adjoints are stored in function definitions that serve as "templates", or code macros. Another alternative, which we found too cumbersome, would be to use a templating engine like Mustache and store adjoints as plain strings. Our templates also use a special syntax d[x] to refer to the derivative of a variable x.

While differentiating a function, if Tangent encounters a function call, it first checks if it has a gradient registered for that function. If not, it tries to get the function source, and generate a derivative ahead-of-time. But, it's easy to register your own gradients. Here's a toy example of defining the gradient of x^3.

import tangent
from tangent.grads import adjoint

def cube(x):
  return x * x * x
  
# Register the gradient of cube with Tangent
# NOTE! This is not a runnable function, but instead is a code template.
# Tangent will replace the names of the variables `result` and `x` with whatever
# is used in your containing function.
@adjoint(cube)
def dcube(result, x):
  d[x] = d[result] * 3 * x * x
  
def f(val):
    cubed_val = cube(val)
    return cubed_val

print(tangent.grad(f,verbose=1))

Should output something like:

def dfdval(val, bcubed_val=1.0):
    # Grad of: cubed_val = cube(val)
    bval = bcubed_val * 3 * (val * val) # <<<< this is our inlined gradient
    return bval

The signature for the custom gradient of some function

result = orig_function(arg1,arg2)

is

@adjoint(orig_function)
def grad_orig_function(result, arg1, arg2):
  d[arg1] = d[result]*...
  d[arg2] = d[result]*...

The first argument to the template is always the result of the function call, followed by the function arguments, in order. Tangent captures the variable names of the result and arguments, and then will use them to unquote the gradient template at the appropriate place in the backward pass.

Check out an example gradient definition of a NumPy function and of a TF eager function. Also, see the docstring in grads.py for more info.

Debugging

Because Tangent auto-generates derivative code you can read, you can also easily debug your backward pass. For instance, your NN might be outputting NaNs during training, and you want to find out where the NaNs are being generated in your model. Just insert a breakpoint (e.g., pdb.set_trace()) at the end of your forward pass.

SCT for Debugging

For large models, setting a breakpoint at the beginning of the backward pass and stepping through dozens of lines might be cumbersome. Instead, you might want the breakpoint to be placed later in the derivative calculation. Tangent lets you insert code directly into any location in the backward pass. First, run from tangent import insert_grad_of, then add a with insert_grad_of block containing the code you'd like to insert into the backward pass.

from tangent import insert_grad_of
def f(x):
  ...
  with insert_grad_of(x) as dx:
    print("dc/dx = %2.2f" % dx)
    pdb.set_trace()
  ...

Ad Hoc Gradient Code

Derivative Surgery

You can use the insert_grad_of feature to do more than debugging and logging. Some NN architectures benefit from tricks that directly manipulate the backward pass. For example, recurrent neural networks (RNNs) suffer from the "exploding gradient" problem, where gradients grow exponentially. This prevents the model from training properly. A typical solution is to force the derivatives inside of an RNN to not exceed a certain value by directly clipping them. We can implement this with insert_grad_of.

def f(params, x):
  h = x
  for i in range(5):
    with insert_grad_of(h) as g:
      g = tf.clip_by_value(g, -1, 1)
    h = rnn(params, h)
  return h

dfdparams = tangent.grad(f)

You can perform other backward-pass tricks with insert_grad_of, such as stop gradients (use a break in the inlined code to stop a for loop), or synthetic gradients (replace a derivative with a prediction from a neural network). This feature lets Tangent users easily debug their models, or quickly try out derivative tweaks in the backward pass.

Forward Mode

Reverse-mode autodiff, or backpropagation, generates efficient derivatives for the types of functions we use in machine learning, where there are usually many (perhaps millions) of input variables and only a single output (our loss). When the inverse is true, where there are many more outputs than inputs, reverse mode is not an efficient algorithm, as it has to be run as many times as there are output variables. However, a less famous algorithm, forward-mode autodiff, only has to be run as many times as there are input variables.). Tangent supports forward-mode autodiff.

def f(x):
  a = x * x
  b = x * a
  c = a + b
  return c

forward_df = tangent.autodiff(f, mode='forward')

SCT Forward Mode

Hessian-Vector Products

Although we won't dig into the technical details, forward-mode is very useful when combined with reverse-mode to calculate efficient higher-order derivatives, particularly for Hessian-vector products (HVP) of NNs. This is useful in research applications, and usually very painful and slow to calculate. Autograd has native forward-mode support, while TensorFlow has 3rd-party support.

To take higher-order derivatives, you can use any combination of forward- and reverse-mode autodiff in Tangent. This works because the code Tangent produces can also be fed back in as input. The autodiff literature recommends calculating HVPs in a "Forward-over-Reverse" style. This means first apply reverse mode autodiff to the function, and then apply forward mode to that.

def f(x):
    a = x * x * x
    b = a * x ** 2.0
    return tf.reduce_sum(b)

hvp = tangent.autodiff(tangent.autodiff(f,mode='reverse'),mode='forward')

Performance

Although we did not build Tangent for performance, it is competitive with major ML libraries. Because we are generating derivatives ahead-of-time, there is no interpretive overhead like there is with runtime autodiff libraries. We implemented a few compiler optimizations (dead code elimination, and constant folding), but we are still working on extra optimization passes to further increase performance.

Small Benchmark

Optimization

We are often interested in the gradients of only some of the arguments. In this case, many of the adjoint calculation might be dead code. In the optimization pass this is removed. We also perform limited constant folding and assignment propagation.

Known Limitations

Tangent is still an experiment, so expect some bugs. If you report them to us on GitHub, we will do our best to fix them quickly.

We are working to add support in Tangent for more aspects of the Python language (e.g., closures, inline function definitions, classes, more NumPy and TensorFlow functions). We also hope to add more advanced automatic differentiation and compiler functionality in the future, such as automatic trade-off between memory and compute (Griewank and Walther 2000; Gruslys et al., 2016), more aggressive optimizations, and lambda lifting.

Many of Python's advanced features are difficult to statically analyze or to define sensible gradients of, so we restrict Python to a functional subset (i.e. no mutable objects).

Closures

Closures are currently not supported for the following reasons:

  • AD relies on being able to resolve function names. If function names are resolved using the enclosing function namespace, we cannot be sure that they will resolve to the same function at each call.
  • Although we can access functions from the enclosing function namespace, we cannot write to this namespace, which is required for the gradients.

Classes

Classes are not currently supported, but are on our near-term roadmap. This will enable PyTorch/Chainer/TFEager-style class definitions of neural networks, and parameterized functions, like in TF Slim.

Team

Tangent is developed by Alex Wiltschko, Bart van Merrienboer and Dan Moldovan.

Comments
  • gradients are inconsistently vectorized

    gradients are inconsistently vectorized

    Sometimes gradients are vectorized, and sometimes they are not. Consider this example where the gradient is vectorized (ie. array in, array of derivatives out).

    def f(x):
        return x**2
    
    df = tangent.grad(f)
    
    print(df(np.array([0, 1, 2])))
    

    This comes out like I would expect.

    :RESULTS: [ 0. 2. 4.] :END:

    Compare it to this:

    def f1(x):
        return x + 2.0 * np.cos(x)
    # df/dx = 1 - 2*sin(x)
    
    df1 = tangent.grad(f1)
    
    x = np.array([0.0, 1.0, 2.0])
    print(df1(x)) # It is not clear this is even correct.
    print(1 - 2 * np.sin(x))
    
    # A vectorized version
    df1v = np.vectorize(tangent.grad(f1))
    print(df1v(np.array([0, 1, 2])))
    

    :RESULTS: -2.50153682327 [ 1. -0.68294197 -0.81859485] [ 1. -0.68294197 -0.81859485]

    :END:

    It is not clear that df1 even returns the right answer for the array.

    This seems important because an obvious thing one might want to do is pass the tangent.grad function to scipy.optimize.fsolve. But fsolve requires the fprime function to take array arguments, and return an array of derivatives.

    opened by jkitchin 9
  • Can't import

    Can't import "make_vjp"

    I pip installed tangent and can't get it to import. I think this has to do with upgrading tensorflow to 1.4 though not sure.

    Mac Os Sierra 10.12.6 Python 3.6 Tensorflow 1.4.0

    Running in Jupyter notebook (or ipython)

    ImportError                               Traceback (most recent call last)
    <ipython-input-1-b7bc666cce03> in <module>()
    ----> 1 import tangent
    
    /Users/rick.shapiro/anaconda/lib/python3.6/site-packages/tangent/__init__.py in <module>()
         18 import gast
         19 
    ---> 20 from tangent import annotate
         21 from tangent import ast as ast_
         22 from tangent import compile as compile_
    
    /Users/rick.shapiro/anaconda/lib/python3.6/site-packages/tangent/annotate.py in <module>()
         27 from tangent import cfg
         28 from tangent import quoting
    ---> 29 from tangent import tracing
         30 from tangent import utils
         31 
    
    /Users/rick.shapiro/anaconda/lib/python3.6/site-packages/tangent/tracing.py in <module>()
         14 """Utilities for tracing code, a useful fallback when ahead-of-time AD fails.
         15 """
    ---> 16 from tensorflow.python.eager.backprop import make_vjp
         17 
         18 
    
    ImportError: cannot import name 'make_vjp'
    
    opened by rshap91 7
  • UnicodeDecodeError: 'gbk' codec can't decode byte 0x9d in position 6304: illegal multibyte sequence

    UnicodeDecodeError: 'gbk' codec can't decode byte 0x9d in position 6304: illegal multibyte sequence

    Environment: Windows 7 Python: 3.6.2

    pip installation failed.

    Collecting tangent
      Using cached tangent-0.1.0.tar.gz
        Complete output from command python setup.py egg_info:
        Traceback (most recent call last):
          File "<string>", line 1, in <module>
          File "C:\Users\ADMINI~1\AppData\Local\Temp\pip-build-k2pei7vz\tangent\setu
    p.py", line 5, in <module>
            readme = f.read()
        UnicodeDecodeError: 'gbk' codec can't decode byte 0x9d in position 6304: ill
    egal multibyte sequence
    
        ----------------------------------------
    Command "python setup.py egg_info" failed with error code 1 in C:\Users\ADMINI~1
    \AppData\Local\Temp\pip-build-k2pei7vz\tangent\
    
    opened by Watesoyan 7
  • The newly released tf-nightly is incompatible with 'gast=0.2.2'

    The newly released tf-nightly is incompatible with 'gast=0.2.2'

    In the environment.yml, it require that 'tf-nightly == 1.5.0.dev20171026'. The tf-nightly 1.x is no longer available. And the newest released tf-nightly 2.2.0 is only compatible with 'gast==0.3.3', which gives rise to the problem of #97 . But the solution 'pinning the gast < 0.3.0' disables tf-nightly 2.2.0.

    I tried install the gast 0.2.2, and used tensorflow 2.1.0 instead of tf-nightly 2.2.0. But it then occured the problem #95 and then #99 , which causes failure while importing tangent.

    Thus I wonder if there is any simpler and executable way to solve this problem.

    opened by Sijie-L 6
  • Error when importing Tangent - Failed to load the native TensorFlow runtime.

    Error when importing Tangent - Failed to load the native TensorFlow runtime.

    Problem

    I get the following error when running import tangent in Python 3.6:

    In [1]: import tangent
    ---------------------------------------------------------------------------
    ImportError                               Traceback (most recent call last)
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow.py in <module>()
         57
    ---> 58   from tensorflow.python.pywrap_tensorflow_internal import *
         59   from tensorflow.python.pywrap_tensorflow_internal import __version__
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow_internal.py in <module>()
         27             return _mod
    ---> 28     _pywrap_tensorflow_internal = swig_import_helper()
         29     del swig_import_helper
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow_internal.py in swig_import_helper()
         23             try:
    ---> 24                 _mod = imp.load_module('_pywrap_tensorflow_internal', fp, pathname, description)
         25             finally:
    
    ~/.conda/envs/py36/lib/python3.6/imp.py in load_module(name, file, filename, details)
        242         else:
    --> 243             return load_dynamic(name, filename, file)
        244     elif type_ == PKG_DIRECTORY:
    
    ~/.conda/envs/py36/lib/python3.6/imp.py in load_dynamic(name, path, file)
        342             name=name, loader=loader, origin=path)
    --> 343         return _load(spec)
        344
    
    ImportError: /usr/lib64/libstdc++.so.6: version `CXXABI_1.3.7' not found (required by /home/pauperei/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)
    
    During handling of the above exception, another exception occurred:
    
    ImportError                               Traceback (most recent call last)
    <ipython-input-1-b7bc666cce03> in <module>()
    ----> 1 import tangent
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tangent/__init__.py in <module>()
         18 import gast
         19
    ---> 20 from tangent import annotate
         21 from tangent import ast as ast_
         22 from tangent import compile as compile_
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tangent/annotate.py in <module>()
         27 from tangent import cfg
         28 from tangent import quoting
    ---> 29 from tangent import tracing
         30 from tangent import utils
         31
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tangent/tracing.py in <module>()
         14 """Utilities for tracing code, a useful fallback when ahead-of-time AD fails.
         15 """
    ---> 16 from tensorflow.python.eager.backprop import make_vjp
         17
         18
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/__init__.py in <module>()
         22
         23 # pylint: disable=wildcard-import
    ---> 24 from tensorflow.python import *  # pylint: disable=redefined-builtin
         25 # pylint: enable=wildcard-import
         26
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/__init__.py in <module>()
         47 import numpy as np
         48
    ---> 49 from tensorflow.python import pywrap_tensorflow
         50
         51 # Protocol buffers
    
    ~/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow.py in <module>()
         72 for some common reasons and solutions.  Include the entire stack trace
         73 above this error message when asking for help.""" % traceback.format_exc()
    ---> 74   raise ImportError(msg)
         75
         76 # pylint: enable=wildcard-import,g-import-not-at-top,unused-import,line-too-long
    
    ImportError: Traceback (most recent call last):
      File "/home/pauperei/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow.py", line 58, in <module>
        from tensorflow.python.pywrap_tensorflow_internal import *
      File "/home/pauperei/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow_internal.py", line 28, in <module>
        _pywrap_tensorflow_internal = swig_import_helper()
      File "/home/pauperei/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/pywrap_tensorflow_internal.py", line 24, in swig_import_helper
        _mod = imp.load_module('_pywrap_tensorflow_internal', fp, pathname, description)
      File "/home/pauperei/.conda/envs/py36/lib/python3.6/imp.py", line 243, in load_module
        return load_dynamic(name, filename, file)
      File "/home/pauperei/.conda/envs/py36/lib/python3.6/imp.py", line 343, in load_dynamic
        return _load(spec)
    ImportError: /usr/lib64/libstdc++.so.6: version `CXXABI_1.3.7' not found (required by /home/pauperei/.conda/envs/py36/lib/python3.6/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)
    
    
    Failed to load the native TensorFlow runtime.
    
    See https://www.tensorflow.org/install/install_sources#common_installation_problems
    
    for some common reasons and solutions.  Include the entire stack trace
    above this error message when asking for help.
    

    System information:

    Red Hat Enterprise Linux Server release 5.3 (Tikanga)
    Amazon Linux Bare Metal release 2012.03
    
    opened by paupereira 6
  • python3: pip install fails with UnicodeDecodeError

    python3: pip install fails with UnicodeDecodeError

    this seems to be related to issue #13 but I tried it Dec/1 and got the following error:

    Collecting tangent
      Downloading tangent-0.1.8.tar.gz (81kB)
        100% |################################| 81kB 1.9MB/s 
        Complete output from command python setup.py egg_info:
        Traceback (most recent call last):
          File "<string>", line 1, in <module>
          File "/tmp/pip-build-bifyt5f0/tangent/setup.py", line 5, in <module>
            readme = f.read()
          File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode
            return codecs.ascii_decode(input, self.errors)[0]
        UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 498: ordinal not in range(128)
    

    Note this was an attempt to install under python 3.5.2 (standard ubuntu 16 package). In contrast it seems like the python2.7 install does work OK.

    opened by utke1 6
  • AttributeError: module 'builtins' has no attribute 't'

    AttributeError: module 'builtins' has no attribute 't'

    Getting AttributeError: module 'builtins' has no attribute 't' when running the Hessian-vector products example:

    def f(x):
        a = x * x * x
        b = a * x ** 2.0
        return tf.reduce_sum(b)
    
    hvp = tangent.grad(tangent.grad(f, mode='reverse'), mode='forward')
    
    opened by amirziai 6
  • Incorrect gradient calculation

    Incorrect gradient calculation

    Hi, I've been experimenting with this library for a few months now and really like the capabilities present. I'm working to develop an AD capability via code generation for a set of aerospace engineering codes in Python.

    However, I think I've run into either a bug or a usage misunderstanding. Consider the following stand-alone function, which takes several parameters and returns a scalar:

    import tangent
    
    BTU_s2HP, HP_per_RPM_to_FT_LBF = 1.4148532, 5252.11
    
    def enthalpyandpower(W_in, W_out, ht_in, ht_out_ideal, eff, Nmech, b1_W, b1_ht, b1_ht_ideal):
    
        ht_out = W_in/W_out * (ht_in * (1.0 - eff) + ht_out_ideal * eff)
        power = W_in * eff * (ht_in - ht_out_ideal) * BTU_s2HP
    
    
        ht_out += b1_W / W_out * \
            (b1_ht * (1.0 - eff) +
             b1_ht_ideal * eff)
        power += b1_W * eff * \
            (b1_ht - b1_ht_ideal) * BTU_s2HP
    
        # calculate torque based on revised power and shaft speed
        trq = power / \
            Nmech * HP_per_RPM_to_FT_LBF
    
        return power
    

    If I generate the partial derivative of power with respect to the first parameter W_in

    dpower_dwin = tangent.autodiff(enthalpyandpower, wrt=(0,), verbose=1)
    

    then I get:

    def denthalpyandpowerdW_in(W_in, W_out, ht_in, ht_out_ideal, eff, Nmech,
        b1_W, b1_ht, b1_ht_ideal, bpower):
        # Initialize the tape
        _stack = tangent.Stack()
        _ht_out3 = ht_out_ideal * eff
        _1_0_minus_eff = 1.0 - eff
        _ht_out2 = ht_in * _1_0_minus_eff
        _ht_out = _ht_out2 + _ht_out3
        W_in_over_W_out = W_in / W_out
        ht_out = W_in_over_W_out * _ht_out
        _power2 = ht_in - ht_out_ideal
        W_in_times_eff = W_in * eff
        _power = W_in_times_eff * _power2
        power = _power * BTU_s2HP
        tangent.push(_stack, ht_out, '_1c132dd6')
        _3285 = b1_ht - b1_ht_ideal
        b1_W_times_eff = b1_W * eff
        _3244 = b1_W_times_eff * _3285
        _eb3b = _3244 * BTU_s2HP
        tangent.push(_stack, power, '_b65b4e60')
        power = power + _eb3b
        assert tangent.shapes_match(power, bpower
            ), 'Shape mismatch between return value (%s) and seed derivative (%s)' % (
            numpy.shape(power), numpy.shape(bpower))
        power = tangent.pop(_stack, '_b65b4e60')
        bpower = tangent.init_grad(power, allow_lazy_initializer=True)
        ht_out = tangent.pop(_stack, '_1c132dd6')
        bht_out = tangent.init_grad(ht_out, allow_lazy_initializer=True)
    
        # Grad of: power = W_in * eff * (ht_in - ht_out_ideal) * BTU_s2HP
        _b_power = tangent.unbroadcast(bpower * BTU_s2HP, _power)
        b_power = _b_power
        _3f78 = tangent.unbroadcast(b_power * _power2, W_in_times_eff)
        bW_in_times_eff = _3f78
        _bW_in2 = tangent.unbroadcast(bW_in_times_eff * eff, W_in)
        bW_in = _bW_in2
    
        # Grad of: ht_out = W_in / W_out * (ht_in * (1.0 - eff) + ht_out_ideal * eff)
        _a32f = tangent.unbroadcast(bht_out * _ht_out, W_in_over_W_out)
        _9f3c = _a32f
        _bW_in = _9f3c / W_out
        bW_in = tangent.add_grad(bW_in, _bW_in)
        return bW_in
    

    Running this seems to give me a partial derivative of 0.0 regardless of the evaluation point. However, the partial is certainly non-zero, e.g. at (30., 30., 10., 9.5, 0.95, 1000., 1000., 1000., 999.) it would be about 0.67206, but instead

    x = denthalpyandpowerdW_in(30., 30., 10., 9.5, 0.95, 1000., 1000., 1000., 999., 1.0)
    print(x)
    

    returns 0.0.

    The correct derivative of can be found analytically with some work, or confirmed roughly by finite difference:

    x0 = enthalpyandpower(30., 30., 10., 9.5, 0.95, 1000., 1000., 1000., 999.)
    x1 = enthalpyandpower(30.001, 30., 10., 9.5, 0.95, 1000., 1000., 1000., 999.)
    
    print((x1 - x0) / (0.001))
    

    Have I made a user error, or is this an unexpected bug? Thanks!

    opened by thearn 5
  • extra edges in control flow graph after

    extra edges in control flow graph after "return"

    Issue: The control flow graph that tangent builds sometimes has extra edges following a "return" statement.

    Example:

    def fn3(self):  # arguments
        if 2 > 1:  # compare
          return 1  # return1
        return 2  # return2
    

    The cfg produced by build_cfg has the following edges: arguments->compare compare->return1 compare->return2 return1->return2 # this is the extra edge.

    opened by dbieber 5
  • Unexpected result in LogSumExp gradient using Tangent package in Python

    Unexpected result in LogSumExp gradient using Tangent package in Python

    Problem:

    • First implementation:

    I'm trying to get Tangent to compute the gradient of a function that contains the following implementation of logsumexp:

    import numpy as np
    import tangent
    
    def logsumexp(a):
        # a = a.reshape(-1)
        result = 0.0
        largest_in_a = a[0]
        a_shape = len(a)
    
        # numba is slow when using max or np.max, so re-implementing:
        for i in range(1, a_shape):
            if a[i] > largest_in_a:
                largest_in_a = a[i]
    
        for i in range(a_shape):
            result += np.exp(a[i] - largest_in_a)
    
        return np.log(result) + largest_in_a
    

    I call tangent as follows:

    x = np.array([1,2,3,4])
    grad_logsumexp = tangent.grad(logsumexp)
    

    And get the result

    grad_logsumexp(x)
    Out[100]: array([0, 0, 0, 0])
    

    While the correct answer is

    array([0.0320586 , 0.08714432, 0.23688282, 0.64391426])
    
    • Second implementation:

    On the other hand, doing this works:

    def logsumexp_naive(a):
            return np.log(np.sum(np.exp(a)))
    
    grad_logsumexp_naive = tangent.grad(logsumexp_naive)
    grad_logsumexp_naive(x)
    

    Question:

    What's going on with the first implementation?

    opened by paupereira 4
  • Update grads.py

    Update grads.py

    Added the derivatives of the following functions: np.tan() np.arccos() np.arcsin() np.arctan()

    Added tests for the following functions: np.cos() np.sin() np.tan() np.cosh() np.sinh() np.tanh() np.arccos() np.arcsin() np.arctan()

    Changed the position of the (cos, sin, tan) functions, such that they are correctly ordered.

    opened by RikHendriks 4
  • Error in tangent tutorial notebook:

    Error in tangent tutorial notebook: "AttributeError: module 'gast' has no attribute 'Num'"

    Hi i opened your tutorial notebook and run the cells in notebook. after installing tangent at cell with following code:

    import tangent
    df = tangent.grad(f)
    

    the following error arises:

    AttributeError Traceback (most recent call last) in () ----> 1 import tangent 2 df = tangent.grad(f)

    3 frames /usr/local/lib/python3.6/dist-packages/tangent/grammar.py in () 16 import gast 17 ---> 18 LITERALS = (gast.Num, gast.Str, gast.Bytes, gast.Ellipsis, gast.NameConstant) 19 20 CONTROL_FLOW = (gast.For, gast.AsyncFor, gast.While, gast.If, gast.Try)

    AttributeError: module 'gast' has no attribute 'Num'

    opened by dariush-bahrami 6
  • docs: fix simple typo, subtituted -> substituted

    docs: fix simple typo, subtituted -> substituted

    There is a small typo in tangent/naming.py.

    Should read substituted rather than subtituted.

    Semi-automated pull request generated by https://github.com/timgates42/meticulous/blob/master/docs/NOTE.md

    opened by timgates42 0
  • Python 3.8 compatibility (gast >= 0.3.0 has breaking changes to API)

    Python 3.8 compatibility (gast >= 0.3.0 has breaking changes to API)

    As referenced in the 3.8 documentation, the ast.Num, ast.Str, etc. classes are deprecated and being removed in future versions.

    To support this (?), the gast library has changed to replace these with gast.Constant instead. (I.e., a gast.Num would be gast.Constant(value=1, kind=None) instead of gast.Num(n=1)).

    Unfortunately, this breaks tangent:

    >>> import tangent
    Traceback (most recent call last):                                                                                        
      File "<stdin>", line 1, in <module>
      File "/home/ssimmons/tangent/tangent/__init__.py", line 20, in <module>
        from tangent import annotate 
      File "/home/ssimmons/tangent/tangent/annotate.py", line 27, in <module>
        from tangent import cfg
      File "/home/ssimmons/tangent/tangent/cfg.py", line 29, in <module>
        from tangent import grammar
      File "/home/ssimmons/tangent/tangent/grammar.py", line 18, in <module>
        LITERALS = (gast.Num, gast.Str, gast.Bytes, gast.Ellipsis, gast.NameConstant)
    AttributeError: module 'gast' has no attribute 'Num'
    

    So I think that we need to restrict to the old API (and use Python <3.8) or adapt to the new API.

    opened by singularperturbation 0
  • AttributeError: module 'tensorflow' has no attribute 'to_float'

    AttributeError: module 'tensorflow' has no attribute 'to_float'

    I'm using TF 2.0, and I get this error when I import tangent, due to a list of non-differentiable functions that includes tf.to_float (line 60), which is deprecated:

    https://www.tensorflow.org/versions/r1.14/api_docs/python/tf/to_float

    help wanted good first issue 
    opened by ziofil 9
Releases(v0.1.8)
Owner
Google
Google ❤️ Open Source
Google
aka "Bayesian Methods for Hackers": An introduction to Bayesian methods + probabilistic programming with a computation/understanding-first, mathematics-second point of view. All in pure Python ;)

Bayesian Methods for Hackers Using Python and PyMC The Bayesian method is the natural approach to inference, yet it is hidden from readers behind chap

Cameron Davidson-Pilon 25.1k Jan 2, 2023
Implementing Graph Convolutional Networks and Information Retrieval Mechanisms using pure Python and NumPy

Implementing Graph Convolutional Networks and Information Retrieval Mechanisms using pure Python and NumPy

Noah Getz 3 Jun 22, 2022
Pure python PEMDAS expression solver without using built-in eval function

pypemdas Pure python PEMDAS expression solver without using built-in eval function. Supports nested parenthesis. Supported operators: + - * / ^ Exampl

null 1 Dec 22, 2021
A simplistic and efficient pure-python neural network library from Phys Whiz with CPU and GPU support.

A simplistic and efficient pure-python neural network library from Phys Whiz with CPU and GPU support.

Manas Sharma 19 Feb 28, 2022
Viperdb - A tiny log-structured key-value database written in pure Python

ViperDB ?? ViperDB is a lightweight embedded key-value store written in pure Pyt

null 17 Oct 17, 2022
A modern pure-Python library for reading PDF files

pdf A modern pure-Python library for reading PDF files. The goal is to have a modern interface to handle PDF files which is consistent with itself and

null 6 Apr 6, 2022
Implementation of TimeSformer, a pure attention-based solution for video classification

TimeSformer - Pytorch Implementation of TimeSformer, a pure and simple attention-based solution for reaching SOTA on video classification.

Phil Wang 602 Jan 3, 2023
PURE: End-to-End Relation Extraction

PURE: End-to-End Relation Extraction This repository contains (PyTorch) code and pre-trained models for PURE (the Princeton University Relation Extrac

Princeton Natural Language Processing 657 Jan 9, 2023
Implementation of STAM (Space Time Attention Model), a pure and simple attention model that reaches SOTA for video classification

STAM - Pytorch Implementation of STAM (Space Time Attention Model), yet another pure and simple SOTA attention model that bests all previous models in

Phil Wang 109 Dec 28, 2022
The codes for the work "Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation"

Swin-Unet The codes for the work "Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation"(https://arxiv.org/abs/2105.05537). A validatio

null 869 Jan 7, 2023
Code release for our paper, "SimNet: Enabling Robust Unknown Object Manipulation from Pure Synthetic Data via Stereo"

SimNet: Enabling Robust Unknown Object Manipulation from Pure Synthetic Data via Stereo Thomas Kollar, Michael Laskey, Kevin Stone, Brijen Thananjeyan

null 68 Dec 14, 2022
Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data

Real-ESRGAN Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data Ported from https://github.com/xinntao/Real-ESRGAN Depend

Holy Wu 44 Dec 27, 2022
The pure and clear PyTorch Distributed Training Framework.

The pure and clear PyTorch Distributed Training Framework. Introduction Requirements and Usage Dependency Dataset Basic Usage Slurm Cluster Usage Base

WILL LEE 208 Dec 20, 2022
MLP-Numpy - A simple modular implementation of Multi Layer Perceptron in pure Numpy.

MLP-Numpy A simple modular implementation of Multi Layer Perceptron in pure Numpy. I used the Iris dataset from scikit-learn library for the experimen

Soroush Omranpour 1 Jan 1, 2022
A pure PyTorch implementation of the loss described in "Online Segment to Segment Neural Transduction"

ssnt-loss ℹ️ This is a WIP project. the implementation is still being tested. A pure PyTorch implementation of the loss described in "Online Segment t

張致強 1 Feb 9, 2022
A pure PyTorch batched computation implementation of "CIF: Continuous Integrate-and-Fire for End-to-End Speech Recognition"

A pure PyTorch batched computation implementation of "CIF: Continuous Integrate-and-Fire for End-to-End Speech Recognition"

張致強 14 Dec 2, 2022
HashNeRF-pytorch - Pure PyTorch Implementation of NVIDIA paper on Instant Training of Neural Graphics primitives

HashNeRF-pytorch Instant-NGP recently introduced a Multi-resolution Hash Encodin

Yash Sanjay Bhalgat 616 Jan 6, 2023
PaddleRobotics is an open-source algorithm library for robots based on Paddle, including open-source parts such as human-robot interaction, complex motion control, environment perception, SLAM positioning, and navigation.

简体中文 | English PaddleRobotics paddleRobotics是基于paddle的机器人开源算法库集,包括人机交互、复杂运动控制、环境感知、slam定位导航等开源算法部分。 人机交互 主动多模交互技术TFVT-HRI 主动多模交互技术是通过视觉、语音、触摸传感器等输入机器人

null 185 Dec 26, 2022
Empirical Study of Transformers for Source Code & A Simple Approach for Handling Out-of-Vocabulary Identifiers in Deep Learning for Source Code

Transformers for variable misuse, function naming and code completion tasks The official PyTorch implementation of: Empirical Study of Transformers fo

Bayesian Methods Research Group 56 Nov 15, 2022