提交 adbc78dd authored 作者: nouiz's avatar nouiz

Merge pull request #501 from lamblin/remove_api

Remove obsolete :api: role, and replace refs.
...@@ -23,7 +23,7 @@ import sys, os ...@@ -23,7 +23,7 @@ import sys, os
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'ext'] extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo']
todo_include_todos = True todo_include_todos = True
...@@ -175,7 +175,7 @@ latex_documents = [ ...@@ -175,7 +175,7 @@ latex_documents = [
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
# the title page. # the title page.
#latex_logo = 'images/snake_theta2-trans.png' #latex_logo = 'images/snake_theta2-trans.png'
latex_logo = None latex_logo = 'images/theano_logo_allblue_200x46.png'
# For "manual" documents, if this is true, then toplevel headings are parts, # For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters. # not chapters.
......
import sys
import re
import os
from docutils import nodes, utils
from docutils.parsers.rst import roles
import epydoc.docwriter.xlink as xlink
def role_fn(name, rawtext, text, lineno, inliner,
options={}, content=[]):
node = nodes.reference(rawtext, text, refuri = "http://pylearn.org/theano/wiki/%s" % text)
return [node], []
_TARGET_RE = re.compile(r'^(.*?)\s*<(?:URI:|URL:)?([^<>]+)>$')
def create_api_role(name, problematic):
"""
Create and register a new role to create links for an API documentation.
Create a role called `name`, which will use the URL resolver registered as
``name`` in `api_register` to create a link for an object.
:Parameters:
`name` : `str`
name of the role to create.
`problematic` : `bool`
if True, the registered role will create problematic nodes in
case of failed references. If False, a warning will be raised
anyway, but the output will appear as an ordinary literal.
"""
def resolve_api_name(n, rawtext, text, lineno, inliner,
options={}, content=[]):
# Check if there's separate text & targets
m = _TARGET_RE.match(text)
if m: text, target = m.groups()
else: target = text
# node in monotype font
text = utils.unescape(text)
node = nodes.literal(rawtext, text, **options)
# Get the resolver from the register and create an url from it.
try:
url = xlink.api_register[name].get_url(target)
except IndexError, exc:
msg = inliner.reporter.warning(str(exc), line=lineno)
if problematic:
prb = inliner.problematic(rawtext, text, msg)
return [prb], [msg]
else:
return [node], []
if url is not None:
node = nodes.reference(rawtext, '', node, refuri=url, **options)
return [node], []
roles.register_local_role(name, resolve_api_name)
def setup(app):
try:
xlink.set_api_file('api', os.path.join(app.outdir, 'api', 'api-objects.txt'))
apiroot = os.getenv('THEANO_API_ROOT')
if not apiroot:
apiroot = os.path.join(os.path.realpath('api'), '')
xlink.set_api_root('api', apiroot)
#xlink.create_api_role('api', True)
create_api_role('api', True)
except IOError:
print >>sys.stderr, 'WARNING: Could not find api file! API links will not work.'
app.add_role("wiki", role_fn)
...@@ -44,7 +44,7 @@ What needs to be defined ...@@ -44,7 +44,7 @@ What needs to be defined
In order to be C-compatible, a Type must define several additional In order to be C-compatible, a Type must define several additional
methods, which all start with the ``c_`` prefix. The complete list can methods, which all start with the ``c_`` prefix. The complete list can
be found in the documentation for :api:`gof.type.Type`. Here, we'll focus on be found in the documentation for :class:`.gof.type.Type`. Here, we'll focus on
the most important ones: the most important ones:
......
...@@ -10,7 +10,7 @@ Making the double type ...@@ -10,7 +10,7 @@ Making the double type
Type's contract Type's contract
=============== ===============
In Theano's framework, a ``Type`` (:api:`gof.type.Type`) In Theano's framework, a ``Type`` (:class:`.gof.type.Type`)
is any object which defines the following is any object which defines the following
methods. To obtain the default methods described below, the Type should methods. To obtain the default methods described below, the Type should
be an instance of ``Type`` or should be an instance of a be an instance of ``Type`` or should be an instance of a
......
...@@ -252,3 +252,10 @@ to both. ...@@ -252,3 +252,10 @@ to both.
As you read through examples of Theano code, you will probably see many As you read through examples of Theano code, you will probably see many
instances of Modules being nested in this way. instances of Modules being nested in this way.
===================================================================
:mod:`module` -- API documentation
===================================================================
.. automodule:: theano.compile.module
:members:
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
env env
toolbox toolbox
type
.. _libdoc_gof_type:
================================================
:mod:`type` -- Interface for types of variables
================================================
.. module:: env
:platform: Unix, Windows
:synopsis: Interface for types of symbolic variables
.. moduleauthor:: LISA
---------
Reference
---------
.. automodule:: theano.gof.type
:members:
...@@ -133,7 +133,7 @@ analogous to a function. ...@@ -133,7 +133,7 @@ analogous to a function.
A Module is meant to contain Components. A Module is meant to contain Components.
Attributes which are not Components themselves must at least be transform-able Attributes which are not Components themselves must at least be transform-able
into Components by :api:`compile.module.wrap`. If a Module contains something into Components by :func:`.compile.module.wrap`. If a Module contains something
that is not convertible into a Component, then it is not possible to compile that is not convertible into a Component, then it is not possible to compile
that Module with ``make``. that Module with ``make``.
......
.. _module: .. _module2:
###### ######
Module Module
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
New C code generation? New C code generation?
====================== ======================
== Issues == Issues
======
There are several issues with the current way C code is generated: There are several issues with the current way C code is generated:
* Ops cannot declare their own persistent variables. * Ops cannot declare their own persistent variables.
...@@ -13,91 +14,92 @@ There are several issues with the current way C code is generated: ...@@ -13,91 +14,92 @@ There are several issues with the current way C code is generated:
* It is currently impossible to specialize support code based on the self. * It is currently impossible to specialize support code based on the self.
* Caching of the generated code for graphs is greatly suboptimal. * Caching of the generated code for graphs is greatly suboptimal.
== Structure == Structure
=========
Currently, the general structure of the generated C code is approximately as follows: Currently, the general structure of the generated C code is approximately as follows:
{{{ .. code-block:: c
<imports>
<weave type converters> <imports>
<op/result support code> <weave type converters>
<op/result support code>
struct my_computation {
<input/output storage> struct my_computation {
<persistent fields> <input/output storage>
init(<input/output storage>) { <initialize persistent fields> } <persistent fields>
cleanup { <clean up persistent fields> } init(<input/output storage>) { <initialize persistent fields> }
run { <run the computation> } cleanup { <clean up persistent fields> }
}; run { <run the computation> }
};
<runner for the struct>
PyObject* instantiate(PyObject* args) { <runner for the struct>
<weave stuff> PyObject* instantiate(PyObject* args) {
<make up a CObject out of the runner and a my_computation instance> <weave stuff>
<weave stuff> <make up a CObject out of the runner and a my_computation instance>
} <weave stuff>
<python exports for instantiate> }
}}} <python exports for instantiate>
The module produced via that method then has to be used as such: The module produced via that method then has to be used as such::
{{{
obj = module.instantiate(error_storage, input_storage, output_storage, orphan_storage) obj = module.instantiate(error_storage, input_storage, output_storage, orphan_storage)
cutils.run_cthunk(obj) cutils.run_cthunk(obj)
}}}
We would like to get rid of weave dependencies, avoid name conflicts with the support code and have a nicer user interface for the produced module. The proposed new structure is as follows: We would like to get rid of weave dependencies, avoid name conflicts with the support code and have a nicer user interface for the produced module. The proposed new structure is as follows:
{{{ .. code-block:: c
<imports>
<imports>
struct op1 {
<persistent variables> struct op1 {
<support code> <persistent variables>
init() { <initialize persistent fields> } <support code>
cleanup { <clean up persistent fields> } init() { <initialize persistent fields> }
run(<inputs>) { <run the computation for op1> } cleanup { <clean up persistent fields> }
}; run(<inputs>) { <run the computation for op1> }
};
struct op2 { <same> };
... struct op2 { <same> };
struct opN { <ditto> };
struct driver {
op1 o1; op2 o2; ... opN oN;
<input storage>
<output storage>
init(<storage>) { <initialize ops, storage> }
cleanup() { <free storage?> }
run() {
<extract inputs>
o1.run(input1, input2);
o2.run(o1.output1);
... ...
oN.run(...); struct opN { <ditto> };
<sync outputs>
} struct driver {
} op1 o1; op2 o2; ... opN oN;
<input storage>
PyObject* <name>(PyObject* inputs) { <output storage>
<init driver, input/output storage> init(<storage>) { <initialize ops, storage> }
<put inputs in input storage> cleanup() { <free storage?> }
driver.run() run() {
<free input storage> <extract inputs>
<return output storage> o1.run(input1, input2);
} o2.run(o1.output1);
...
PyObject* <name>_driver(PyObject* storage) { oN.run(...);
<init driver with storage> <sync outputs>
<return driver> }
} }
<export <name> and <name>_driver> PyObject* <name>(PyObject* inputs) {
}}} <init driver, input/output storage>
<put inputs in input storage>
driver.run()
<free input storage>
<return output storage>
}
PyObject* <name>_driver(PyObject* storage) {
<init driver with storage>
<return driver>
}
<export <name> and <name>_driver>
Gains: Gains:
* support code can be put inside a struct and become private to the Op * support code can be put inside a struct and become private to the Op
* we can export several functions that can be used directly, eg {{{z = module.add(1, 2)}}} * we can export several functions that can be used directly, eg ``z = module.add(1, 2)``
* this won't do filtering like {{{Result.filter}}} so the usefulness is limited by that * this won't do filtering like ``Result.filter`` so the usefulness is limited by that
* the sequence of operations might be clearer to read * the sequence of operations might be clearer to read
* we can use more descriptive names in each Op struct representing its input names (if we can find them using the inspect module) without worrying about name conflicts * we can use more descriptive names in each Op struct representing its input names (if we can find them using the inspect module) without worrying about name conflicts
...@@ -106,14 +108,15 @@ Losses: ...@@ -106,14 +108,15 @@ Losses:
* make functions static and inline as much as possible * make functions static and inline as much as possible
== Caching == Caching
=======
The current way of caching is from a hash of the generated code. That is inefficient because code has to be generated each time, which might be a costly process. Furthermore, usage of hashing in sets make it difficult to ensure a consistent ordering of Ops in graphs where several orderings are valid, so the generated C code is potentially different each time. Here is a proposal for a better way to compute the hash: The current way of caching is from a hash of the generated code. That is inefficient because code has to be generated each time, which might be a costly process. Furthermore, usage of hashing in sets make it difficult to ensure a consistent ordering of Ops in graphs where several orderings are valid, so the generated C code is potentially different each time. Here is a proposal for a better way to compute the hash:
* Result_hash = Result version + Result desc * Result_hash = Result version + Result desc
* Op_hash = Op version + Op desc + input/output hashes * Op_hash = Op version + Op desc + input/output hashes
* Env_hash = Env version + combination of the Op hashes and their traversal order wrt a consistent traversal method * Env_hash = Env version + combination of the Op hashes and their traversal order wrt a consistent traversal method
The version could be set explicitly via a {{{__version__}}} field or it could simply be equal to the file's last modification date. We could also have a {{{__nocache__}}} field indicating that code produced by the Op or Result cannot be cached. The version could be set explicitly via a ``__version__`` field or it could simply be equal to the file's last modification date. We could also have a ``__nocache__`` field indicating that code produced by the Op or Result cannot be cached.
It should also be easier to bypass the cache (eg an option to CLinker to regenerate the code). It should also be easier to bypass the cache (eg an option to CLinker to regenerate the code).
......
...@@ -62,7 +62,7 @@ What is staticmethod, st_impl? ...@@ -62,7 +62,7 @@ What is staticmethod, st_impl?
implicitly take the class instance as a first argument. Hence, st_impl implicitly take the class instance as a first argument. Hence, st_impl
can be used for Op implementations when no information from the Op can be used for Op implementations when no information from the Op
instance is needed. This can be useful for testing an implementation. instance is needed. This can be useful for testing an implementation.
See :api:`XlogX` for an example. See the ``XlogX`` class below for an example.
**This documentation is useful when we show users how to write Ops. **This documentation is useful when we show users how to write Ops.
Olivier says this behavior should be discouraged but I feel that st_impl Olivier says this behavior should be discouraged but I feel that st_impl
...@@ -72,7 +72,7 @@ should be encouraged where possible.** ...@@ -72,7 +72,7 @@ should be encouraged where possible.**
how do we write scalar ops and upgrade them to tensor ops? how do we write scalar ops and upgrade them to tensor ops?
============================================================ ============================================================
**Olivier says that :api:`XlogX` gives a good example. In fact, I would **Olivier says that** :class:`~theano.tensor.xlogx.XlogX` **gives a good example. In fact, I would
like to beef xlogx up into our running example for demonstrating how to like to beef xlogx up into our running example for demonstrating how to
write an Op:** write an Op:**
......
...@@ -112,8 +112,8 @@ Misc ...@@ -112,8 +112,8 @@ Misc
---- ----
The sparse equivalent of dmatrix is csc_matrix and csr_matrix. The sparse equivalent of dmatrix is csc_matrix and csr_matrix.
:api:`Dot` vs. :api:`StructuredDot` :class:`~theano.sparse.basic.Dot` vs. :class:`~theano.sparse.basic.StructuredDot`
---------------------------------------- ---------------------------------------------------------------------------------
Often when you use a sparse matrix it is because there is a meaning to the Often when you use a sparse matrix it is because there is a meaning to the
structure of non-zeros. The gradient on terms outside that structure structure of non-zeros. The gradient on terms outside that structure
......
...@@ -73,19 +73,9 @@ if __name__ == '__main__': ...@@ -73,19 +73,9 @@ if __name__ == '__main__':
print ' --help: this help' print ' --help: this help'
sys.exit(0) sys.exit(0)
options['--all'] = not (bool(options['--epydoc']) ^ bool(options['--rst'])) if not (options['--epydoc'] or options['--rst']):
# Default is now rst
if 0: options['--rst'] = True
import gen_oplist
print 'Generating oplist...'
gen_oplist.print_file(open('%s/doc/indexes/oplist.txt' % throot, 'w'))
print 'oplist done!'
if 0:
import gen_typelist
print 'Generating typelist...'
gen_typelist.print_file(open('%s/doc/indexes/typelist.txt' % throot, 'w'))
print 'typelist done!'
def mkdir(path): def mkdir(path):
try: try:
...@@ -96,8 +86,6 @@ if __name__ == '__main__': ...@@ -96,8 +86,6 @@ if __name__ == '__main__':
outdir = options['-o'] or (throot + '/html') outdir = options['-o'] or (throot + '/html')
mkdir(outdir) mkdir(outdir)
os.chdir(outdir) os.chdir(outdir)
mkdir("doc")
mkdir("api")
# Make sure the appropriate 'theano' directory is in the PYTHONPATH # Make sure the appropriate 'theano' directory is in the PYTHONPATH
pythonpath = os.environ.get('PYTHONPATH', '') pythonpath = os.environ.get('PYTHONPATH', '')
...@@ -105,6 +93,7 @@ if __name__ == '__main__': ...@@ -105,6 +93,7 @@ if __name__ == '__main__':
os.environ['PYTHONPATH'] = pythonpath os.environ['PYTHONPATH'] = pythonpath
if options['--all'] or options['--epydoc']: if options['--all'] or options['--epydoc']:
mkdir("api")
from epydoc.cli import cli from epydoc.cli import cli
sys.path[0:0] = [throot] sys.path[0:0] = [throot]
...@@ -120,6 +109,7 @@ if __name__ == '__main__': ...@@ -120,6 +109,7 @@ if __name__ == '__main__':
# TODO # TODO
if options['--all'] or options['--rst']: if options['--all'] or options['--rst']:
mkdir("doc")
import sphinx import sphinx
sys.path[0:0] = [os.path.join(throot, 'doc')] sys.path[0:0] = [os.path.join(throot, 'doc')]
sphinx.main(['', '-E', os.path.join(throot, 'doc'), '.']) sphinx.main(['', '-E', os.path.join(throot, 'doc'), '.'])
......
"""script to generate doc/oplist.txt, which compiles to :doc:`oplist`. """
__docformat__ = "restructuredtext en"
import sys, os
throot = "/".join(sys.path[0].split("/")[:-2])
sys.path[0:0] = [throot]
from theano import gof
def print_title(file, title_string, under_char, over_char=''):
l = len(title_string)
if over_char:
print >>file, over_char * l
print >>file, title_string
if under_char:
print >>file, under_char * l
print >>file, ""
def print_hline(file):
print >>file, '-' * 80
class Entry:
"""Structure for generating the oplist file"""
symbol = None
name = None
module = None
docstring = None
tags = []
def __init__(self, symbol, name, current_module):
self.symbol = symbol
self.name = name
self.module = symbol.__module__ # current_module.__name__ # symbol.__module__
self.docstring = symbol.__doc__
self.tags = ['%s' % current_module.__name__] + getattr(symbol, '__oplist_tags', [])
def mini_desc(self, maxlen=50):
"""Return a short description of the op"""
def chomp(s):
"""interpret and left-align a docstring"""
if 'subtensor' in s:
debug = 0
else:
debug = 0
r = []
leadspace = True
for c in s:
if leadspace and c in ' \n\t':
continue
else:
leadspace = False
if c == '\n':
if debug:
print >> sys.stderr, 'breaking'
break
if c in '\t*`':
c = ' ';
r.append(c)
if debug:
print >> sys.stderr, r
return "".join(r)
minmax = 5
assert maxlen >= minmax
if not self.docstring:
return "" #+ '(no doc)'
elif len(self.docstring) < maxlen:
return chomp(self.docstring)
else:
return "%s ..."% chomp(self.docstring[:maxlen-minmax])
apilink = property(lambda self: ":api:`%s <%s.%s>`"% (self.name, self.module, self.name))
"""Return the ReST link into the epydoc of this symbol"""
class EntryOp(Entry):
def __init__(self, symbol, *args):
has_perform = hasattr(symbol, 'perform')
if symbol is gof.Op:
raise TypeError('not an Op subclass')
if not issubclass(symbol, gof.Op):
raise TypeError('not an Op subclass')
Entry.__init__(self, symbol, *args)
class EntryConstructor(Entry):
def __init__(self, symbol, name, module):
is_op = isinstance(symbol, gof.Op)
is_ctor = symbol in getattr(module, '__oplist_constructor_list', [])
if not (is_op or is_ctor):
raise TypeError('not a constructor', symbol)
Entry.__init__(self, symbol, name, module)
def search_entries(module_list, ops = None, constructors = None, seen = None):
if ops is None: ops = []
if constructors is None: constructors = []
if seen is None: seen = set()
modules = []
for module in module_list:
symbol_name_list = [s for s in dir(module) if not s[0] == '_']
for symbol_name in symbol_name_list:
symbol = getattr(module, symbol_name)
try:
if symbol in seen:
continue
seen.add(symbol)
except TypeError:
pass
if type(symbol) == type(module): # module
modules.append(symbol)
try:
ops.append(EntryOp(symbol, symbol_name, module))
except TypeError:
try:
constructors.append(EntryConstructor(symbol, symbol_name, module))
except TypeError:
pass
for symbol in modules:
search_entries([symbol], ops, constructors, seen)
return ops, constructors
def print_entries(file, ops, constructors):
tags = {}
for o in ops + constructors:
for t in o.tags:
tags.setdefault(t, []).append(o)
for t in tags:
print_title(file, t, '=')
tagged_ops = [op for op in tags[t] if isinstance(op, EntryOp)]
if len(tagged_ops):
print_title(file, 'Op Classes', '-')
for op in tagged_ops:
print >>file, "- %s" % op.apilink
print >>file, " %s" % op.mini_desc()
print >>file, ""
tagged_ops = [op for op in tags[t] if isinstance(op, EntryConstructor)]
if len(tagged_ops):
print_title(file, 'Op Constructors', '-')
for op in tagged_ops:
print >>file, "- %s" % op.apilink
print >>file, " %s" % op.mini_desc()
print >>file, ""
def print_file(file):
print >>file, '.. _oplist:\n\n'
print_title(file, "Op List", "~", "~")
print >>file, """
This page lists the `Op Classes` and `constructors` that are provided by the Theano library.
`Op Classes` drive from :api:`Op`, whereas `constructors` are typically `Op Class` instances, but may be true Python functions.
In the future, this list may distinguish `constructors` that are Op instances from true Python functions.
"""
print_hline(file)
print >>file, ""
print >>file, ".. contents:: "
print >>file, ""
ops, constructors = search_entries([theano])
print_entries(file, ops, constructors)
print >>file, ""
import theano
if __name__ == "__main__":
"""Generate the op list"""
if len(sys.argv) >= 2:
file = open(sys.argv[1], 'w')
else:
file = sys.stdout
print_file(file)
from gen_oplist import print_title, print_hline
def print_file(file):
print >>file, '.. _typelist:\n\n'
print_title(file, "Type List", "~", "~")
print >>file, "*THIS PAGE IS A PLACEHOLDER: WRITEME*"
print >>file, ""
print_hline(file)
print >>file, ""
print >>file, ".. contents::"
print >>file, ""
print_title(file, "Type Classes", '=')
print >>file, "- scalar.Scalar\n"
print >>file, "- tensor.Tensor\n"
print >>file, "- sparse.Sparse\n"
print_title(file, "Type Instances", '=')
print >>file, "- scalar.int8\n"
print >>file, "- tensor.lvector\n"
print >>file, "- sparse.??\n"
print >>file, ""
if __name__ == '__main__':
if len(sys.argv) >= 2:
file = open(sys.argv[1], 'w')
else:
file = sys.stdout
print_file(file)
...@@ -174,7 +174,8 @@ internal state, and returns the old state value. ...@@ -174,7 +174,8 @@ internal state, and returns the old state value.
>>> accumulator = function([inc], state, updates=[(state, state+inc)]) >>> accumulator = function([inc], state, updates=[(state, state+inc)])
This code introduces a few new concepts. The ``shared`` function constructs This code introduces a few new concepts. The ``shared`` function constructs
so-called :term:`shared variables`. These are hybrid symbolic and non-symbolic so-called :term:`shared variables <shared variable>`.
These are hybrid symbolic and non-symbolic
variables. Shared variables can be used in symbolic expressions just like variables. Shared variables can be used in symbolic expressions just like
the objects returned by ``dmatrices(...)`` but they also have an internal the objects returned by ``dmatrices(...)`` but they also have an internal
value, that defines the value taken by this symbolic variable in *all* the value, that defines the value taken by this symbolic variable in *all* the
......
...@@ -343,10 +343,10 @@ class Method(Component): ...@@ -343,10 +343,10 @@ class Method(Component):
"""Compile a function for this Method. """Compile a function for this Method.
:param allocate_all: if True, storage will be :param allocate_all: if True, storage will be
allocated for all needed Variables even if there is no allocated for all needed Variables even if there is no
associated storage for them in the memo. If allocate_all is associated storage for them in the memo. If allocate_all is
False, storage will only be allocated for Variables that are False, storage will only be allocated for Variables that are
reachable from the inputs list. reachable from the inputs list.
:returns: a function that implements this method :returns: a function that implements this method
:rtype: `Function` instance :rtype: `Function` instance
...@@ -593,7 +593,8 @@ class Composite(Component): ...@@ -593,7 +593,8 @@ class Composite(Component):
""" """
Generator that yields (path, component) pairs in a flattened Generator that yields (path, component) pairs in a flattened
hierarchy of composites and components, where path is a hierarchy of composites and components, where path is a
sequence of keys such that sequence of keys such that::
component is self[path[0]][path[1]]... component is self[path[0]][path[1]]...
If include_self is True, the list will include the Composite If include_self is True, the list will include the Composite
...@@ -865,12 +866,13 @@ def register_wrapper(condition, wrapper): ...@@ -865,12 +866,13 @@ def register_wrapper(condition, wrapper):
""" """
:type condition: function x -> bool :type condition: function x -> bool
:param condition: this function should return True iff `wrapper` can sensibly turn x into a :param condition: this function should return True iff `wrapper` can
Component. sensibly turn x into a Component.
:type wrapper: function x -> Component :type wrapper: function x -> Component
:param wrapper: this function should convert `x` into an instance of a Component subclass. :param wrapper: this function should convert `x` into an instance of
a Component subclass.
""" """
__autowrappers.append((condition, wrapper)) __autowrappers.append((condition, wrapper))
...@@ -947,8 +949,8 @@ class ModuleInstance(ComponentDictInstanceNoInit): ...@@ -947,8 +949,8 @@ class ModuleInstance(ComponentDictInstanceNoInit):
WRITEME WRITEME
:note: ModuleInstance is meant to be instantiated by Module. This differs :note: ModuleInstance is meant to be instantiated by Module. This differs
from ComponentDictInstance on a key point, which is that getattr from ComponentDictInstance on a key point, which is that getattr
does a similar thing to getitem. does a similar thing to getitem.
:note: ModuleInstance is compatible for use as ComponentDict.InstanceType. :note: ModuleInstance is compatible for use as ComponentDict.InstanceType.
""" """
...@@ -1169,7 +1171,8 @@ FancyModuleInstance = ModuleInstance ...@@ -1169,7 +1171,8 @@ FancyModuleInstance = ModuleInstance
def func_to_mod(f): def func_to_mod(f):
""" """
Creates a dummy module, with external member variables for the input Creates a dummy module, with external member variables for the input
parameters required by the function f, and a member output defined as: parameters required by the function f, and a member output defined as::
output <= f(**kwinit) output <= f(**kwinit)
""" """
def make(**kwinit): def make(**kwinit):
......
...@@ -223,10 +223,9 @@ class Variable(utils.object2): ...@@ -223,10 +223,9 @@ class Variable(utils.object2):
A Variable which is the output of a symbolic computation will have an owner != None. A Variable which is the output of a symbolic computation will have an owner != None.
Code Example **Code Example**
============
.. python:: .. code-block:: python
import theano import theano
from theano import tensor from theano import tensor
......
...@@ -59,17 +59,19 @@ class CLinkerType(CLinkerObject): ...@@ -59,17 +59,19 @@ class CLinkerType(CLinkerObject):
:type name: string :type name: string
:param sub: a dictionary of special codes. Most importantly sub['fail']. See CLinker :param sub: a dictionary of special codes. Most importantly
for more info on `sub` and ``fail``. sub['fail']. See CLinker for more info on `sub` and ``fail``.
:type sub: dict string -> string :type sub: dict string -> string
:note: It is important to include the `name` inside of variables which are declared :note: It is important to include the `name` inside of variables which
here, so that name collisions do not occur in the source file that is generated. are declared here, so that name collisions do not occur in the
source file that is generated.
:note: The variable called ``name`` is not necessarily defined yet where this code is :note: The variable called ``name`` is not necessarily defined yet
inserted. This code might be inserted to create class variables for example, whereas where this code is inserted. This code might be inserted to
the variable ``name`` might only exist inside certain functions in that class. create class variables for example, whereas the variable ``name``
might only exist inside certain functions in that class.
:todo: Why should variable declaration fail? Is it even allowed to? :todo: Why should variable declaration fail? Is it even allowed to?
...@@ -87,9 +89,10 @@ class CLinkerType(CLinkerObject): ...@@ -87,9 +89,10 @@ class CLinkerType(CLinkerObject):
return "addr_of_%(name)s = NULL;" return "addr_of_%(name)s = NULL;"
:note: The variable called ``name`` is not necessarily defined yet where this code is :note: The variable called ``name`` is not necessarily defined yet
inserted. This code might be inserted in a class constructor for example, whereas where this code is inserted. This code might be inserted in a
the variable ``name`` might only exist inside certain functions in that class. class constructor for example, whereas the variable ``name``
might only exist inside certain functions in that class.
:todo: Why should variable initialization fail? Is it even allowed to? :todo: Why should variable initialization fail? Is it even allowed to?
""" """
...@@ -107,7 +110,7 @@ class CLinkerType(CLinkerObject): ...@@ -107,7 +110,7 @@ class CLinkerType(CLinkerObject):
exception and insert "%(fail)s". exception and insert "%(fail)s".
:todo: Point out that template filling (via sub) is now performed :todo: Point out that template filling (via sub) is now performed
by this function. --jpt by this function. --jpt
Example: Example:
...@@ -116,15 +119,17 @@ class CLinkerType(CLinkerObject): ...@@ -116,15 +119,17 @@ class CLinkerType(CLinkerObject):
return "if (py_%(name)s == Py_None)" + \\\ return "if (py_%(name)s == Py_None)" + \\\
addr_of_%(name)s = &py_%(name)s;" + \\\ addr_of_%(name)s = &py_%(name)s;" + \\\
"else" + \\\ "else" + \\\
{ PyErr_SetString(PyExc_ValueError, 'was expecting None'); %(fail)s;}" { PyErr_SetString(PyExc_ValueError, \\\
'was expecting None'); %(fail)s;}"
:param name: the name of the ``PyObject *`` pointer that will the value for this Type :param name: the name of the ``PyObject *`` pointer that will
store the value for this Type
:type name: string :type name: string
:param sub: a dictionary of special codes. Most importantly sub['fail']. See CLinker :param sub: a dictionary of special codes. Most importantly
for more info on `sub` and ``fail``. sub['fail']. See CLinker for more info on `sub` and ``fail``.
:type sub: dict string -> string :type sub: dict string -> string
...@@ -340,7 +345,8 @@ class Type(object2, PureType, CLinkerType): ...@@ -340,7 +345,8 @@ class Type(object2, PureType, CLinkerType):
The following following code illustrates the use of a Type instance, here tensor.fvector: The following following code illustrates the use of a Type instance, here tensor.fvector:
.. python:: .. code-block:: python
# Declare a symbolic floating-point vector using __call__ # Declare a symbolic floating-point vector using __call__
b = tensor.fvector() b = tensor.fvector()
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论