Supported subset of Python

The supported subset of Python covers functions, inner functions, and lambda functions, which can have parameters, parameters with default values, a positional wildcard or a keyword wildcard parameter (cf. function definition) and which can be called with any combination of positional arguments, keyword arguments, and starred expressions as Python allows.

The compound statements for, while, if, try, and with are supported, as of course are also function definitions.

Import statements are somewhat easier to handle as they are not generally processed, except the from module import name statements. These are are currently processed once when a module is first loaded by pyadi, unconditionally, meaning using from module import name within another statement (like if or try) may or not work, because pyadi makes no attempt to find out what actually happened when the module was loaded by Python and whether or not the import was effective. Plain import statements need not to be processed because pyadi finds the module from the function that is being differentiated.

The data types Dictionary displays, tuple(), and dict() are supported as literals well as they are in the form of list comprehensions and dict comprehensions, also known as generator expressions.

Object oriented programming with classes is supported, including inheritance, method calling, bound methods, and the super() function. Object methods including the constructor are differentiated. Objects can also define a hidden __call__ method, which is also differentiated when the object is called.

Iterators including user-defined iterators are supported but they (i.e. the hidden methods __iter__ and __next__) are not differentiated. However, a “derivative” iterator object will automatically be created for each iterator object that the code uses, and its constructor and other methods being called regularly will be differentiated, so as long as the iterations just shuffe data around, the derivative should also be correct.

Generator functions and the yield statment are also supported. Here, the generator function and the yielded expressions are differentiated entirely.

Formatted strings are differentiated, and the rule for print() prints the differentiated arguments, so the differentiated programs will print lines with the values of differentiated expressions, in addition to the original line.

Several of the most important functions are already supported, be it because they are available in source of because they have been added to the list of builtin rules in forwardad. The latter must happen for any function that cannot or shall not be differentiated in source. It is a work in progress to cover more and more builtin functions. When a function is not covered by a rule and the source code cannot be obtained, DiffFor() will raise NoSource. Users can use setrule() to dynamically add rules at runtime to avoid this scenario. This can also be used to install custom derivatives for any function.

Limitations

There are several known limitations:

  • Multiple decorators are untested

  • Coroutines, async and await are not supported

  • Iterator functions __next__ and __iter__ are not differentiated so they work correcty when data is copied, but not when float computations are performed inside. Generators using yield should be used in this case, for example

  • Lambda functions cannot by passed to DiffFor() directly, they must be wrapped in a regular function

  • List multiplication with * is not supported, a list comprehension with generator expression should be used instead. List addition with + is supported.

  • There is no activity analysis

  • There is no vector mode, or derivative class as in ADiMat

  • There is no reverse mode