提交 1efb1539 authored 作者: Pascal Lamblin's avatar Pascal Lamblin

Merge pull request #4335 from fvisin/fix_elemwise

Check RST files in '/theano/doc/' to follow numpy's docstring + Fix elemwise
...@@ -8,7 +8,7 @@ Contributing ...@@ -8,7 +8,7 @@ Contributing
============ ============
You want to contribute to Theano? That is great! This page explain our You want to contribute to Theano? That is great! This page explain our
workflow and some ressource for doing so. workflow and some resource for doing so.
Looking for an idea for a first contribution? Check `github issue Looking for an idea for a first contribution? Check `github issue
<https://github.com/Theano/Theano/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+fix%22>` <https://github.com/Theano/Theano/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+fix%22>`
...@@ -39,11 +39,287 @@ To get up to speed, you'll need to ...@@ -39,11 +39,287 @@ To get up to speed, you'll need to
.. _Sphinx: http://sphinx.pocoo.org/ .. _Sphinx: http://sphinx.pocoo.org/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _Allowed docstring sections in Napoleon: https://sphinxcontrib-napoleon.readthedocs.org/en/latest/#docstring-sections
.. _NumPy documentation: http://docs.scipy.org/numpy/ .. _NumPy documentation: http://docs.scipy.org/numpy/
.. _unittest: http://docs.python.org/library/unittest.html .. _unittest: http://docs.python.org/library/unittest.html
.. _nose: http://nose.readthedocs.org/en/latest/ .. _nose: http://nose.readthedocs.org/en/latest/
.. _quality_contributions:
Requirements for Quality Contributions
======================================
* All the code should be properly tested.
* The code should be compatible with Python 2.6 and above, as well as Python
3.3 and above (using `six` if needed).
* All the code should respect the
`PEP8 Code Style Guide <http://www.python.org/dev/peps/pep-0008>`_.
* The docstrings of all the classes and functions should respect the
`PEP257 <https://www.python.org/dev/peps/pep-0257/>`_ rules and follow the
`Numpy docstring standard
<https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt>`_.
Each point will be referred to more in detail in the following.
Unit tests
----------
When you submit a pull request, your changes will automatically be
tested via Travis-CI. This will post the results of the tests with a
little icon next to your commit. A yellow circle means the tests are
running. A red X means the tests failed and a green circle means the
tests passed.
Just because the tests run automatically does not mean you shouldn't
run them yourself to make sure everything is all right. You can run
only the portion you are modifying to go faster and have travis to
make sure there are no global impacts.
Also, if you are changing GPU code, travis doesn't test that, because
there are no GPUs on the test nodes.
To run the test suite with the default options, you can follow the
instructions of :ref:`testing_installation`.
Each night we execute all the unit tests automatically, with several
sets of options. The result is sent by email to the `theano-buildbot`_
mailing list.
For more detail, see :ref:`metadocumentation_nightly_build`.
To run all the tests with the same configuration as the buildbot, run
this script:
.. code-block:: bash
theano/misc/do_nightly_build
This script accepts arguments that it forwards to nosetests. You can
run only some tests or enable pdb by giving the equivalent nosetests
parameters.
Setting up your Editor for PEP8
-------------------------------
Here are instructions for :ref:`Vim <vim_pep8>` and :ref:`Emacs
<emacs_pep8>`. If you have similar instructions for other text editors
or IDE, please let us know and we will update this documentation.
.. _vim_pep8:
Vim
~~~
Detection of warnings and errors is done by the `pep8`_ script
(or `flake8`_, that also checks for other things, like syntax
errors). Syntax highlighting and general integration into Vim is done by
the `Syntastic`_ plugin for Vim.
To setup VIM:
#. Install flake8 (if not already installed) with::
pip install flake8
.. note:: You can use ``easy_install`` instead of ``pip``, and ``pep8``
instead of ``flake8`` if you prefer. The important thing is that the
``flake8`` or ``pep8`` executable ends up in your ``$PATH``.
#. Install vundle with::
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim
#. Edit ``~/.vimrc`` and add the lines:
.. code-block:: python
set nocompatible " be iMproved, required
filetype off " required
" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'gmarik/Vundle.vim' " let Vundle manage Vundle (required!)
Plugin 'scrooloose/syntastic'
Plugin 'jimf/vim-pep8-text-width'
" Syntastic settings
" You can run checkers explicitly by calling :SyntasticCheck <checker
let g:syntastic_python_checkers = ['flake8'] "use one of the following checkers:
" flake8, pyflakes, pylint, python (native checker)
let g:syntastic_enable_highlighting = 1 "highlight errors and warnings
let g:syntastic_style_error_symbol = ">>" "error symbol
let g:syntastic_warning_symbol = ">>" "warning symbol
let g:syntastic_check_on_open = 1
let g:syntastic_auto_jump = 0 "do not jump to errors when detected
#. Open a new vim and run ``:PluginInstall`` to automatically install the
plugins. When the installation is done, close the installation "window"
with ``:q``.
From now on Vim will check for PEP8 errors and highlight them whenever a
file is saved.
A few useful commands
"""""""""""""""""""""
* Open the list of errors: ``:lopen``, that can be abbreviated in ``:lop``
(denoted ``:lop[en]``).
* Close that list: ``:lcl[ose]``.
* Next error: ``:lne[xt]``.
* Previous error: ``:lp[revious]``.
Once you fix errors, messages and highlighting will still appear in the
fixed file until you save it again.
We can also configure the ``~/.vimrc`` to make it easier to work with Syntastic.
For instance, to add a summary in the status bar, you can add::
set statusline+=%{SyntasticStatuslineFlag()}
To bind F2 and F3 to navigate to previous and next error, you can add::
map <F2> :lprevious<CR>
map <F3> :lnext<CR>
You can prefix those by ``autocmd FileType python`` if you want these
bindings to work only on Python files.
.. _pep8: http://pypi.python.org/pypi/pep8
.. _flake8: http://pypi.python.org/pypi/flake8
.. _Syntastic: https://github.com/scrooloose/syntastic/
.. _pathogen.vim: https://github.com/tpope/vim-pathogen
.. _quickfix: http://vimdoc.sourceforge.net/htmldoc/quickfix.html#quickfix
.. _emacs_pep8:
Emacs
~~~~~
There is an **excellent** system to configure emacs for Python:
`emacs-for-python
<https://github.com/gabrielelanaro/emacs-for-python>`_. It gathers many
emacs config into one, and modifies them to behave together nicely. You
can use it to check for pep8 compliance and for Python syntax errors.
To install it on Linux, you can do like this:
.. code-block:: bash
cd
git clone https://github.com/gabrielelanaro/emacs-for-python.git ~/.emacs.d/emacs-for-python
Then in your ``~/.emacs`` file, add this:
.. code-block:: common-lisp
;; Mandatory
(load-file "~/.emacs.d/emacs-for-python/epy-init.el")
(add-to-list 'load-path "~/.emacs.d/emacs-for-python/") ;; tell where to load the various files
;; Each of them enables different parts of the system.
;; Only the first two are needed for pep8, syntax check.
(require 'epy-setup) ;; It will setup other loads, it is required!
(require 'epy-python) ;; If you want the python facilities [optional]
(require 'epy-completion) ;; If you want the autocompletion settings [optional]
(require 'epy-editing) ;; For configurations related to editing [optional]
;; [newer version of emacs-for-python]
(require 'epy-nose) ;; For shortcut to call nosetests [optional]
;; Define f10 to previous error
;; Define f11 to next error
(require 'epy-bindings) ;; For my suggested keybindings [optional]
;; Some shortcut that do not collide with gnome-terminal,
;; otherwise, "epy-bindings" define f10 and f11 for them.
(global-set-key [f2] 'flymake-goto-prev-error)
(global-set-key [f3] 'flymake-goto-next-error)
;; Next two lines are the checks to do. You can add more if you wish.
(epy-setup-checker "pyflakes %f") ;; For python syntax check
(epy-setup-checker "pep8 -r %f") ;; For pep8 check
.. note::
The script highlights problematic lines. This can make part of the
line not readable depending on the background. To replace the line
highlight by an underline, add this to your emacs configuration
file:
;; Make lines readable when there is an warning [optional]
(custom-set-faces
'(flymake-errline ((((class color)) (:underline "red"))))
'(flymake-warnline ((((class color)) (:underline "yellow")))))
Documentation and docstrings
----------------------------
* The documentation and the API documentation are generated using `Sphinx`_.
* The documentation should be written in `reStructuredText`_ and the
docstrings of all the classes and functions should respect the
`PEP257 <https://www.python.org/dev/peps/pep-0257/>`_ rules and follow the
`Numpy docstring standard
<https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt>`_.
* Split the docstrings in sections, according to the `Allowed docstring
sections in Napoleon`_
* To cross-reference other objects (e.g. reference other classes or methods) in
the docstrings, use the
`cross-referencing objects <http://www.sphinx-doc.org/en/stable/domains.html#cross-referencing-python-objects>`_
syntax. ``:py`` can be omitted, see e.g. this
`stackoverflow answer <http://stackoverflow.com/a/7754189>`_.
* See :ref:`metadocumentation`, for some information on how to generate the
documentation.
A Docstring Example
~~~~~~~~~~~~~~~~~~~
Here is an example on how to add a docstring to a class.
.. testcode:: python
import theano
class DoubleOp(theano.Op):
"""
Double each element of a tensor.
Parameters
----------
x : tensor
Input tensor
Returns
-------
tensor
a tensor of the same shape and dtype as the input with all
values doubled.
Notes
-----
this is a test note
See Also
--------
:class:`~theano.tensor.elemwise.Elemwise` : You can use this to replace
this example. Just execute `x * 2` with x being a Theano variable.
.. versionadded:: 0.6
"""
This is how it will show up for files that we auto-list in the library
documentation:
.. automodule:: theano.misc.doubleop
:members:
Installation and configuration Installation and configuration
============================== ==============================
...@@ -214,222 +490,6 @@ notified of your revision, so it is advised to reply to the comments on ...@@ -214,222 +490,6 @@ notified of your revision, so it is advised to reply to the comments on
GitHub, to let them know that you have submitted a fix. GitHub, to let them know that you have submitted a fix.
.. _quality_contributions:
Tips for Quality Contributions
==============================
Coding Style Auto Check
-----------------------
In Theano, we use the same coding style as the `Pylearn
<http://deeplearning.net/software/pylearn/v2_planning/API_coding_style.html>`_
project, except that we don't use the numpy docstring standard.
The principal thing to know is that we follow the
`PEP 8 <http://www.python.org/dev/peps/pep-0008/>`_ coding style.
We use git hooks provided in the project `pygithooks
<https://github.com/lumberlabs/pygithooks>`_ to validate that commits
respect pep8. This happens when each user commits, not when we
push/merge to the Theano repository. Github doesn't allow us to have
code executed when we push to the repository. So we ask all
contributors to use those hooks.
For historic reason, we currently don't have all files respecting pep8.
We decided to fix everything incrementally. So not all files respect it
now. So we strongly suggest that you use the "increment" pygithooks
config option to have a good workflow. See the pygithooks main page
for how to set it up for Theano and how to enable this option.
Setting up your Editor for PEP8
-------------------------------
Here are instructions for :ref:`Vim <vim_pep8>` and :ref:`Emacs
<emacs_pep8>`. If you have similar instructions for other text editors
or IDE, please let us know and we will update this documentation.
.. _vim_pep8:
Vim
~~~
Detection of warnings and errors is done by the `pep8`_ script
(or `flake8`_, that also checks for other things, like syntax
errors). Syntax highlighting and general integration into Vim is done by
the `Syntastic`_ plugin for Vim.
To install flake8, simply run::
pip install flake8
You can use ``easy_install`` instead of ``pip``, and ``pep8`` instead of
``flake8`` if you prefer. The important thing is that the ``flake8`` or
``pep8`` executable ends up in your ``$PATH``.
To install Syntastic, according to its documentation, the easiest way is
to install `pathogen.vim`_ first.
Here's a relevant extract of pathogen.vim's installation instructions:
Install to ``~/.vim/autoload/pathogen.vim``. Or copy and paste::
mkdir -p ~/.vim/autoload ~/.vim/bundle; \
curl -so ~/.vim/autoload/pathogen.vim \
https://raw.github.com/tpope/vim-pathogen/HEAD/autoload/pathogen.vim
If you don't have ``curl``, use ``wget -O`` instead.
By the way, if you're using Windows, change all occurrences of ``~/.vim``
to ``~\vimfiles``.
Add this to your vimrc::
call pathogen#infect()
Now any plugins you wish to install can be extracted to a subdirectory
under ``~/.vim/bundle``, and they will be added to the ``'runtimepath'``.
Now, we can install Syntastic. From the installation instructions:
.. code-block:: bash
cd ~/.vim/bundle
git clone https://github.com/scrooloose/syntastic.git
Then reload vim, run ``:Helptags``, and check out ``:help syntastic.txt``.
From now on, when you save into a Python file, a syntax check will be
run, and results will be displayed using Vim's `quickfix`_ mechanism
(more precisely, a location-list). A few useful commands are:
* Open the list of errors: ``:lopen``, that can be abbreviated in ``:lop``
(denoted ``:lop[en]``).
* Close that list: ``:lcl[ose]``.
* Next error: ``:lne[xt]``.
* Previous error: ``:lp[revious]``.
Once you fix errors, messages and highlighting will still appear in the
fixed file until you save it again.
We can also configure the ``~/.vimrc`` to make it easier to work with Syntastic.
For instance, to add a summary in the status bar, you can add::
set statusline+=%{SyntasticStatuslineFlag()}
To bind F2 and F3 to navigate to previous and next error, you can add::
map <F2> :lprevious<CR>
map <F3> :lnext<CR>
You can prefix those by ``autocmd FileType python`` if you want these
bindings to work only on Python files.
.. _pep8: http://pypi.python.org/pypi/pep8
.. _flake8: http://pypi.python.org/pypi/flake8
.. _Syntastic: https://github.com/scrooloose/syntastic/
.. _pathogen.vim: https://github.com/tpope/vim-pathogen
.. _quickfix: http://vimdoc.sourceforge.net/htmldoc/quickfix.html#quickfix
.. _emacs_pep8:
Emacs
~~~~~
There is an **excellent** system to configure emacs for Python:
`emacs-for-python
<https://github.com/gabrielelanaro/emacs-for-python>`_. It gathers many
emacs config into one, and modifies them to behave together nicely. You
can use it to check for pep8 compliance and for Python syntax errors.
To install it on Linux, you can do like this:
.. code-block:: bash
cd
git clone https://github.com/gabrielelanaro/emacs-for-python.git ~/.emacs.d/emacs-for-python
Then in your ``~/.emacs`` file, add this:
.. code-block:: common-lisp
;; Mandatory
(load-file "~/.emacs.d/emacs-for-python/epy-init.el")
(add-to-list 'load-path "~/.emacs.d/emacs-for-python/") ;; tell where to load the various files
;; Each of them enables different parts of the system.
;; Only the first two are needed for pep8, syntax check.
(require 'epy-setup) ;; It will setup other loads, it is required!
(require 'epy-python) ;; If you want the python facilities [optional]
(require 'epy-completion) ;; If you want the autocompletion settings [optional]
(require 'epy-editing) ;; For configurations related to editing [optional]
;; [newer version of emacs-for-python]
(require 'epy-nose) ;; For shortcut to call nosetests [optional]
;; Define f10 to previous error
;; Define f11 to next error
(require 'epy-bindings) ;; For my suggested keybindings [optional]
;; Some shortcut that do not collide with gnome-terminal,
;; otherwise, "epy-bindings" define f10 and f11 for them.
(global-set-key [f2] 'flymake-goto-prev-error)
(global-set-key [f3] 'flymake-goto-next-error)
;; Next two lines are the checks to do. You can add more if you wish.
(epy-setup-checker "pyflakes %f") ;; For python syntax check
(epy-setup-checker "pep8 -r %f") ;; For pep8 check
.. note::
The script highlights problematic lines. This can make part of the
line not readable depending on the background. To replace the line
highlight by an underline, add this to your emacs configuration
file:
;; Make lines readable when there is an warning [optional]
(custom-set-faces
'(flymake-errline ((((class color)) (:underline "red"))))
'(flymake-warnline ((((class color)) (:underline "yellow")))))
Unit tests
----------
When you submit a pull request, your changes will automatically be
tested via Travis-CI. This will post the results of the tests with a
little icon next to your commit. A yellow circle means the tests are
running. A red X means the tests failed and a green circle means the
tests passed.
Just because the tests run automatically does not mean you shouldn't
run them yourself to make sure everything is all right. You can run
only the portion you are modifying to go faster and have travis to
make sure there are no global impacts.
Also, if you are changing GPU code, travis doesn't test that, because
there are no GPUs on the test nodes.
To run the test suite with the default options, you can follow the
instructions of :ref:`testing_installation`.
Each night we execute all the unit tests automatically, with several
sets of options. The result is sent by email to the `theano-buildbot`_
mailing list.
For more detail, see :ref:`metadocumentation_nightly_build`.
To run all the tests with the same configuration as the buildbot, run
this script:
.. code-block:: bash
theano/misc/do_nightly_build
This script accepts arguments that it forwards to nosetests. You can
run only some tests or enable pdb by giving the equivalent nosetests
parameters.
More Advanced Git Usage More Advanced Git Usage
======================= =======================
......
...@@ -875,43 +875,12 @@ Modify and execute to compute: numpy.add and numpy.subtract. ...@@ -875,43 +875,12 @@ Modify and execute to compute: numpy.add and numpy.subtract.
Modify and execute the example to return two outputs: x + y Modify and execute the example to return two outputs: x + y
and x - y. and x - y.
.. _Documentation:
Documentation Documentation and Coding Style
------------- ------------------------------
Please always respect the :ref:`quality_contributions` or your contribution
See :ref:`metadocumentation`, for some information on how to generate will not be accepted.
the documentation.
Here is an example how to add docstring to a class.
.. testcode:: python
import theano
class DoubleOp(theano.Op):
""" Double each element of a tensor.
:param x: input tensor.
:return: a tensor of the same shape and dtype as the input with all
values doubled.
:note:
this is a test note
:seealso:
You can use the elemwise op to replace this example.
Just execute `x * 2` with x being a Theano variable.
.. versionadded:: 0.6
"""
This is how it will show up for files that we auto-list in the library
documentation:
.. automodule:: theano.misc.doubleop
:members:
NanGuardMode and AllocEmpty NanGuardMode and AllocEmpty
--------------------------- ---------------------------
......
===================================================================
:mod:`tensor.elemwise` -- Tensor Elemwise
===================================================================
.. testsetup::
from theano.tensor.elemwise import *
.. module:: tensor.elemwise
:platform: Unix, Windows
:synopsis: Tensor Elemwise
.. moduleauthor:: LISA
.. automodule:: theano.tensor.elemwise
:members:
...@@ -23,6 +23,7 @@ They are grouped into the following sections: ...@@ -23,6 +23,7 @@ They are grouped into the following sections:
shared_randomstreams shared_randomstreams
signal/index signal/index
utils utils
elemwise
extra_ops extra_ops
io io
opt opt
......
...@@ -5,19 +5,29 @@ import theano ...@@ -5,19 +5,29 @@ import theano
class DoubleOp(theano.Op): class DoubleOp(theano.Op):
""" Double each element of a tensor. """
Double each element of a tensor.
:param x: input tensor. Parameters
----------
x : tensor
Input tensor
:return: a tensor of the same shape and dtype as the input with all Returns
-------
tensor
a tensor of the same shape and dtype as the input with all
values doubled. values doubled.
:note: Notes
-----
this is a test note this is a test note
:seealso: See Also
You can use the elemwise op to replace this example. --------
Just execute `x * 2` with x being a Theano variable. :class:`~theano.tensor.elemwise.Elemwise` : You can use this to replace
this example. Just execute `x * 2` with x being a Theano variable.
.. versionadded:: 0.6 .. versionadded:: 0.6
""" """
......
...@@ -76,17 +76,21 @@ class DimShuffle(Op): ...@@ -76,17 +76,21 @@ class DimShuffle(Op):
If True, the output will be a view of the input. If True, the output will be a view of the input.
If False (default), the output will be a copy of the input. If False (default), the output will be a copy of the input.
If j = new_order[i] is an index, the output's ith dimension Note
----
If `j = new_order[i]` is an index, the output's ith dimension
will be the input's jth dimension. will be the input's jth dimension.
If new_order[i] is 'x', the output's ith dimension will If `new_order[i]` is `x`, the output's ith dimension will
be 1 and Broadcast operations will be allowed to do broadcasting be 1 and Broadcast operations will be allowed to do broadcasting
over that dimension. over that dimension.
If input.broadcastable[i] == False then i must be found in new_order. If `input.broadcastable[i] == False` then `i` must be found in new_order.
Broadcastable dimensions, on the other hand, can be discarded. Broadcastable dimensions, on the other hand, can be discarded.
Extended Summary Note
---------------- ----
.. code-block:: python
DimShuffle((False, False, False), ['x', 2, 'x', 0, 1]) DimShuffle((False, False, False), ['x', 2, 'x', 0, 1])
This op will only work on 3d tensors with no broadcastable This op will only work on 3d tensors with no broadcastable
...@@ -96,6 +100,8 @@ class DimShuffle(Op): ...@@ -96,6 +100,8 @@ class DimShuffle(Op):
shape (20, 30, 40), the resulting tensor will have dimensions shape (20, 30, 40), the resulting tensor will have dimensions
(1, 40, 1, 20, 30). (AxBxC tensor is mapped to 1xCx1xAxB tensor) (1, 40, 1, 20, 30). (AxBxC tensor is mapped to 1xCx1xAxB tensor)
.. code-block:: python
DimShuffle((True, False), [1]) DimShuffle((True, False), [1])
This op will only work on 2d tensors with the first dimension This op will only work on 2d tensors with the first dimension
...@@ -105,20 +111,23 @@ class DimShuffle(Op): ...@@ -105,20 +111,23 @@ class DimShuffle(Op):
If the tensor has shape (1, 20), the resulting tensor will have shape If the tensor has shape (1, 20), the resulting tensor will have shape
(20, ). (20, ).
More examples : Example
DimShuffle((), ['x']) -> make a 0d (scalar) into a 1d vector -------
DimShuffle((False, False), [0, 1]) -> identity .. code-block:: python
DimShuffle((False, False), [1, 0]) -> inverts the 1st and 2nd dimensions
DimShuffle((False,), ['x', 0]) -> make a row out DimShuffle((), ['x']) # make a 0d (scalar) into a 1d vector
of a 1d vector (N to 1xN) DimShuffle((False, False), [0, 1]) # identity
DimShuffle((False,), [0, 'x']) -> make a column DimShuffle((False, False), [1, 0]) # inverts the 1st and 2nd dimensions
out of a 1d vector (N to Nx1) DimShuffle((False,), ['x', 0]) # make a row out of a 1d vector
DimShuffle((False, False, False), [2, 0, 1]) -> AxBxC to CxAxB # (N to 1xN)
DimShuffle((False, False), [0, 'x', 1]) -> AxB to Ax1xB DimShuffle((False,), [0, 'x']) # make a column out of a 1d vector
DimShuffle((False, False), [1, 'x', 0]) -> AxB to Bx1xA # (N to Nx1)
DimShuffle((False, False, False), [2, 0, 1]) # AxBxC to CxAxB
The reordering of the dimensions can be done in numpy with the DimShuffle((False, False), [0, 'x', 1]) # AxB to Ax1xB
transpose function. DimShuffle((False, False), [1, 'x', 0]) # AxB to Bx1xA
The reordering of the dimensions can be done with the numpy.transpose
function.
Adding, subtracting dimensions can be done with reshape. Adding, subtracting dimensions can be done with reshape.
""" """
...@@ -300,10 +309,11 @@ class DimShuffle(Op): ...@@ -300,10 +309,11 @@ class DimShuffle(Op):
# get the copy / view of the input depending on whether we're doingi # get the copy / view of the input depending on whether we're doingi
# things inplace or not. # things inplace or not.
if self.inplace: if self.inplace:
get_base = [ get_base = ['{ PyArrayObject * %(basename)s = %(input)s',
'{ PyArrayObject * %(basename)s = %(input)s', 'Py_INCREF((PyObject*)%(basename)s)'] 'Py_INCREF((PyObject*)%(basename)s)']
else: else:
get_base = [('{ PyArrayObject * %(basename)s = ' get_base = [
('{ PyArrayObject * %(basename)s = '
'(PyArrayObject*)PyArray_FromAny((PyObject*)%(input)s,' '(PyArrayObject*)PyArray_FromAny((PyObject*)%(input)s,'
' NULL, 0, 0, NPY_ARRAY_ALIGNED|NPY_ARRAY_ENSURECOPY,' ' NULL, 0, 0, NPY_ARRAY_ALIGNED|NPY_ARRAY_ENSURECOPY,'
' NULL)')] ' NULL)')]
...@@ -343,12 +353,9 @@ class DimShuffle(Op): ...@@ -343,12 +353,9 @@ class DimShuffle(Op):
) )
for i in xrange(nd_out - 2, -1, -1): for i in xrange(nd_out - 2, -1, -1):
strides_statements.append( strides_statements.append(
"if (strides[%(i)s] == 0) strides[%(i)s] = strides[%(i)s+1] * dimensions[%(i)s+1]" % dict(i=str(i))) "if (strides[%(i)s] == 0) strides[%(i)s] = strides[%(i)s+1] * "
"dimensions[%(i)s+1]" % dict(i=str(i)))
#
# PyObject* PyArray_New(PyTypeObject* subtype, int nd, npy_intp* dims, int type_num,
# npy_intp* strides, void* data, int itemsize, int flags, PyObject* obj)
#
close_bracket = [ close_bracket = [
# create a new array, # create a new array,
('%(res)s = (PyArrayObject*)PyArray_New(&PyArray_Type, ' ('%(res)s = (PyArrayObject*)PyArray_New(&PyArray_Type, '
...@@ -482,17 +489,17 @@ class Elemwise(OpenMPOp): ...@@ -482,17 +489,17 @@ class Elemwise(OpenMPOp):
variable number of inputs), whereas the numpy function may variable number of inputs), whereas the numpy function may
not have varargs. not have varargs.
Examples Note
-------- ----
Elemwise(add) # represents + on tensors (x + y) | Elemwise(add) represents + on tensors (x + y)
Elemwise(add, {0 : 0}) # represents the += operation (x += y) | Elemwise(add, {0 : 0}) represents the += operation (x += y)
Elemwise(add, {0 : 1}) # represents += on the second argument (y += x) | Elemwise(add, {0 : 1}) represents += on the second argument (y += x)
Elemwise(mul)(rand(10, 5), rand(1, 5)) # the second input is completed | Elemwise(mul)(rand(10, 5), rand(1, 5)) the second input is completed \
# along the first dimension to match the first input along the first dimension to match the first input
Elemwise(true_div)(rand(10, 5), rand(10, 1)) # same but along the | Elemwise(true_div)(rand(10, 5), rand(10, 1)) same but along the \
# second dimension second dimension
Elemwise(int_div)(rand(1, 5), rand(10, 1)) # the output has size (10, 5) | Elemwise(int_div)(rand(1, 5), rand(10, 1)) the output has size (10, 5)
Elemwise(log)(rand(3, 4, 5)) | Elemwise(log)(rand(3, 4, 5))
""" """
...@@ -781,7 +788,8 @@ class Elemwise(OpenMPOp): ...@@ -781,7 +788,8 @@ class Elemwise(OpenMPOp):
# the gradient contains a constant, translate it as # the gradient contains a constant, translate it as
# an equivalent TensorType of size 1 and proper number of # an equivalent TensorType of size 1 and proper number of
# dimensions # dimensions
res = theano.tensor.constant(numpy.asarray(r.data), dtype=r.type.dtype) res = theano.tensor.constant(numpy.asarray(r.data),
dtype=r.type.dtype)
return DimShuffle((), ['x'] * nd, inplace=False)(res) return DimShuffle((), ['x'] * nd, inplace=False)(res)
new_r = Elemwise(node.op, {})( new_r = Elemwise(node.op, {})(
*[transform(ipt) for ipt in node.inputs]) *[transform(ipt) for ipt in node.inputs])
...@@ -1127,15 +1135,20 @@ class Elemwise(OpenMPOp): ...@@ -1127,15 +1135,20 @@ class Elemwise(OpenMPOp):
idtypes + list(real_odtypes))]) idtypes + list(real_odtypes))])
preloops = {} preloops = {}
for i, (loop_order, dtype) in enumerate(zip(loop_orders, dtypes)): for i, (loop_order, dtype) in enumerate(zip(loop_orders,
dtypes)):
for j, index in enumerate(loop_order): for j, index in enumerate(loop_order):
if index != 'x': if index != 'x':
preloops.setdefault(j, "") preloops.setdefault(j, "")
preloops[j] += ("%%(lv%(i)s)s_iter = (%(dtype)s*)(PyArray_DATA(%%(lv%(i)s)s));\n" % locals()) % sub preloops[j] += ("%%(lv%(i)s)s_iter = (%(dtype)s*)"
"(PyArray_DATA(%%(lv%(i)s)s));\n"
% locals()) % sub
break break
else: # all broadcastable else: # all broadcastable
preloops.setdefault(0, "") preloops.setdefault(0, "")
preloops[0] += ("%%(lv%(i)s)s_iter = (%(dtype)s*)(PyArray_DATA(%%(lv%(i)s)s));\n" % locals()) % sub preloops[0] += ("%%(lv%(i)s)s_iter = (%(dtype)s*)"
"(PyArray_DATA(%%(lv%(i)s)s));\n"
% locals()) % sub
init_array = preloops.get(0, " ") init_array = preloops.get(0, " ")
loop = """ loop = """
...@@ -1202,7 +1215,8 @@ class Elemwise(OpenMPOp): ...@@ -1202,7 +1215,8 @@ class Elemwise(OpenMPOp):
dtype_%(x)s& %(x)s_i = ((dtype_%(x)s*) PyArray_DATA(%(x)s))[0]; dtype_%(x)s& %(x)s_i = ((dtype_%(x)s*) PyArray_DATA(%(x)s))[0];
""" % locals() """ % locals()
if self.openmp: if self.openmp:
contig += """#pragma omp parallel for if(n>=%d)""" % (config.openmp_elemwise_minsize) contig += """#pragma omp parallel for if(n>=%d)
""" % (config.openmp_elemwise_minsize)
contig += """ contig += """
for(int i=0; i<n; i++){ for(int i=0; i<n; i++){
%(index)s %(index)s
...@@ -1259,7 +1273,8 @@ class Elemwise(OpenMPOp): ...@@ -1259,7 +1273,8 @@ class Elemwise(OpenMPOp):
for output in node.outputs]) for output in node.outputs])
version.append(self.scalar_op.c_code_cache_version_apply(scalar_node)) version.append(self.scalar_op.c_code_cache_version_apply(scalar_node))
for i in node.inputs + node.outputs: for i in node.inputs + node.outputs:
version.append(get_scalar_type(dtype=i.type.dtype).c_code_cache_version()) version.append(
get_scalar_type(dtype=i.type.dtype).c_code_cache_version())
version.append(('openmp', self.openmp)) version.append(('openmp', self.openmp))
if all(version): if all(version):
return tuple(version) return tuple(version)
...@@ -1298,20 +1313,22 @@ class CAReduce(Op): ...@@ -1298,20 +1313,22 @@ class CAReduce(Op):
- List of dimensions that we want to reduce - List of dimensions that we want to reduce
- If None, all dimensions are reduced - If None, all dimensions are reduced
Examples Note
-------- ----
CAReduce(add) -> sum (ie, acts like the numpy sum operation) .. code-block:: python
CAReduce(mul) -> product
CAReduce(maximum) -> max CAReduce(add) # sum (ie, acts like the numpy sum operation)
CAReduce(minimum) -> min CAReduce(mul) # product
CAReduce(or_) -> any # not lazy CAReduce(maximum) # max
CAReduce(and_) -> all # not lazy CAReduce(minimum) # min
CAReduce(xor) -> a bit at 1 tell that there was an odd number of bit at CAReduce(or_) # any # not lazy
that position that where 1. CAReduce(and_) # all # not lazy
0 it was an even number ... CAReduce(xor) # a bit at 1 tell that there was an odd number of
# bit at that position that where 1. 0 it was an
# even number ...
In order to (eventually) optimize memory usage patterns, In order to (eventually) optimize memory usage patterns,
L{CAReduce} makes zero guarantees on the order in which it CAReduce makes zero guarantees on the order in which it
iterates over the dimensions and the elements of the iterates over the dimensions and the elements of the
array(s). Therefore, to ensure consistent variables, the scalar array(s). Therefore, to ensure consistent variables, the scalar
operation represented by the reduction must be both commutative operation represented by the reduction must be both commutative
...@@ -1507,7 +1524,8 @@ class CAReduce(Op): ...@@ -1507,7 +1524,8 @@ class CAReduce(Op):
if hasattr(self, 'acc_dtype') and self.acc_dtype is not None: if hasattr(self, 'acc_dtype') and self.acc_dtype is not None:
if self.acc_dtype == 'float16': if self.acc_dtype == 'float16':
raise theano.gof.utils.MethodNotDefined("no c_code for float16") raise theano.gof.utils.MethodNotDefined("no c_code for "
"float16")
acc_type = TensorType( acc_type = TensorType(
broadcastable=node.outputs[0].broadcastable, broadcastable=node.outputs[0].broadcastable,
dtype=self.acc_dtype) dtype=self.acc_dtype)
...@@ -1684,7 +1702,8 @@ for(int i=0;i<PyArray_NDIM(%(iname)s);i++){ ...@@ -1684,7 +1702,8 @@ for(int i=0;i<PyArray_NDIM(%(iname)s);i++){
for output in node.outputs]) for output in node.outputs])
version.append(self.scalar_op.c_code_cache_version_apply(scalar_node)) version.append(self.scalar_op.c_code_cache_version_apply(scalar_node))
for i in node.inputs + node.outputs: for i in node.inputs + node.outputs:
version.append(get_scalar_type(dtype=i.type.dtype).c_code_cache_version()) version.append(
get_scalar_type(dtype=i.type.dtype).c_code_cache_version())
if all(version): if all(version):
return tuple(version) return tuple(version)
else: else:
...@@ -1695,7 +1714,7 @@ class All(CAReduce): ...@@ -1695,7 +1714,7 @@ class All(CAReduce):
""" Applies `bitwise and` to all the values of a tensor along the """ Applies `bitwise and` to all the values of a tensor along the
specified axis(es). specified axis(es).
Equivalent to CAReduce(scalar.and_, axis=axis). Equivalent to `CAReduce(scalar.and\_, axis=axis)`.
""" """
...@@ -1727,7 +1746,7 @@ class Any(CAReduce): ...@@ -1727,7 +1746,7 @@ class Any(CAReduce):
""" Applies `bitwise or` to all the values of a tensor along the """ Applies `bitwise or` to all the values of a tensor along the
specified axis(es). specified axis(es).
Equivalent to CAReduce(scalar.or_, axis=axis). Equivalent to `CAReduce(scalar.or\_, axis=axis)`.
""" """
...@@ -1778,17 +1797,19 @@ class CAReduceDtype(CAReduce): ...@@ -1778,17 +1797,19 @@ class CAReduceDtype(CAReduce):
It must be commutative and associative. It must be commutative and associative.
axis axis
- the dimension along which we want to reduce * the dimension along which we want to reduce
- list of dimensions that we want to reduce * list of dimensions that we want to reduce
- if None, all dimensions are reduced * if None, all dimensions are reduced
dtype dtype
The dtype of the returned tensor. If None, then we use the default The dtype of the returned tensor. If None, then we use the default
dtype which is the same as the input tensor's dtype except when: dtype which is the same as the input tensor's dtype except when:
- the input dtype is a signed integer of precision < 64 bit, in
which case we use int64 * the input dtype is a signed integer of precision < 64 bit, in which
- the input dtype is an unsigned integer of precision < 64 bit, in case we use int64
* the input dtype is an unsigned integer of precision < 64 bit, in
which case we use uint64 which case we use uint64
This default dtype does _not_ depend on the value of "acc_dtype". This default dtype does _not_ depend on the value of "acc_dtype".
This behavior is similar in spirit to that of numpy (except numpy This behavior is similar in spirit to that of numpy (except numpy
uses the default machine integer while we always use 64 bit uses the default machine integer while we always use 64 bit
...@@ -1798,10 +1819,11 @@ class CAReduceDtype(CAReduce): ...@@ -1798,10 +1819,11 @@ class CAReduceDtype(CAReduce):
The dtype of the internal accumulator. The dtype of the internal accumulator.
If None (default), we use the dtype in the list below, If None (default), we use the dtype in the list below,
or the input dtype if its precision is higher: or the input dtype if its precision is higher:
- for int dtypes, we use at least int64;
- for uint dtypes, we use at least uint64; * for int dtypes, we use at least int64;
- for float dtypes, we use at least float64; * for uint dtypes, we use at least uint64;
- for complex dtypes, we use at least complex128. * for float dtypes, we use at least float64;
* for complex dtypes, we use at least complex128.
""" """
...@@ -1930,7 +1952,7 @@ class Sum(CAReduceDtype): ...@@ -1930,7 +1952,7 @@ class Sum(CAReduceDtype):
""" """
Sums all the values of a tensor along the specified axis(es). Sums all the values of a tensor along the specified axis(es).
Equivalent to CAReduceDtype(scalar.add, axis=axis, dtype=dtype), Equivalent to `CAReduceDtype(scalar.add, axis=axis, dtype=dtype)`,
with the difference that this defines the gradient of sum wrt its with the difference that this defines the gradient of sum wrt its
tensor input. tensor input.
...@@ -2005,7 +2027,7 @@ class Prod(CAReduceDtype): ...@@ -2005,7 +2027,7 @@ class Prod(CAReduceDtype):
""" """
Multiplies all the values of a tensor along the specified axis(es). Multiplies all the values of a tensor along the specified axis(es).
Equivalent to CAReduce(scalar.prod, axis = axis), with the Equivalent to `CAReduce(scalar.prod, axis = axis)`, with the
difference that this defines the gradient of prod wrt its tensor difference that this defines the gradient of prod wrt its tensor
input. input.
...@@ -2052,10 +2074,9 @@ class Prod(CAReduceDtype): ...@@ -2052,10 +2074,9 @@ class Prod(CAReduceDtype):
"incoming gradient", ie. the gradient of the cost relative to the "incoming gradient", ie. the gradient of the cost relative to the
output/product). output/product).
-----
With zeros, things get more complicated. For a given group, we have 3 With zeros, things get more complicated. For a given group, we have 3
cases: cases:
* No zeros in the group. Use previous trick. * No zeros in the group. Use previous trick.
* If only one zero is present, then the gradient for that element is * If only one zero is present, then the gradient for that element is
non-zero, but is zero for all others. non-zero, but is zero for all others.
...@@ -2188,7 +2209,8 @@ class MulWithoutZeros(scalar.BinaryScalarOp): ...@@ -2188,7 +2209,8 @@ class MulWithoutZeros(scalar.BinaryScalarOp):
def c_code_cache_version(self): def c_code_cache_version(self):
return (1,) return (1,)
mul_without_zeros = MulWithoutZeros(scalar.upcast_out, name='mul_without_zeros') mul_without_zeros = MulWithoutZeros(scalar.upcast_out,
name='mul_without_zeros')
class ProdWithoutZeros(CAReduceDtype): class ProdWithoutZeros(CAReduceDtype):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论