Package: algovision
AlgoVision - A Framework for Differentiable Algorithms and Algorithmic SupervisionThis repository includes the official implementation of our NeurIPS 2021 Paper "Learning with Algorithmic Supervision via Continuous Relaxations"
(Paper @ArXiv,
Video @Youtube).algovisionis a Python 3.6+ and PyTorch 1.9.0+ based library for making algorithms differentiable. It can be installed via:pipinstallalgovisionApplications include smoothly integrating algorithms into neural networks for algorithmic supervision, problem-specific optimization within an algorithm, and whatever your imagination allows.
Asalgovisionrelies on PyTorch it also supports CUDA, etc.Check out the Documentation!🌱 IntroDeriving a loss from a smooth algorithm can be as easy asfromexamplesimportget_bubble_sortimporttorch# Get an array (the first dimension is the batch dimension, which is always required)array=torch.randn(1,8,requires_grad=True)bubble_sort=get_bubble_sort(beta=5)result,loss=bubble_sort(array)loss.backward()print(array)print(result)print(array.grad)Here, the loss is a sorting loss corresponding to the number of swaps in the bubble sort algorithm.
But we can also define this algorithm from scratch:fromalgovisionimport(Algorithm,Input,Output,Var,VarInt,# coreLet,LetInt,Print,# instructionsEq,NEq,LT,LEq,GT,GEq,CatProbEq,CosineSimilarity,IsTrue,IsFalse,# conditionsIf,While,For,# control_structuresMin,ArgMin,Max,ArgMax,# functions)importtorchbubble_sort=Algorithm(# Define the variables the input corresponds toInput('array'),# Declare and initialize all differentiable variablesVar('a',torch.tensor(0.)),Var('b',torch.tensor(0.)),Var('swapped',torch.tensor(1.)),Var('loss',torch.tensor(0.)),# Declare and initialize a hard integer variable (VarInt) for the control flow.# It can be defined in terms of a lambda expression. The required variables# are automatically inferred from the signature of the lambda expression.VarInt('n',lambdaarray:array.shape[1]-1),# Start a relaxed While loop:While(IsTrue('swapped'),# Set `swapped` to 0 / FalseLet('swapped',0),# Start an unrolled For loop. Corresponds to `for i in range(n):`For('i','n',# Set `a` to the `i`th element of `array`Let('a','array',['i']),# Using an inplace lambda expression, we can include computations# based on variables to obtain the element at position i+1.Let('b','array',[lambdai:i+1]),# An If-Else statement with the condition a > bIf(GT('a','b'),if_true=[# Set the i+1 th element of array to aLet('array',[lambdai:i+1],'a'),# Set the i th element of array to bLet('array',['i'],'b'),# Set swapped to 1 / TrueLet('swapped',1.),# Increment the loss by 1 using a lambda expressionLet('loss',lambdaloss:loss+1.),]),),# Decrement the hard integer variable n by 1LetInt('n',lambdan:n-1),),# Define what the algorithm should returnOutput('array'),Output('loss'),# Set the inverse temperature betabeta=5,)👾 Full Instruction Set(click to expand)The full set of modules is:fromalgovisionimport(Algorithm,Input,Output,Var,VarInt,# coreLet,LetInt,Print,# instructionsEq,NEq,LT,LEq,GT,GEq,CatProbEq,CosineSimilarity,IsTrue,IsFalse,# conditionsIf,While,For,# control_structuresMin,ArgMin,Max,ArgMax,# functions)Algorithmis the main class,InputandOutputdefine arguments and return values,Vardefines differentiable variables andVarIntdefines non-differentiable integer variables.Eq,LT, etc. are relaxed conditions forIfandWhile, which are respective control structures.Forbounded loops of fixed length that are unrolled.Letsets a differentiable variable,LetIntsets a hard integer variable.
Note that hard integer variables should only be used if they are independent of the input values, but they may depend on the input shape (e.g., for reducing the number of iterations after each traversal of a For loop).Printprints for debug purposes.Min,ArgMin,Max, andArgMaxreturn the element-wise min/max/argmin/argmax of a list of tensors (of equal shape).λ Lambda ExpressionsKey to defining an algorithm arelambdaexpressions (seeherefor a reference).
They allow defining anonymous functions and therefore allow expressing computations in-place.
In most cases inalgovision, it is possible to write a value in terms of a lambda expressions.
The name of the used variable will be inferred from the signature of the expression.
For example,lambda x: x**2will take the variable namedxand return the square of it at the location where the expression is written.Let('z', lambda x, y: x**2 + y)corresponds to the regular line of codez = x**2 + y.
This also allows inserting complex external functions including neural networks as part of the lambda expression.
Assumingnetis a neural networks, one can writeLet('y', lambda x: net(x))(corresponding toy = net(x)).LetLetis a very flexible instruction. The following table shows the use cases of it.AlgoVisionPythonDescriptionLet('a', 'x')a = xVariableais set to the value of variablex.Let('a', lambda x: x**2)a = x**2As soon as we compute anything on the right hand side of the equation, we need to write it as alambdaexpression.Let('a', 'array', ['i'])a = array[i]Indexing on the right hand requires an additional list parameter after the second argument.Let('a', lambda array, i: array[:, i])a = array[i]Equivalent to the row above: indexing can also be manually done inside of alambdaexpression. Note that in this case, the batch dimension has to be written explicitly.Let('a', 'array', ['i', lambda j: j+1])a = array[i, j+1]Multiple indices andlambdaexpressions are also supported.Let('a', 'array', [None, slice(0, None, 2)])a = array[:, 0::2]Noneandslices are also supported.Let('a', ['i'], 'x')a[i] = xIndexing can also be done on the left hand side of the equation.Let('a', ['i'], 'x', ['j'])a[i] = x['j']...or on both sides.Let(['a', 'b'], lamba x, y: (x+y, x-y))a, b = x+y, x-yMultiple return values are supported.In its most simple formLetobtains two arguments, a string naming the variable where the result is written, and the value that may be expressed via alambdaexpression.If the lambda expression returns multiple values, e.g., because a complex function is called and has two return values, the left argument can be a list of strings.
That is,Let(['a', 'b'], lamba x, y: (x+y, x-y))corresponds toa, b = x+y, x-y.Letalso supports indexing. This is denoted by an additional list argument after the left and/or the right argument.
For example,Let('a', 'array', ['i'])corresponds toa = array[i], whileLet('array', ['i'], 'b')corresponds toarray[i] = b.Let('array', ['i'], 'array', ['j'])corresponding toarray[i] = array[j]is also supported.Note that indexing can also be expressed throughlambdaexpressions.
For example,Let('a', 'array', ['i'])is equivalent toLet('a', lambda array, i: array[:, i]). Note how in this case the batch dimension has to be explicitly taken into account ([:, ]).
Relaxed indexing on the right-hand side is only supported throughlambdaexpressions due to its complexity.
Relaxed indexing on the left-hand side is supported if exactly one probability weight tensor is in the list (e.g.,Let('array', [lambda x: get_weights(x)], 'a')).LetIntonly supports setting the variable to an integer (Pythonint) or list of integers (as well as the same type via lambda expressions).
Note that hard integer variables should only be used if they are independent of the input values, but they may depend on the input shape.If you need help implementing your differentiable algorithm, you mayschedule an appointment. This will also help me improve the documentation and usability.🧪 ExperimentsThe experiments can be found in theexperimentsfolder. Additional experiments will be added soon.🔬 Sorting SupervisionThe sorting supervision experiment can be run withpythonexperiments/train_sort.pyor by checking outthis Colab notebook.📖 CitingIf you used our library, please cite it as@inproceedings{petersen2021learning,title={{Learning with Algorithmic Supervision via Continuous Relaxations}},author={Petersen, Felix and Borgelt, Christian and Kuehne, Hilde and Deussen, Oliver},booktitle={Conference on Neural Information Processing Systems (NeurIPS)},year={2021}}📜 Licensealgovisionis released under the MIT license. SeeLICENSEfor additional details.