Skip to content

Commit

Permalink
Some fixes + slide on make_thunk.
Browse files Browse the repository at this point in the history
  • Loading branch information
abergeron committed Oct 28, 2014
1 parent 4225132 commit 6a73fdb
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 63 deletions.
27 changes: 18 additions & 9 deletions advanced.tex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
keywordstyle=\color{darkgreen}\bfseries,
commentstyle=\color{greenblue}\itshape,
stringstyle=\color{violet},
showstringspaces=false,
tabsize=4,
backgroundcolor=\color{lightgray},
frame=single,
Expand Down Expand Up @@ -191,7 +192,7 @@ \section{How to Make an Op (Python)}
\begin{frame}{Example}
\lstinputlisting[firstline=19,lastline=21]{doubleop.py}
\begin{itemize}
\item Here since the operation is simple the gardent is simple
\item Here since the operation is simple the gradient is simple
\item Note that we return a list
\end{itemize}
\end{frame}
Expand Down Expand Up @@ -257,8 +258,10 @@ \section{How to Make an Op (C)}
\end{frame}

\begin{frame}[allowframebreaks]{Example}
\vskip5mm
This is the C code equivalent to \code{perform}
\lstinputlisting[linerange={30-58}]{doubleop.py}
\vskip4mm
\lstinputlisting[linerange={1-27}]{doublec.py}
\end{frame}

\begin{frame}{Exercice: DoubleC}
Expand Down Expand Up @@ -288,9 +291,8 @@ \section{How to Make an Op (C)}
\end{frame}

\begin{frame}{COp: Example}
\only<1>{This is the same C code as before, using the COp style.}
\only<1>{\lstinputlisting[linerange={60-71}]{doubleop.py}}
\only<2>{\lstinputlisting[language=C,basicstyle=\fontfamily{pcr}\selectfont\scriptsize]{doublecop.c}}
\only<1>{\lstinputlisting[linerange={1-16}]{doublecop.py}}
\only<2>{\lstinputlisting[language=C]{doublecop.c}}
\end{frame}

\begin{frame}{Tests}
Expand All @@ -300,6 +302,7 @@ \section{How to Make an Op (C)}
\begin{itemize}
\item You should skip the test in those cases
\end{itemize}
\item Using DebugMode will compare the output of the Python version to the output of the C version and raise an error if they don't match
\end{itemize}
\end{frame}

Expand All @@ -324,17 +327,23 @@ \section{How to Make an Op (C)}
\section{How to make a complex Op}

\begin{frame}{\code{make_thunk}}
\color{red} description of the make\_thunk interface
\lstinputlisting[linerange={12-14}]{thunk.py}
\begin{itemize}
\item Define instead of \code{perform} or \code{c_code}
\item Gives total freedom on how the computation is performed
\item More complex to use and generally not needed
\end{itemize}
\end{frame}

\begin{frame}{Example}
\color{red}Once again with ScalMulOp but with a make thunk that passes the scalar value to a helper function
\end{frame}
%\begin{frame}{Example}
%\color{red}Once again with ScalMulOp but with a make thunk that passes the scalar value to a helper function
%\end{frame}

\section{Optimizations}
% How to integrate your op automatically

\begin{frame}{Replace an Existing Op}
% use theano.gof.opt.OpSub
\color{red} Replace ScalMulOp(2) with DoubleOp
\end{frame}

Expand Down
33 changes: 33 additions & 0 deletions doublec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from theano import Op, Apply
from theano.tensor import as_tensor_variable

class DoubleC(Op):
__props__ = ()

def make_node(self, x):
x = as_tensor_variable(x)
if x.ndim != 1:
raise TypeError("DoubleC only works on 1D")
return Apply(self, [x], [x.type()])

def c_code(self, node, name, input_names,
output_names, sub):
return """
Py_XDECREF(%(out)s);
%(out)s = (PyArrayObject *)PyArray_NewLikeArray(
%(inp)s, NPY_ANYORDER, NULL, 0);
if (%(out)s == NULL) {
%(fail)s
}
for (npy_intp i = 0; i < PyArray_DIM(%(inp)s, 0); i++) {
*(dtype_%(out)s *)PyArray_GETPTR1(%(out)s, i) =
(*(dtype_%(inp)s *)PyArray_GETPTR1(%(inp)s, i)) * 2;
}
""" % dict(inp=input_names[0], out=output_names[0],
fail=sub["fail"])

def infer_shape(self, node, input_shapes):
return input_shapes

def grad(self, inputs, output_grads):
return [output_grads[0] * 2]
12 changes: 4 additions & 8 deletions doublecop.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ int APPLY_SPECIFIC(doublecop)(PyArrayObject *x,
if (*out == NULL)
return -1;

for (PyObject *iter_in = PyArray_IterNew(inp),
PyObject *iter_out = PyArray_IterNew(*out);
PyArray_ITER_NOTDONE(iter_in) &&
PyArray_ITER_NOTDONE(iter_out);
PyArray_ITER_NEXT(iter_in),
PyArray_ITER_NEXT(iter_out)) {
*(DTYPE_OUTPUT_0 *)PyArray_ITER_DATA(iter_out) =
(*(DTYPE_INPUT_0 *)PyArray_ITER_DATA(iter_in)) * 2;
for (npy_intp i = 0; i < PyArray_DIM(x, 0); i++) {
*(DTYPE_OUTPUT_0 *)PyArray_GETPTR1(*out, i) =
(*(DTYPE_INPUT_0 *)PyArray_GETPTR1(x, i)) * 2;
}
return 0;
}
22 changes: 22 additions & 0 deletions doublecop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from theano import Apply
from theano.gof import COp
from theano.tensor import as_tensor_variable

class DoubleCOp(COp):
__props__ = ()

def __init__(self):
COp.__init__(self, "./doublecop.c",
"APPLY_SPECIFIC(doublecop)")

def make_node(self, x):
x = as_tensor_variable(x)
if x.ndim != 1:
raise TypeError("DoubleCOp only works with 1D")
return Apply(self, [x], [x.type()])

def infer_shape(self, input_shapes):
return input_shapes

def grad(self, inputs, g):
return [g[0] * 2]
46 changes: 0 additions & 46 deletions doubleop.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,3 @@ def R_op(self, inputs, eval_points):
if eval_points[0] is None:
return eval_points
return self.grad(inputs, eval_points)


class DoubleC:
# This C code only works for 1D
def c_code(self, node, name, input_names,
output_names, sub):
return """
Py_XDECREF(%(out)s);
%(out)s = (PyArrayObject *)PyArray_NewLikeArray(
%(inp)s, NPY_ANYORDER, NULL, 0);
if (%(out)s == NULL) {
%(fail)s
}
{
PyObject *iter_in = PyArray_IterNew(inp),
PyObject *iter_out = PyArray_IterNew(*out);
if (iter_in == NULL || iter_out == NULL) {
Py_XDECREF(iter_in); Py_XDECREF(iter_out);
%(fail)s
}
for (;PyArray_ITER_NOTDONE(iter_in) &&
PyArray_ITER_NOTDONE(iter_out);
PyArray_ITER_NEXT(iter_in),
PyArray_ITER_NEXT(iter_out)) {
*(dtype_%(out)s *)PyArray_ITER_DATA(iter_out) =
(*(dtype_%(inp)s *)PyArray_ITER_DATA(iter_in)) * 2;
}
Py_DECREF(iter_in);
Py_DECREF(iter_out);
}
""" % dict(inp=input_names[0], out=output_names[0],
fail=sub["fail"])


from theano.gof import COp

class DoubleCOp(COp):
__props__ = ()

def __init__(self):
COp.__init__(self, "./doublecop.c",
"APPLY_SPECIFIC(doublecop)")

def make_node(self, x):
x = as_tensor_variable(x)
return Apply(self, [x], [x.type()])
20 changes: 20 additions & 0 deletions thunk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from theano import Op

class MyOp(Op):
__props__ = ()

def __init__(self, ...):
# set up parameters

def make_node(self, ...):
# create apply node

def make_thunk(self, node, storage_map,
compute_map, no_recycling):
# return a thunk

def infer_shape(self, input_shapes):
# return output shapes

def grad(self, inputs, output_grads):
# return gradient graph for each input

0 comments on commit 6a73fdb

Please sign in to comment.