提交 690d3628 authored 作者: abergeron's avatar abergeron

Merge pull request #3301 from harlouci/numpydoc_compile

Numpydoc compile
......@@ -10,10 +10,11 @@ from functools import reduce
class OpFromGraph(gof.Op):
"""This creates an `Op` from inputs and outputs lists of variables.
"""
This creates an `Op` from inputs and outputs lists of variables.
The signature is similar to theano.function() and the resulting
`Op`'s perform will do the same operation as::
`Op`'s perform will do the same operation as:
orig_function(inputs, outputs, **kwargs)
......@@ -31,11 +32,15 @@ class OpFromGraph(gof.Op):
- Add support to pickle this Op.
- Add support/test with random generator
:note:
- We support shared variables in the inner graph. This is automatic and
invisible to the user. They can be as input to the node or in the
inner graph.
- We support unused inputs. This is needed for the grad.
Notes
-----
- We support shared variables in the inner graph. This is automatic and
invisible to the user. They can be as input to the node or in the
inner graph.
- We support unused inputs. This is needed for the grad.
Examples
--------
Example 1:
......@@ -49,8 +54,6 @@ class OpFromGraph(gof.Op):
e2 = op(x, y, z) + op(z, y, x)
fn = function([x, y, z], [e2])
Example 2 with shared variable:
.. code-block:: python
......@@ -139,7 +142,8 @@ class OpFromGraph(gof.Op):
def connection_pattern(self, node):
"""
Return connection pattern of subfgraph defined by inputs and outputs
Return connection pattern of subfgraph defined by inputs and outputs.
"""
return io_connection_pattern(self.new_inputs, self.new_outputs)
......
"""Provides `DebugMode`, an evaluation mode for debugging theano internals.
"""
Provides `DebugMode`, an evaluation mode for debugging theano internals.
:TODO: add support for IfElse Op, LazyLinker, PureOp, etc.
TODO: add support for IfElse Op, LazyLinker, PureOp, etc.
"""
from __future__ import print_function
......@@ -123,7 +124,11 @@ _logger.addFilter(NoDuplicateOptWarningFilter())
#
########################
class DebugModeError(Exception):
"""Generic Exception raised to indicate an internal theano problem"""
"""
Generic Exception raised to indicate an internal theano problem.
"""
pass
......@@ -135,21 +140,30 @@ class BadThunkOutput(DebugModeError):
do not agree, or if one of these methods do not give the same result
when called twice with the same inputs (but different memory layouts
for the output).
"""
r = None
"""The `Variable` instance for which conflicting values were computed"""
"""
The `Variable` instance for which conflicting values were computed.
"""
thunk1 = ''
val1 = None
"""The value computed by `thunk1`"""
"""
The value computed by `thunk1`.
"""
thunk2 = ''
val2 = None
"""The value computed by `thunk2`"""
"""
The value computed by `thunk2`.
"""
def __init__(self, r, thunk1, val1, thunk2, val2, inputs_val=()):
"""Initialize members"""
super(BadThunkOutput, self).__init__()
self.r = r
self.thunk1 = thunk1
......@@ -159,16 +173,22 @@ class BadThunkOutput(DebugModeError):
self.inputs_val = inputs_val
def offending_op(self):
"""Return the Op class whose c_code and perform
implementations didn't match"""
"""
Return the Op class whose c_code and perform implementations
didn't match.
"""
return type(self.r.owner.op)
def __str__(self):
return self.str_diagnostic()
def str_diagnostic(self):
"""Return a pretty multiline string representating the cause
of the exception"""
"""
Return a pretty multiline string representing the cause of
the exception.
"""
sio = StringIO()
print("BadThunkOutput", file=sio)
print(" Apply :", self.r.owner, file=sio)
......@@ -202,41 +222,61 @@ class BadThunkOutput(DebugModeError):
class BadOptimization(DebugModeError):
"""Exception: some variable and its substitute take different
runtime values.
"""
Exception: some variable and its substitute take different runtime values.
"""
new_r = None
"""A `Variable` instance that took a different value from `old_r`,
but which replaced `old_r`."""
"""
A `Variable` instance that took a different value from `old_r`,
but which replaced `old_r`.
"""
old_r = None
"""A `Variable` instance that was replaced by `new_r`."""
"""
A `Variable` instance that was replaced by `new_r`.
"""
old_r_val = None
"""The value computed for `old_r`."""
"""
The value computed for `old_r`.
"""
new_r_val = None
"""The value computed for `new_r`."""
"""
The value computed for `new_r`.
"""
reason = None
"""An object that indicates why old_r was turned into new_r.
"""
An object that indicates why old_r was turned into new_r.
Convention is that this is the name of the optimization that
requested the replacement.
"""
old_graph = ""
"""A multiline string representation of the graph leading to
old_r, at the time of the replacement."""
"""
A multiline string representation of the graph leading to
old_r, at the time of the replacement.
"""
new_graph = ""
"""A multiline string representation of the graph leading to
new_r, at the time of the replacement."""
"""
A multiline string representation of the graph leading to
new_r, at the time of the replacement.
"""
def __init__(self, old_r, new_r, old_r_val, new_r_val, reason,
old_graph, new_graph):
"""Initialize members"""
super(BadOptimization, self).__init__()
self.old_r = old_r
self.new_r = new_r
......@@ -250,8 +290,11 @@ class BadOptimization(DebugModeError):
return self.str_diagnostic()
def str_diagnostic(self):
"""Return a pretty multiline string representating the cause
of the exception"""
"""
Return a pretty multiline string representating the cause
of the exception.
"""
sio = StringIO()
val_str_len_limit = 800
print("BadOptimization Error", super(BadOptimization,
......@@ -340,8 +383,11 @@ class BadOptimization(DebugModeError):
class BadDestroyMap(DebugModeError):
"""Exception: Some perform() or c_code() modified an input that
wasn't in the destroy_map"""
"""
Exception: Some perform() or c_code() modified an input that
wasn't in the destroy_map.
"""
def __init__(self, node, idx, old_val, new_val, perform):
super(BadDestroyMap, self).__init__()
self.node = node
......@@ -395,8 +441,12 @@ class BadDestroyMap(DebugModeError):
class BadViewMap(DebugModeError):
"""Exception: Some perform() or c_code() created a memory alias
that wasn't in the view_map"""
"""
Exception: Some perform() or c_code() created a memory alias
that wasn't in the view_map.
"""
def __init__(self, node, output_idx, out_storage,
in_alias_idx=None, out_alias_idx=None):
super(BadViewMap, self).__init__()
......@@ -426,7 +476,8 @@ class BadViewMap(DebugModeError):
class StochasticOrder(DebugModeError):
"""Exception: Repeated Optimizations of the same graph do not give
"""
Exception: Repeated Optimizations of the same graph do not give
identical results.
The most common cause is that an Optimization iterates over some
......@@ -440,8 +491,12 @@ class StochasticOrder(DebugModeError):
class InvalidValueError(DebugModeError):
"""Exception: some Op an output value that is inconsistent with
the Type of that output"""
"""
Exception: some Op an output value that is inconsistent with
the Type of that output.
"""
def __init__(self, r, v, client_node=None, hint='none',
specific_hint='none'):
super(InvalidValueError, self).__init__()
......@@ -498,8 +553,11 @@ class InvalidValueError(DebugModeError):
def char_from_number(number):
""" Converts number to string by rendering it in base 26 using
capital letters as digits """
"""
Converts number to string by rendering it in base 26 using
capital letters as digits.
"""
base = 26
......@@ -523,31 +581,45 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
stop_on_name=False, prefix_child=None,
scan_ops=None, profile=None,
scan_inner_to_outer_inputs=None):
"""Print the graph leading to `r` to given depth.
:param r: Variable instance
:param prefix: prefix to each line (typically some number of spaces)
:param depth: maximum recursion depth (Default -1 for unlimited).
:param done: dict of Apply instances that have already been printed
and their associated printed ids
:param print_type: whether to print the Variable type after the other infos
:param file: file-like object to which to print
:param print_destroy_map: whether to print the op destroy_map after
other info
:param print_view_map: whether to print the op view_map after other info
:param order: If not empty will print the index in the toposort.
:param ids: How do we print the identifier of the variable
id - print the python id value
int - print integer character
CHAR - print capital character
"" - don't print an identifier
:param stop_on_name: When True, if a node in the graph has a name,
we don't print anything below it.
:param scan_ops: Scan ops in the graph will be added inside this list
for later printing purposes.
:param scan_inner_to_outer_inputs: a dictionary mapping a scan ops
inner function inputs to the scan op inputs (outer inputs) for
printing purposes.
"""
Print the graph leading to `r` to given depth.
Parameters
----------
r
Variable instance.
prefix
Prefix to each line (typically some number of spaces).
depth
Maximum recursion depth (Default -1 for unlimited).
done
dict of Apply instances that have already been printed and their
associated printed ids.
print_type
Whether to print the Variable type after the other infos.
file
File-like object to which to print.
print_destroy_map
Whether to print the op destroy_map after other info.
print_view_map
Whether to print the op view_map after other info.
order
If not empty will print the index in the toposort.
ids
How do we print the identifier of the variable :
id - print the python id value,
int - print integer character,
CHAR - print capital character,
"" - don't print an identifier.
stop_on_name
When True, if a node in the graph has a name, we don't print anything
below it.
scan_ops
Scan ops in the graph will be added inside this list for later printing
purposes.
scan_inner_to_outer_inputs
A dictionary mapping a scan ops inner function inputs to the scan op
inputs (outer inputs) for printing purposes.
"""
if depth == 0:
......@@ -712,17 +784,24 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
def _optcheck_fgraph(input_specs, output_specs, accept_inplace=False):
"""Create an FunctionGraph for debugging.
:param input_specs: fgraph inputs
:type input_specs: WRITEME
:param output_specs: fgraph outputs
:type output_specs: WRITEME
:param accept_inplace: are inplace ops permitted in the original graph?
:type accept_inplace: Bool
:rtype: `FunctionGraph`
:returns: a new FunctionGraph with a cloned graph, with debugging
`Feature` instances already installed.
"""
Create a FunctionGraph for debugging.
Parameters
----------
input_specs: WRITEME
fgraph inputs.
output_specs: WRITEME
fgraph outputs.
accept_inplace : bool
Are inplace ops permitted in the original graph?
Returns
-------
FunctionGraph
A new FunctionGraph with a cloned graph, with debugging `Feature`
instances already installed.
"""
orig_inputs = [spec.variable for spec in input_specs]
updates = [spec.update for spec in input_specs if spec.update]
......@@ -784,7 +863,8 @@ def check_eq(var, val1, val2):
def _check_inputs(node, storage_map, r_vals, dr_vals, active_nodes,
clobber_dr_vals=True,
perform=None, warn_input_not_reused=True):
"""Raise BadDestroyMap if necessary, update dr_vals
"""
Raise BadDestroyMap if necessary, update dr_vals.
Returns a list of output variables that actually worked inplace
(their value is aliased to the value of at least one input).
......@@ -871,10 +951,11 @@ def _check_viewmap(node, storage_map):
"""
This functions raises a BadViewMap exception when it detects the
following:
- output node storages aliased to input storage, with no declaration
in view_map
- if not aliased to an input, check if two outputs are aliased together
and used subsequently in the graph
- Output node storages aliased to input storage, with no declaration
in view_map.
- If not aliased to an input, check if two outputs are aliased together
and used subsequently in the graph.
"""
for oi, onode in enumerate(node.outputs):
......@@ -937,14 +1018,24 @@ def _check_viewmap(node, storage_map):
def _is_used_in_graph(var):
"""
Returns True if `var` is used by another node in the graph
Returns
-------
bool
True if `var` is used by another node in the graph.
"""
return not(var.clients == [('output', 1)] or var.clients == [])
def _check_strides_match(a, b, warn_err, op):
"""
param: warn_err: if 0, no warning, if 1 warning, if 2 error
Parameters
----------
warn_err
If 0, no warning, if 1 warning, if 2 error.
"""
if warn_err == 0:
return
......@@ -965,12 +1056,20 @@ def _check_strides_match(a, b, warn_err, op):
def _lessbroken_deepcopy(a):
"""
:param a: any object
Returns a copy of `a` that shares no internal storage with the original
(a deep copy).
This function handles numpy arrays specially, because copy.deepcopy()
called on a 0-d array will return a numpy scalar, not an array.
Parameters
----------
a
Any object
Returns
-------
object
A copy of `a` that shares no internal storage with the original
(a deep copy). This function handles numpy arrays specially, because
copy.deepcopy() called on a 0-d array will return a numpy scalar,
not an array.
"""
# this exists because copy.deepcopy on numpy arrays is broken
# This logic is also in link.py
......@@ -990,13 +1089,15 @@ def _lessbroken_deepcopy(a):
def _find_bad_optimizations0(order, reasons, r_vals):
"""Use a simple algorithm to find broken optimizations.
"""
Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's
a problem it identifies the wrong optimization as the culprit.
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
# iterate over variables looking for values that don't match the
# values of the variables they replaced. This is the sign of a
......@@ -1078,19 +1179,24 @@ def _find_bad_optimizations1(order, reasons, r_vals):
def _find_bad_optimizations2(order, reasons, r_vals):
"""Use a simple algorithm to find broken optimizations.
"""
Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's
a problem it identifies the wrong optimization as the culprit.
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
checked_variables = set()
def check_variable_norec(new_r):
"""Verify that `r` has the same value as the results it replaces """
"""
Verify that `r` has the same value as the results it replaces.
"""
for reason, r, old_graph_str, new_graph_str in reasons[new_r]:
new_r_val = r_vals[new_r]
r_val = r_vals[r]
......@@ -1134,7 +1240,10 @@ _find_bad_optimizations = _find_bad_optimizations0
def _get_preallocated_maps(node, thunk, prealloc_modes, def_val,
storage_map, r_vals, dr_vals, perform,
active_order_set, inplace_outs, init_outputs):
'''Preallocate outputs in different memory layouts'''
"""
Preallocate outputs in different memory layouts.
"""
# To avoid circular imports
from theano.tensor import TensorType
......@@ -1357,7 +1466,10 @@ def _get_preallocated_maps(node, thunk, prealloc_modes, def_val,
def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
storage_map, r_vals, dr_vals, perform,
active_order_set, inplace_outs, init_outputs):
'''Try to apply thunk() on different output storages'''
"""
Try to apply thunk() on different output storages.
"""
# If node has an inner compiled Theano function with mode DebugMode,
# disable memory checks in that mode, since they were already run.
......@@ -1460,26 +1572,40 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
class _FunctionGraphEvent(object):
"""A record of an event in the life of an FunctionGraph.
"""
A record of an event in the life of an FunctionGraph.
The __eq__ function is important here, as it is the basis for
comparing optimization runs.
"""
kind = ""
"""One of 'import', 'change', 'prune'"""
"""
One of 'import', 'change', 'prune'.
"""
node = None
"""Either 'output' or an Apply instance"""
"""
Either 'output' or an Apply instance.
"""
op = None
"""Either 'output' or an Op instance"""
idx = None
"""change events involve an position index of the input variable"""
"""
Change events involve an position index of the input variable.
"""
reason = None
"""change events sometimes have a reason"""
"""
Change events sometimes have a reason.
"""
def __init__(self, kind, node, idx=None, reason=None):
self.kind = kind
......@@ -1522,8 +1648,11 @@ class _FunctionGraphEvent(object):
class _VariableEquivalenceTracker(object):
"""A FunctionGraph Feature that keeps tabs on an FunctionGraph and
tries to detect problems."""
"""
A FunctionGraph Feature that keeps tabs on an FunctionGraph and
tries to detect problems.
"""
fgraph = None
"""WRITEME"""
......@@ -1675,7 +1804,11 @@ class _DummyLinker(object):
class _Linker(gof.link.LocalLinker):
"""Special debugging linker"""
"""
Special debugging linker.
"""
def __init__(self, maker, schedule=None):
super(gof.LocalLinker, self).__init__()
self.fgraph = None
......@@ -2236,11 +2369,39 @@ _NODEFAULT = ['NODEFAULT']
class _Maker(FunctionMaker): # inheritance buys a few helper functions
"""Special debugging FunctionMaker
"""
Special debugging FunctionMaker.
Parameters
----------
inputs : list of SymbolicInput instances
outputs : list of SymbolicOutput instances
Outputs may also be a single Variable (not a list), in which case
the functions produced by FunctionMaker will return their output
value directly.
accept_inplace
True iff it is acceptable to have inplace operations in the graph from
the inputs to the outputs.
on_unused_input
What to do if a variable in the 'inputs' list is not used in the
graph. Possible values are 'raise', 'warn' and 'ignore'.
output_keys
If the outputs argument for theano.function was a list, then
output_keys is None. If the outputs argument was a dict, then
output_keys is a sorted list of the keys from that dict.
Notes
-----
The constructor sets TensorType.filter_checks_isfinite when
`mode.check_isfinite` is True.
"""
verbose = 0
"""Verbosity level of compile-time and run-time checks. (Default
0: silent)"""
"""
Verbosity level of compile-time and run-time checks. (Default 0: silent).
"""
def __init__(self, inputs, outputs, optimizer, mode,
accept_inplace=False,
......@@ -2248,33 +2409,6 @@ class _Maker(FunctionMaker): # inheritance buys a few helper functions
profile=None,
on_unused_input=None,
output_keys=None):
"""
:type inputs: a list of SymbolicInput instances
:type outputs: a list of SymbolicOutput instances outputs may
also be a single Variable (not a list), in
which case the functions produced by
FunctionMaker will return their output value
directly
:param accept_inplace: True iff it is acceptable to have
inplace operations in the graph from the inputs to
the outputs
:param on_unused_input: What to do if a variable in the
'inputs' list is not used in the
graph. Possible values are 'raise',
'warn', and 'ignore'.
:param output_keys: If the outputs argument for
theano.function was a list, then
output_keys is None. If the outputs
argument was a dict, then output_keys is a
sorted list of the keys from that dict.
:note: this function sets TensorType.filter_checks_isfinite
when `mode.check_isfinite` is True
"""
self.profile = profile
# Handle the case where inputs and/or outputs is a single
# Variable (not in a list)
......@@ -2395,11 +2529,15 @@ class _Maker(FunctionMaker): # inheritance buys a few helper functions
"""
Create a function.
defaults -> a list matching the inputs list and providing default
values if the default for an input is None, then that input
is a required input. For an input with an update, the
default acts as initialization.
trustme -> disables some exceptions, used internally
Parameters
----------
defaults
A list matching the inputs list and providing default values if the
default for an input is None, then that input is a required input.
For an input with an update, the default acts as initialization.
trustme
Disables some exceptions, used internally.
"""
if defaults is None:
defaults = [None] * len(self.inputs)
......@@ -2514,35 +2652,40 @@ copyreg.pickle(_Maker, _pickle_DebugMode_Maker)
class DebugMode(Mode):
"""Evaluation Mode that detects internal theano errors.
"""
Evaluation Mode that detects internal theano errors.
This mode catches several kinds of internal error:
- inconsistent outputs when calling the same Op twice with the same
- Inconsistent outputs when calling the same Op twice with the same
inputs, for instance if c_code and perform implementations, are
inconsistent, or in case of incorrect handling of output memory
(see `BadThunkOutput`),
(see `BadThunkOutput`).
- a variable replacing another when their runtime values don't
- A variable replacing another when their runtime values don't
match. This is a symptom of an incorrect optimization step, or
faulty Op implementation (raises `BadOptimization`)
faulty Op implementation (raises `BadOptimization`).
- stochastic optimization ordering (raises `StochasticOrder`)
- Stochastic optimization ordering (raises `StochasticOrder`).
- incomplete `destroy_map` specification (raises `BadDestroyMap`)
- Incomplete `destroy_map` specification (raises `BadDestroyMap`).
- an op that returns an illegal value not matching the output
Variable Type (raises InvalidValueError)
- An op that returns an illegal value not matching the output
Variable Type (raises InvalidValueError).
Each of these exceptions inherits from the more generic `DebugModeError`.
If there are no internal errors, this mode behaves like FAST_RUN
or FAST_COMPILE, but takes a little longer and uses more memory.
If there are internal errors, this mode will raise an
`DebugModeError` exception.
Raises
------
DebugModeError
If there are internal errors.
:remark: The work of debugging is implemented by the `_Maker`, `_Linker`,
Notes
-----
The work of debugging is implemented by the `_Maker`, `_Linker`,
and `_VariableEquivalenceTracker` classes.
"""
......@@ -2551,22 +2694,26 @@ class DebugMode(Mode):
"""
When checking for the stability of optimization, recompile the
graph this many times.
"""
check_c_code = config.DebugMode.check_c
"""
Should we evaluate (and check) the `c_code` implementations?
"""
check_py_code = config.DebugMode.check_py
"""
Should we evaluate (and check) the `perform` implementations?
Always checked if no `c_code`.
"""
check_isfinite = config.DebugMode.check_finite
"""
Should we check for (and complain about) NaN/Inf ndarray elements?
"""
require_matching_strides = config.DebugMode.check_strides
......@@ -2574,6 +2721,7 @@ class DebugMode(Mode):
Should we check for (and complain about) Ops whose python and C
outputs are ndarrays with different strides? (This can catch bugs,
but is generally overly strict.) 0 no check, 1 warn, 2 err.
"""
check_preallocated_output = config.DebugMode.check_preallocated_output
......@@ -2584,13 +2732,15 @@ class DebugMode(Mode):
"c_contiguous", "f_contiguous", "strided" (positive and negative
strides), "wrong_size" (larger and smaller dimensions), and "ALL"
(all of the above).
"""
# This function will be used to create a FunctionMaker in
# function_module.function
def function_maker(self, i, o, m, *args, **kwargs):
"""
Return an instance of `_Maker` which handles much of the debugging work
Return an instance of `_Maker` which handles much of the debugging work.
"""
assert m is self
return _Maker(i, o, self.optimizer, self, *args, **kwargs)
......@@ -2604,12 +2754,11 @@ class DebugMode(Mode):
check_preallocated_output=None,
require_matching_strides=None,
linker=_DummyLinker()):
"""Initialize member variables.
"""
If any of these arguments (except optimizer) is not None, it overrides
the class default.
The linker argument is not used. It is set there to allow
Mode.requiring() and some other fct to work with DebugMode too.
the class default. The linker argument is not used. It is set there to
allow Mode.requiring() and some other fct to work with DebugMode too.
"""
if not isinstance(linker, _DummyLinker):
......
"""Define the `function` function
"""
Define the `function` function.
"""
import six.moves.cPickle as pickle
import logging
......@@ -23,8 +25,9 @@ def function_dump(filename, inputs, outputs=None, mode=None, updates=None,
no_default_updates=False, accept_inplace=False, name=None,
rebuild_strict=True, allow_input_downcast=None, profile=None,
on_unused_input=None):
"""This is helpful to make a reproducable case for problem during
Theano compilation.
"""
This is helpful to make a reproducable case for problem during Theano
compilation.
Ex:
......@@ -65,78 +68,67 @@ def function(inputs, outputs=None, mode=None, updates=None, givens=None,
"""
Return a callable object that will calculate `outputs` from `inputs`.
:type inputs: list of either Variable or Param instances.
:param inputs: function parameters, these are not allowed to be shared
variables
:type outputs: list or dict of Variables or Out instances. If it is a
dict, the keys must be strings
:param outputs: expressions to compute
:type mode: string or `Mode` instance.
:param mode: compilation mode
:type updates: iterable over pairs (shared_variable, new_expression).
List, tuple or OrderedDict.
:param updates: update the values for SharedVariable inputs
according to these expressions
:type givens: iterable over pairs (Var1, Var2) of Variables. List,
tuple or dict. The Var1 and Var2 in each pair must
have the same Type.
:param givens: specific substitutions to make in the computation
graph (Var2 replaces Var1).
:type no_default_updates: either bool or list of Variables
:param no_default_updates: if True, do not perform any automatic
update on Variables. If False (default), perform them
all. Else, perform automatic updates on all Variables that are
neither in "updates" nor in "no_default_updates".
:param name: an optional name for this function. The profile mode
will print the time spent in this function.
:param rebuild_strict: True (Default) is the safer and better
tested setting, in which case `givens` must substitute new
variables with the same Type as the variables they replace.
False is a you-better-know-what-you-are-doing setting, that
permits `givens` to replace variables with new variables of
any Type. The consequence of changing a Type is that all
results depending on that variable may have a different Type
too (the graph is rebuilt from inputs to outputs). If one of
the new types does not make sense for one of the Ops in the
graph, an Exception will be raised.
:type allow_input_downcast: Boolean or None
:param allow_input_downcast: True means that the values passed as
inputs when calling the function can be silently downcasted to
fit the dtype of the corresponding Variable, which may lose
precision. False means that it will only be cast to a more
general, or precise, type. None (default) is almost like
False, but allows downcasting of Python float scalars to
floatX.
:type profile: None, True, or ProfileStats instance
:param profile: accumulate profiling information into a given
ProfileStats instance. If argument is `True` then a new
ProfileStats instance will be used. This profiling object
will be available via self.profile.
:param on_unused_input: What to do if a variable in the 'inputs'
list is not used in the graph. Possible values are 'raise',
'warn', 'ignore' and None.
:rtype: Function instance
:returns: a callable object that will compute the outputs (given
the inputs) and update the implicit function arguments
according to the `updates`.
:note: Regarding givens: Be careful to make sure that these
substitutions are independent--behaviour when Var1 of one pair
appears in the graph leading to Var2 in another expression is
undefined. Replacements specified with givens are different
from optimizations in that Var2 is not expected to be
equivalent to Var1.
Parameters
----------
inputs : list of either Variable or Param instances.
Function parameters, these are not allowed to be shared variables.
outputs : list or dict of Variables or Out instances.
If it is a dict, the keys must be strings. Expressions to compute.
mode : string or `Mode` instance.
Compilation mode.
updates : iterable over pairs (shared_variable, new_expression). List, tuple
or OrderedDict.
Updates the values for SharedVariable inputs according to these
expressions.
givens : iterable over pairs (Var1, Var2) of Variables. List, tuple or dict.
The Var1 and Var2 in each pair must have the same Type.
Specific substitutions to make in the computation graph (Var2 replaces
Var1).
no_default_updates: either bool or list of Variables
If True, do not perform any automatic update on Variables. If False
(default), perform them all. Else, perform automatic updates on all
Variables that are neither in "updates" nor in "no_default_updates".
name : str
An optional name for this function. The profile mode will print the time
spent in this function.
rebuild_strict : bool
True (Default) is the safer and better tested setting, in which case
`givens` must substitute new variables with the same Type as the
variables they replace.
False is a you-better-know-what-you-are-doing setting, that permits
`givens` to replace variables with new variables of any Type.
The consequence of changing a Type is that all results depending on that
variable may have a different Type too (the graph is rebuilt from inputs
to outputs). If one of the new types does not make sense for one of the
Ops in the graph, an Exception will be raised.
allow_input_downcast: bool or None
True means that the values passed as inputs when calling the function
can be silently downcasted to fit the dtype of the corresponding
Variable, which may lose precision. False means that it will only be
cast to a more general, or precise, type. None (default) is almost like
False, but allows downcasting of Python float scalars to floatX.
profile: None, True, or ProfileStats instance
Accumulate profiling information into a given ProfileStats instance.
If argument is `True` then a new ProfileStats instance will be used.
This profiling object will be available via self.profile.
on_unused_input
What to do if a variable in the 'inputs' list is not used in the graph.
Possible values are 'raise', 'warn', 'ignore' and None.
Returns
-------
Function instance
A callable object that will compute the outputs (given the inputs) and
update the implicit function arguments according to the `updates`.
Notes
-----
Regarding givens: Be careful to make sure that these
substitutions are independent--behaviour when Var1 of one pair
appears in the graph leading to Var2 in another expression is
undefined. Replacements specified with givens are different
from optimizations in that Var2 is not expected to be
equivalent to Var1.
Internal documentation:
......@@ -214,6 +206,7 @@ def function(inputs, outputs=None, mode=None, updates=None, givens=None,
was easier to develop the VM in Python then translate it to C instead
of just writing it in C from scratch.
CVM stands for C Virtual Machine.
"""
if isinstance(outputs, dict):
output_items = list(outputs.items())
......
"""Driver of graph construction, optimization, and linking.
"""
Driver of graph construction, optimization, and linking.
"""
from __future__ import print_function
......@@ -31,13 +33,18 @@ __docformat__ = "restructuredtext en"
class UnusedInputError(Exception):
"""
A symbolic input passed to function is not needed
A symbolic input passed to function is not needed.
"""
pass
def alias_root(v):
"Return the variable to which v is aliased by view_maps and destroy_maps"
"""
Return the variable to which v is aliased by view_maps and destroy_maps.
"""
if v.owner is None:
return v
vmap = getattr(v.owner.op, 'view_map', {})
......@@ -56,8 +63,11 @@ def alias_root(v):
def view_tree_set(v, treeset):
"""Add to `treeset` all variables that are views of v, given that v is
not a view"""
"""
Add to `treeset` all variables that are views of v, given that v is
not a view.
"""
treeset.add(v)
for cl, v_input_pos_to_cl in v.clients:
if cl == 'output':
......@@ -79,6 +89,7 @@ def infer_reuse_pattern(fgraph, outputs_to_disown):
This list (or set) is also refered to as no_recycling sometimes,
especially by linker code.
"""
rval = set()
for o in outputs_to_disown:
......@@ -94,7 +105,10 @@ def fgraph_updated_vars(fgraph, expanded_inputs):
Reconstruct the full "updates" dictionary, mapping from FunctionGraph input
variables to the fgraph outputs that will replace their values.
:rtype: dict variable -> variable
Returns
-------
dict variable -> variable
"""
updated_vars = {}
potential_values = list(fgraph.outputs) # copy the list
......@@ -111,7 +125,9 @@ class Supervisor:
Listener for FunctionGraph events which makes sure that no
operation overwrites the contents of protected Variables. The
outputs of the FunctionGraph are protected by default.
"""
def __init__(self, protected):
self.protected = list(protected)
......@@ -139,6 +155,7 @@ def std_fgraph(input_specs, output_specs, accept_inplace=False):
The returned FunctionGraph is a clone of the graph between the provided
inputs and outputs.
"""
orig_inputs = [spec.variable for spec in input_specs]
updates = [spec.update for spec in input_specs if spec.update]
......@@ -173,7 +190,10 @@ std_fgraph.features = [gof.toolbox.PreserveNames]
class AliasedMemoryError(Exception):
"""Memory is aliased that should not be"""
"""
Memory is aliased that should not be.
"""
pass
......@@ -190,19 +210,16 @@ class Function(object):
Type of the functions returned by theano.function or
theano.FunctionMaker.create.
`Function` is the callable object that does computation. It has the storage
of inputs and outputs, performs the packing and unpacking of inputs and
return values. It implements the square-bracket indexing so that you can
look up the value of a symbolic node.
`Function` is the callable object that does computation. It has
the storage of inputs and outputs, performs the packing and
unpacking of inputs and return values. It implements the
square-bracket indexing so that you can look up the value of a
symbolic node.
Functions are copyable via {{{fn.copy()}}} and
{{{copy.copy(fn)}}}. When a function is copied, this instance is
duplicated. Contrast with self.maker (instance of
`FunctionMaker`) that is shared between copies. The meaning of
copying a function is that the containers and their current values
will all be duplicated. This requires that mutable inputs be
Functions are copyable via {{{fn.copy()}}} and {{{copy.copy(fn)}}}.
When a function is copied, this instance is duplicated. Contrast with
self.maker (instance of `FunctionMaker`) that is shared between copies.
The meaning of copying a function is that the containers and their current
values will all be duplicated. This requires that mutable inputs be
copied, whereas immutable inputs may be shared between copies.
A Function instance is hashable, on the basis of its memory
......@@ -220,62 +237,93 @@ class Function(object):
the good results if you pass a python or numpy scalar instead of a
numpy tensor. C code should raise an error if you pass an object
of the wrong type.
Attributes
----------
finder
inv_finder
"""
pickle_aliased_memory_strategy = 'warn'
"""How to deal with pickling finding aliased storage.
"""
How to deal with pickling finding aliased storage.
Meaningful settings are: 'ignore', 'warn', 'raise'
Meaningful settings are: 'ignore', 'warn', 'raise'.
If the value is 'warn', then a message will be printed to stderr
if aliased storage is dectected during pickle.dump.
If the value is 'raise', then an AliasedMemoryError will be raised
if aliased storage is detected during pickle.dump.
"""
input_storage = None
"""list of Container instances"""
"""
List of Container instances.
"""
output_storage = None
"""list of Container instances"""
"""
List of Container instances.
"""
indices = None
"""list of (SymbolicInput|SymbolicInputKit, indices,
[SymbolicInput,...]), one tuple for each input
"""
List of (SymbolicInput|SymbolicInputKit, indices, [SymbolicInput,...]),
one tuple for each input.
The first tuple element is the SymbolicInput object for the
corresponding function input.
The first tuple element is the SymbolicInput object for the corresponding
function input.
The second and third tuple elements are used only by Kits, which
are deprecated.
"""
defaults = None
""" list of 3-tuples, one 3-tuple for each input.
"""
List of 3-tuples, one 3-tuple for each input.
Tuple element 0: Bool: Is this input required at each function call?
Tuple element 1: Bool: Should this inputs value be reverted after
each call?
Tuple element 2: Any: The value associated with this input.
"""
unpack_single = None
"""Bool: for outputs lists of length 1, should the 0'th element be
returned directly?"""
"""
Bool: for outputs lists of length 1, should the 0'th element be
returned directly?
"""
return_none = None
"""Bool: whether the function should return None or not"""
"""
Bool: whether the function should return None or not.
"""
maker = None
"""FunctionMaker instance"""
"""
FunctionMaker instance.
"""
fn = None
"""a function that evaluates the graph. Typically a linker's
make_thunk method created this function."""
"""
A function that evaluates the graph. Typically a linker's make_thunk method
created this function.
"""
finder = None
"""Dictionary mapping several kinds of things to containers.
"""
Dictionary mapping several kinds of things to containers.
We set an entry in finder for:
......@@ -286,21 +334,20 @@ returned directly?"""
- the name of the input
All entries map to the container or to DUPLICATE if an ambiguity
is detected
is detected.
"""
inv_finder = None
"""Dict. Reverse lookup of `finder`.
"""
Dict. Reverse lookup of `finder`.
It maps container -> SymbolicInput
"""
def __init__(self, fn, input_storage, output_storage, indices, outputs,
defaults, unpack_single, return_none, output_keys, maker):
"""
Initialize attributes. create finder, inv_finder.
"""
self.fn = fn
self.input_storage = input_storage
self.output_storage = output_storage
......@@ -875,13 +922,35 @@ NODEFAULT = ['NODEFAULT']
class FunctionMaker(object):
"""`FunctionMaker` is the class to `create` `Function` instances.
"""
`FunctionMaker` is the class to `create` `Function` instances.
This class has the fgraph, the optimizer, and the linker. When
This class has the fgraph, the optimizer, and the linker. When
copying a `Function`, there is no need to duplicate the
`FunctionMaker` instance. Deepcopy still copies both, which can
`FunctionMaker` instance. Deepcopy still copies both, which can
variable in re-compilation.
Parameters
----------
inputs : list of SymbolicInput instances
outputs : list of SymbolicOutput instances
Outputs may also be a single Variable (not a list), in which case the
functions produced by FunctionMaker will return their output value
directly.
mode : Mode instance
Telling FunctionMaker how to optimize and link. None means to use the
`config.mode`.
accept_inplace : bool
True iff it is acceptable to have inplace operations in the graph from
the inputs to the outputs.
on_unused_input : {'raise', 'warn', 'ignore', None}
What to do if a variable in the 'inputs' list is not used in the graph.
Possible values are:
- 'raise': raise an error
- 'warn': log a warning
- 'ignore': do not do anything
- None: Use the value in the Theano flags on_unused_input.
"""
@staticmethod
......@@ -1101,29 +1170,6 @@ class FunctionMaker(object):
mode=None, accept_inplace=False, function_builder=Function,
profile=None, on_unused_input=None, fgraph=None,
output_keys=None):
"""
:type inputs: a list of SymbolicInput instances
:type outputs: a list of SymbolicOutput instances outputs may
also be a single Variable (not a list), in which case the
functions produced by FunctionMaker will return their
output value directly
:param mode: a Mode instance telling FunctionMaker how to
optimize and link. None means to use the `config.mode`.
:param accept_inplace: True iff it is acceptable to have
inplace operations in the graph from the inputs to the
outputs
:param on_unused_input: What to do if a variable in the 'inputs' list
is not used in the graph. Possible values are:
- 'raise': raise an error
- 'warn': log a warning
- 'ignore': do not do anything
- None: Use the value in the Theano flags on_unused_input
"""
mode = theano.compile.mode.get_mode(mode)
# figure out which profile object to use (if any)
......@@ -1313,12 +1359,15 @@ class FunctionMaker(object):
"""
Create a function.
input_storage -> a list matching the inputs list and providing
default values if the default for an input is
None, then that input is a required input. For an
input with an update, the default acts as
initialization.
trustme -> disables some exceptions, used internally
Parameters
----------
input_storage
A list matching the inputs list and providing default values if the
default for an input is None, then that input is a required input.
For an input with an update, the default acts as initialization.
trustme
Disables some exceptions, used internally.
"""
if input_storage is None:
......@@ -1453,43 +1502,43 @@ def orig_function(inputs, outputs, mode=None, accept_inplace=False,
"""
Return a Function that will calculate the outputs from the inputs.
:param inputs: list of `SymbolicInput` or `In` instances
:param outputs: a SymbolicOutput or a list of `SymbolicOutput` or
`Out` instances. The return value of the returned function
will match the format of this argument (either the value
itself or a list of one or more return values)
:param mode: a descriptive string or a Mode instance. (Default of None
means to use `config.mode` (See below for descriptive string list).
:param name: an optional name for this fct. If used, the profile mode will
print the time spent in this fct.
Parameters
----------
inputs : list of `SymbolicInput` or `In` instances
outputs : a SymbolicOutput or a list of `SymbolicOutput` or `Out` instances
The return value of the returned function will match the format of this
argument (either the value itself or a list of one or more return
values).
mode : descriptive string or Mode instance
Default of None means to use `config.mode` (see below for descriptive
string list).
name : str
An optional name for this fct. If used, the profile mode will print the
time spent in this fct.
accept_inplace : bool
True iff the graph can contain inplace operations prior to the
optimization phase (default is False).
profile : None or ProfileStats instance
on_unused_input : {'raise', 'warn', 'ignore', None}
What to do if a variable in the 'inputs' list is not used in the graph.
output_keys :
If the outputs were provided to theano.function as a list, then
output_keys is None. Otherwise, if outputs were provided as a dict,
output_keys is the sorted list of keys from the outputs.
Notes
-----
Currently, the library provides the following mode strings:
- FAST_RUN (default) (optimize without too much time)
- FAST_COMPILE (minimal optimization)
- ProfileMode(deprecated): allow to print a profile mode with
mode.print_summary
- DebugMode: verify many internal conditions that are normally assumed
(slow)
:param accept_inplace: True iff the graph can contain inplace operations
prior to the optimization phase (default is False)
- FAST_RUN (default) (optimize without too much time)
:param profile: None or ProfileStats instance
- FAST_COMPILE (minimal optimization)
:param on_unused_input: What to do if a variable in the 'inputs' list is
not used in the graph. Possible values are 'raise', 'warn', 'ignore'
and None
- ProfileMode(deprecated): allow to print a profile mode with
mode.print_summary
:param output_keys: If the outputs were provided to theano.function as a
list, then output_keys is None. Otherwise, if outputs were provided
as a dict, output_keys is the sorted list of keys from the outputs
- DebugMode: verify many internal conditions that are normally assumed
(slow)
"""
......@@ -1554,6 +1603,7 @@ def convert_function_input(input):
- a tuple (name, (r,up), val) will be
`In`(r, name=name, value=val, update=up, autoname=True)
"""
if isinstance(input, (SymbolicInput, SymbolicInputKit)):
return input
......@@ -1615,7 +1665,10 @@ def convert_function_input(input):
def get_info_on_inputs(named_inputs, n_unnamed_inputs):
"""Return a human-readable description of named and un-named inputs."""
"""
Return a human-readable description of named and un-named inputs.
"""
n_named_inputs = len(named_inputs)
def get_plural(n):
......
"""Define `SymbolicInput`, `SymbolicOutput`, `In`, `Out` """
"""
Define `SymbolicInput`, `SymbolicOutput`, `In`, `Out`.
"""
from theano import gof
from .sharedvalue import SharedVariable
......@@ -15,52 +17,43 @@ class SymbolicInput(object):
"""
Represents a symbolic input for use with function or FunctionMaker.
variable: a Variable instance.
This will be assigned a value before running the function,
not computed from its owner.
name: Any type. (If autoname=True, defaults to variable.name).
If name is a valid Python identifier, this input can be set by
kwarg, and its value can be accessed by self.<name>.
update: Variable instance (default: None)
value (see previous) will be replaced with this expression
variable after each function call. If update is None, the
Parameters
----------
variable : a Variable instance
This will be assigned a value before running the function, not computed
from its owner.
name : Any type
If autoname=True, defaults to variable.name.
If name is a valid Python identifier, this input can be set by kwarg,
and its value can be accessed by self.<name>.
update : Variable instance
Defaults to None. Value (see previous) will be replaced with this
expression variable after each function call. If update is None, the
update will be the default value of the input.
mutable : bool
Defaults to False if update is None, True if update is not None.
True: permit the compiled function to modify the python object being
passed as the input.
False: do not permit the compiled function to modify the python object
being passed as the input.
strict : bool
Defaults to False.
True: means that the value you pass for this input must have exactly the
right type.
False: the value you pass for this input may be cast automatically to
the proper type.
allow_downcast : bool or None
Defaults to None. Only applies when `strict` is False.
True: the value you pass for this input can be silently downcasted to
fit the right type, which may lose precision.
False: the value will only be cast to a more general, or precise, type.
None: Almost like False, but allows downcast of Python floats to floatX.
autoname : bool
Defaults to True. See the name option.
implicit : bool
Defaults to False. See help(In). Note that 'None' is not allowed here,
since we are in the symbolic case.
mutable: Bool (default: False if update is None, True if update is
not None)
True: permit the compiled function to modify the python object
being passed as the input
False: do not permit the compiled function to modify the
python object being passed as the input.
strict: Bool (default: False)
True: means that the value you pass for this input must have
exactly the right type
False: the value you pass for this input may be cast
automatically to the proper type
allow_downcast: Bool or None (default: None)
Only applies when `strict` is False.
True: the value you pass for this input can be silently
downcasted to fit the right type, which may lose precision.
False: the value will only be cast to a more general, or
precise, type. None: Almost like False, but allows downcast
of Python floats to floatX.
autoname: Bool (default: True)
See the name option.
implicit: Bool (default: False)
See help(In). Note that 'None' is not allowed here, since we
are in the symbolic case.
"""
def __init__(self, variable, name=None, update=None, mutable=None,
......@@ -105,19 +98,22 @@ class SymbolicInputKit(object):
A SymbolicInputKit provides the distribute function in order to set or
initialize several inputs from a single value. Specialized Kits should
override it.
"""
def __init__(self, name):
if not isinstance(name, string_types):
raise TypeError('naem must be a string (got: %s)' % name)
raise TypeError('name must be a string (got: %s)' % name)
self.name = name
self.sinputs = []
self.variables = []
def add_input(self, sinput):
"""
Add a SymbolicInput to this SymbolicInputKit. It will be given the
next available index.
Add a SymbolicInput to this SymbolicInputKit.
It will be given the next available index.
"""
self.sinputs.append(sinput)
self.variables.append(sinput.variable)
......@@ -127,18 +123,20 @@ class SymbolicInputKit(object):
Given a list of indices corresponding to SymbolicInputs in this kit
as well as a corresponding list of containers, initialize all the
containers using the provided value.
"""
raise NotImplementedError
def complete(self, inputs):
"""
Given inputs (a list of Variable instances), checks through all
the SymbolicInputs in the kit and return a sorted list of
indices and a list of their corresponding SymbolicInputs such
that each of them represents some variable in the inputs list.
Given inputs (a list of Variable instances), checks through all the
SymbolicInputs in the kit and return a sorted list of indices and a list
of their corresponding SymbolicInputs such that each of them represents
some variable in the inputs list.
Not all the provided inputs will have a corresponding SymbolicInput in
the kit.
Not all the provided inputs will have a corresponding
SymbolicInput in the kit.
"""
ret = []
for input in inputs:
......@@ -157,73 +155,62 @@ class In(SymbolicInput):
"""
Represents a symbolic input for use with function or FunctionMaker.
variable: a Variable instance.
This will be assigned a value before running the function,
not computed from its owner.
name: Any type. (If autoname=True, defaults to variable.name).
If name is a valid Python identifier, this input can be set by
kwarg, and its value can be accessed by self.<name>.
value: Any type.
Parameters
----------
variable : a Variable instance
This will be assigned a value before running the function, not computed
from its owner.
name : Any type
If autoname=True, defaults to variable.name.
If name is a valid Python identifier, this input can be set by kwarg,
and its value can be accessed by self.<name>.
value : Any type
The initial/default value for this input. If update is None,
this input acts just like an argument with a default value in
Python. If update is not None, changes to this value will
"stick around", whether due to an update or a user's explicit
action.
update: Variable instance (default: None)
value (see previous) will be replaced with this expression
variable after each function call. If update is None, the
update : Variable instance
Defaults to None. Value (see previous) will be replaced with this
expression variable after each function call. If update is None, the
update will be the default value of the input.
mutable: Bool (default: False if update is None, True if update is
not None)
mutable : bool
Defaults to False if update is None, True if update is not None.
True: permit the compiled function to modify the python object
being passed as the input
being passed as the input.
False: do not permit the compiled function to modify the
python object being passed as the input.
borrow: Bool (default: take the same value as mutable)
borrow : bool
Default : take the same value as mutable.
True: permit the output of the compiled function to be aliased
to the input
False: do not permit any output to be aliased to the input
strict: Bool (default: False)
True: means that the value you pass for this input must have
exactly the right type
False: the value you pass for this input may be cast
automatically to the proper type
allow_downcast: Bool or None (default: None)
Only applies when `strict` is False.
True: the value you pass for this input can be silently
downcasted to fit the right type, which may lose precision.
False: the value will only be cast to a more general, or
precise, type. None: Almost like False, but allows downcast
of Python floats to floatX.
autoname: Bool (default: True)
See the name option.
implicit: Bool or None (default: None)
to the input.
False: do not permit any output to be aliased to the input.
strict : bool
Defaults to False.
True: means that the value you pass for this input must have exactly
the right type.
False: the value you pass for this input may be cast automatically to
the proper type.
allow_downcast : bool or None
Defaults to None. Only applies when `strict` is False.
True: the value you pass for this input can be silently downcasted to
fit the right type, which may lose precision.
False: the value will only be cast to a more general, or precise, type.
None: Almost like False, but allows downcast of Python floats to floatX.
autoname : bool
Defaults to True. See the name option.
implicit : bool or None
Defaults to None.
True: This input is implicit in the sense that the user is not allowed
to provide a value for it. Requires 'value' to be set.
to provide a value for it. Requires 'value' to be set.
False: The user can provide a value for this input. Be careful when
'value' is a container, because providing an input value will
overwrite the content of this container.
'value' is a container, because providing an input value will
overwrite the content of this container.
None: Automatically choose between True or False depending on the
situation. It will be set to False in all cases except if 'value'
is a container (so that there is less risk of accidentally
overwriting its content without being aware of it).
situation. It will be set to False in all cases except if 'value' is a
container (so that there is less risk of accidentally overwriting its
content without being aware of it).
"""
# Note: the documentation above is duplicated in doc/topics/function.txt,
# try to keep it synchronized.
......@@ -273,10 +260,14 @@ class SymbolicOutput(object):
"""
Represents a symbolic output for use with function or FunctionMaker.
borrow: set this to True to indicate that a reference to
function's internal storage may be returned. A value
returned for this output might be clobbered by running
the function again, but the function might be faster.
Parameters
----------
borrow : bool
Set this to True to indicate that a reference to function's internal
storage may be returned. A value returned for this output might be
clobbered by running the function again, but the function might be
faster.
"""
def __init__(self, variable, borrow=False):
......
"""WRITEME
"""
WRITEME
"""
from __future__ import print_function
import logging
......@@ -34,8 +36,9 @@ AddConfigVar('optimizer_requiring',
def check_equal(x, y):
"""
Returns True iff x[0] and y[0] are equal (checks the dtype and
shape if x and y are numpy.ndarray instances). Used internally.
Returns True iff x[0] and y[0] are equal (checks the dtype and shape if x
and y are numpy.ndarray instances). Used internally.
"""
# I put the import here to allow using theano without scipy.
import scipy.sparse as sp
......@@ -125,17 +128,19 @@ def register_optimizer(name, opt):
class AddDestroyHandler(gof.Optimizer):
"""This optimizer performs two important functions:
"""
This optimizer performs two important functions:
1) It has a 'requirement' of the destroyhandler. This means that the fgraph
will include it as a feature for this optimization, and keep this feature
enabled for subsequent optimizations. All optimizations that work inplace
enabled for subsequent optimizations. All optimizations that work inplace
on any of their inputs must run *after* this optimization to ensure that
the DestroyHandler has been included in the fgraph.
2) It tries to replace each output with an Op that purports to destroy it
(but it won't I promise). If this replacement succeeds it means that
there is a bug in theano. It should not be possible to destroy outputs.
(but it won't I promise). If this replacement succeeds it means that
there is a bug in theano. It should not be possible to destroy outputs.
"""
def apply(self, fgraph):
for o in fgraph.outputs:
......@@ -157,11 +162,13 @@ class AddDestroyHandler(gof.Optimizer):
class AddNoOutputFromInplace(gof.Optimizer):
"""This optimizer adds to the fgraph a feature that will prevent outputs
"""
This optimizer adds to the fgraph a feature that will prevent outputs
of a fgraph to be created by performing inplace operations on intermediary
variables. This is useful when the outputs of the fgraph are preallocated
to prevent useless copying of the data. Currently, scan preallocates its
outputs
"""
def add_requirements(self, fgraph):
super(AddNoOutputFromInplace, self).add_requirements(fgraph)
......@@ -169,10 +176,12 @@ class AddNoOutputFromInplace(gof.Optimizer):
class PrintCurrentFunctionGraph(gof.Optimizer):
"""This optimizer is for debugging.
"""
This optimizer is for debugging.
Toss it into the optimization pipeline to see the state of things at any
given point.
"""
def __init__(self, header):
self.header = header
......@@ -233,18 +242,23 @@ optdb.register('merge3', gof.MergeOptimizer(),
class Mode(object):
"""
The Mode represents a way to optimize and then link a computation
graph.
* optimizer -> a structure of type Optimizer. An Optimizer may
simplify the math, put similar computations together, improve
numerical stability and various other improvements.
* linker -> a structure of type Linker. A Linker decides which
implementations to use (C or Python, for example) and how to
string them together to perform the computation.
See predefined_linkers, predefined_optimizers and also
predefined_modes.
The Mode represents a way to optimize and then link a computation graph.
Parameters
----------
optimizer : a structure of type Optimizer
An Optimizer may simplify the math, put similar computations together,
improve numerical stability and various other improvements.
linker : a structure of type Linker
A Linker decides which implementations to use (C or Python, for example)
and how to string them together to perform the computation.
See Also
--------
predefined_linkers
predefined_optimizers
predefined_modes
"""
def __init__(self, linker=None, optimizer='default'):
......@@ -326,6 +340,7 @@ class Mode(object):
Keyword arguments can be provided for the linker,
in which case its `clone` method will be called with these
arguments.
"""
new_linker = self.linker.clone(**link_kwargs)
new_optimizer = self.provided_optimizer
......@@ -412,7 +427,10 @@ def get_default_mode():
def register_mode(name, mode):
"""Add a `Mode` which can be referred to by `name` in `function`."""
"""
Add a `Mode` which can be referred to by `name` in `function`.
"""
if name in predefined_modes:
raise ValueError('Mode name already taken: %s' % name)
predefined_modes[name] = mode
......@@ -8,7 +8,6 @@ from theano.compile.mode import Mode
class MonitorMode(Mode):
"""
`MonitorMode` is a debug mode to easily step through function execution.
......@@ -19,28 +18,28 @@ class MonitorMode(Mode):
A typical use case is to detect the introduction of NaN values in a graph.
For an example of such a use case, see doc/tutorial/debug_faq.txt.
Parameters
----------
pre_func
A function to call before executing a thunk, with arguments:
- the thunk index
- the Apply node
- the thunk to be called
post_func
A function to call after executing a thunk, with the same three
arguments as `pre_func`.
optimizer
The optimizer to use. One may use for instance 'fast_compile' to skip
optimizations.
linker
DO NOT USE. This mode uses its own linker. The parameter is needed to
allow selecting optimizers to use.
"""
def __init__(self, pre_func=None, post_func=None,
optimizer='default', linker=None):
"""
Constructor.
:param pre_func: A function to call before executing a thunk, with
arguments:
- the thunk index
- the Apply node
- the thunk to be called
:param post_func: A function to call after executing a thunk, with the
same three arguments as `pre_func`.
:param optimizer: The optimizer to use. One may use for instance
'fast_compile' to skip optimizations.
:param linker: DO NOT USE. This mode uses its own linker.
The parameter is needed to allow selecting optimizers to use.
"""
self.pre_func = pre_func
self.post_func = post_func
wrap_linker = theano.gof.WrapLinkerMany([theano.gof.OpWiseCLinker()],
......@@ -67,6 +66,7 @@ class MonitorMode(Mode):
def eval(self, i, node, fn):
"""
The method that calls the thunk `fn`.
"""
if self.pre_func is not None:
self.pre_func(i, node, fn)
......@@ -96,9 +96,9 @@ class MonitorMode(Mode):
"""
Create a new instance of this Mode.
Keyword arguments can be provided for the linker,
but they will be ignored, because ProfileMode needs
to use its own linker.
Keyword arguments can be provided for the linker, but they will be
ignored, because ProfileMode needs to use its own linker.
"""
new_mode = type(self)(pre_func=self.pre_func,
post_func=self.post_func,
......
......@@ -16,11 +16,14 @@ def flatten(l):
Parameters
----------
l : List/tuple/other objects, might be nested.
l : list/tuple/other objects
Might be nested.
Returns
-------
A flattened list of objects
object
A flattened list of objects.
"""
if isinstance(l, (list, tuple, collections.ValuesView)):
rval = []
......@@ -53,6 +56,7 @@ def contains_nan(arr):
This approach is faster and more memory efficient than the obvious
alternative, calling `np.any(np.isnan(ndarray))`, which requires the
construction of a boolean array with the same shape as the input array.
"""
if isinstance(arr, theano.gof.type.CDataType._cdata_type):
return False
......@@ -81,6 +85,7 @@ def contains_inf(arr):
This approach is more memory efficient than the obvious alternative,
calling `np.any(np.isinf(ndarray))`, which requires the construction of a
boolean array with the same shape as the input array.
"""
if isinstance(arr, theano.gof.type.CDataType._cdata_type):
return False
......@@ -97,14 +102,16 @@ class NanGuardMode(Mode):
Parameters
----------
nan_is_error : bool
If True, raise an error anytime a NaN is encountered
inf_is_error: bool
If True, raise an error anytime a NaN is encountered.
inf_is_error : bool
If True, raise an error anytime an Inf is encountered. Note that some
pylearn2 modules currently use np.inf as a default value (e.g.
mlp.max_pool) and these will cause an error if inf_is_error is True.
big_is_error: bool
big_is_error : bool
If True, raise an error when a value greater than 1e10 is encountered.
"""
def __init__(self, nan_is_error, inf_is_error, big_is_error=True):
if cuda.cuda_available:
self.guard_input = cuda.fvector('nan_guard')
......@@ -135,12 +142,13 @@ class NanGuardMode(Mode):
var : numpy.ndarray
The value to be checked.
nd : theano.gof.Apply
The Apply node being executed
The Apply node being executed.
f : callable
The thunk for the apply node
The thunk for the apply node.
is_input : bool
If True, `var` is an input to `nd`.
If False, it is an output.
"""
error = False
if nan_is_error:
......@@ -193,15 +201,18 @@ class NanGuardMode(Mode):
def nan_check(i, node, fn):
"""
Runs `fn` while checking its inputs and outputs for NaNs / Infs
Runs `fn` while checking its inputs and outputs for NaNs / Infs.
Parameters
----------
i : currently ignored (TODO: determine why it is here or remove)
i :
Currently ignored.
TODO: determine why it is here or remove).
node : theano.gof.Apply
The Apply node currently being executed
The Apply node currently being executed.
fn : callable
The thunk to execute for this Apply node
The thunk to execute for this Apply node.
"""
inputs = fn.inputs
# TODO: figure out why individual inputs are themselves lists
......
"""This file contains auxiliary Ops, used during the compilation phase
and Ops building class (:class:`FromFunctionOp`) and decorator
(:func:`as_op`) that help make new Ops more rapidly.
"""
This file contains auxiliary Ops, used during the compilation phase and Ops
building class (:class:`FromFunctionOp`) and decorator (:func:`as_op`) that
help make new Ops more rapidly.
"""
import copy
......@@ -18,14 +19,19 @@ import numpy
def register_view_op_c_code(type, code, version=()):
""" Tell ViewOp how to generate C code for a Theano Type
:param type: A Theano type. It must be the Theano class itself and not an
instance of the class.
:param code: C code that returns a view for the Theano type 'type'.
Use %(iname)s and %(oname)s for the input and output C
variable names respectively.
:param version: A number indicating the version of the code, for cache.
"""
Tell ViewOp how to generate C code for a Theano Type.
Parameters
----------
type : Theano type
It must be the Theano class itself and not an instance of the class.
code : C code
Returns a view for the Theano type 'type'. Use %(iname)s and %(oname)s
for the input and output C variable names respectively.
version
A number indicating the version of the code, for cache.
"""
ViewOp.c_code_and_version[type] = (code, version)
......@@ -33,7 +39,9 @@ def register_view_op_c_code(type, code, version=()):
class ViewOp(gof.Op):
"""
Returns an inplace view of the input. Used internally by Theano.
"""
view_map = {0: [0]}
# Mapping from Type to C code (and version) to use.
# In the C code, the name of the input variable is %(iname)s,
......@@ -96,9 +104,9 @@ class OutputGuard(ViewOp):
Only the AddDestroyHandler optimizer tries to insert them in the graph.
This Op is declared as destructive while it is not destroying
anything. It returns a view. This is used to prevent destruction of
the output variables of a Theano function.
This Op is declared as destructive while it is not destroying anything.
It returns a view. This is used to prevent destruction of the output
variables of a Theano function.
There is a mechanism in Theano that should prevent this, but the use
of OutputGuard adds a safeguard: it may be possible for some optimization
......@@ -106,6 +114,7 @@ class OutputGuard(ViewOp):
making in-place optimizations.
TODO: find a current full explanation.
"""
destroy_map = {0: [0]}
......@@ -115,14 +124,19 @@ _output_guard = OutputGuard()
def register_deep_copy_op_c_code(typ, code, version=()):
""" Tell DeepCopyOp how to generate C code for a Theano Type
:param typ: A Theano type. It must be the Theano class itself and not an
instance of the class.
:param code: C code that deep copies the Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C
variable names respectively.
:param version: A number indicating the version of the code, for cache.
"""
Tell DeepCopyOp how to generate C code for a Theano Type.
Parameters
----------
typ : Theano type
It must be the Theano class itself and not an instance of the class.
code: C code
Deep copies the Theano type 'typ'. Use %(iname)s and %(oname)s for the
input and output C variable names respectively.
version
A number indicating the version of the code, for cache.
"""
DeepCopyOp.c_code_and_version[typ] = (code, version)
......@@ -189,15 +203,20 @@ deep_copy_op = DeepCopyOp()
def register_shape_c_code(type, code, version=()):
""" Tell Shape Op how to generate C code for a Theano Type
:param typ: A Theano type. It must be the Theano class itself and not an
instance of the class.
:param code: C code that return a vector representing the shape
for the Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C
variable names respectively.
:param version: A number indicating the version of the code, for cache.
"""
Tell Shape Op how to generate C code for a Theano Type.
Parameters
----------
typ : Theano type
It must be the Theano class itself and not an instance of the class.
code : C code
Returns a vector representing the shape for the Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C variable names
respectively.
version
A number indicating the version of the code, for cache.
"""
Shape.c_code_and_version[type] = (code, version)
......@@ -206,8 +225,12 @@ class Shape(gof.Op):
"""
L{Op} to return the shape of a matrix.
@note: Non-differentiable.
Notes
-----
Non-differentiable.
"""
_f16_ok = True
# Mapping from Type to C code (and version) to use.
......@@ -293,8 +316,12 @@ class Shape_i(gof.Op):
"""
L{Op} to return the shape of a matrix.
@note: Non-differentiable.
Notes
-----
Non-differentiable.
"""
_f16_ok = True
# Mapping from Type to C code (and version) to use.
......@@ -381,18 +408,24 @@ class Shape_i(gof.Op):
def shape_i(var, i, fgraph=None):
"""Equivalent of var.shape[i], but apply if possible the shape
feature optimization
"""
Equivalent of var.shape[i], but apply if possible the shape feature
optimization.
This is useful in optimization that need to get the shape. This
remove the need of the following shape_feature optimization that
convert it. So this speed up optimization and remove Equilibrium
max iteration problems.
:param var: the variable we want to take the shape of
:param i: The shape dimensions we want
:param fgraph: optional. If var.fgraph do not exist, the fgraph that
have the shape_feature to introduce var in to get the optimized shape.
Parameters
----------
var
The variable we want to take the shape of.
i
The shape dimensions we want
fgraph : optional
If var.fgraph do not exist, the fgraph that have the shape_feature to
introduce var in to get the optimized shape.
"""
if fgraph is None and hasattr(var, 'fgraph'):
......@@ -421,15 +454,20 @@ def shape_i(var, i, fgraph=None):
def register_shape_i_c_code(typ, code, check_input, version=()):
""" Tell Shape_i how to generate C code for a Theano Type
:param typ: A Theano type. It must be the Theano class itself and not
an instance of the class.
:param code: C code that gets the shape of dimensions %(i)s for the
Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C
variable names respectively.
:param version: A number indicating the version of the code, for cache.
"""
Tell Shape_i how to generate C code for a Theano Type.
Parameters
----------
typ : Theano type
It must be the Theano class itself and not an instance of the class.
code : C code
Gets the shape of dimensions %(i)s for the Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C variable names
respectively.
version
A number indicating the version of the code, for cache.
"""
Shape_i.c_code_and_version[typ] = (code, check_input, version)
......@@ -459,6 +497,7 @@ class FromFunctionOp(gof.Op):
Also the gradient is undefined in the resulting op and Theano will
raise an error if you attempt to get the gradient of a graph
containing this op.
"""
def __init__(self, fn, itypes, otypes, infer_shape):
......@@ -519,29 +558,29 @@ class FromFunctionOp(gof.Op):
def as_op(itypes, otypes, infer_shape=None):
"""
Decorator that converts a function into a basic Theano op that
will call the supplied function as its implementation.
Decorator that converts a function into a basic Theano op that will call
the supplied function as its implementation.
It takes an optional infer_shape parameter that should be a
callable with this signature:
It takes an optional infer_shape parameter that should be a callable with
this signature:
def infer_shape(node, input_shapes):
...
return output_shapes
Here `input_shapes` and `output_shapes` are lists of tuples that
represent the shape of the corresponding inputs/outputs.
Here `input_shapes` and `output_shapes` are lists of tuples that represent
the shape of the corresponding inputs/outputs.
This should not be used when performance is a concern since the
very basic nature of the resulting Op may interfere with certain
graph optimizations.
This should not be used when performance is a concern since the very basic
nature of the resulting Op may interfere with certain graph optimizations.
Example usage:
Examples
--------
@as_op(itypes=[theano.tensor.fmatrix, theano.tensor.fmatrix],
otypes=[theano.tensor.fmatrix])
def numpy_dot(a, b):
return numpy.dot(a, b)
@as_op(itypes=[theano.tensor.fmatrix, theano.tensor.fmatrix],
otypes=[theano.tensor.fmatrix])
def numpy_dot(a, b):
return numpy.dot(a, b)
"""
if not isinstance(itypes, (list, tuple)):
itypes = [itypes]
......@@ -565,18 +604,19 @@ def as_op(itypes, otypes, infer_shape=None):
def register_rebroadcast_c_code(typ, code, version=()):
"""Tell Rebroadcast how to generate C code for a Theano Type
:param typ: A Theano type. It must be the Theano class itself and not an
instance of the class.
:param code: C code that checks if the dimension %(axis)s is of
shape 1 for the Theano type 'typ'. Use %(iname)s and
%(oname)s for the input and output C variable names
respectively, and %(axis)s for the axis that we need to
check. This code is put in a loop for all axes.
"""
Tell Rebroadcast how to generate C code for a Theano Type.
typ : Theano type
It must be the Theano class itself and not an instance of the class.
code : C code
That checks if the dimension %(axis)s is of shape 1 for the Theano type
'typ'. Use %(iname)s and %(oname)s for the input and output C variable
names respectively, and %(axis)s for the axis that we need to check.
This code is put in a loop for all axes.
version
A number indicating the version of the code, for cache.
:param version: A number indicating the version of the code, for cache.
"""
Rebroadcast.c_code_and_version[typ] = (code, version)
......@@ -585,17 +625,23 @@ class Rebroadcast(gof.Op):
"""
Change the input's broadcastable fields in some predetermined way.
:code:`Rebroadcast((0, True), (1, False))(x)` would make :code:`x`
broadcastable in axis 0 and not broadcastable in axis 1
See Also
--------
unbroadcast <theano.tensor.unbroadcast>
addbroadcast <theano.tensor.addbroadcast>
patternbroadcast <theano.tensor.patternbroadcast>
.. seealso::
Notes
-----
Works inplace and works for CudaNdarrayType.
:func:`unbroadcast <theano.tensor.unbroadcast>`
:func:`addbroadcast <theano.tensor.addbroadcast>`
:func:`patternbroadcast <theano.tensor.patternbroadcast>`
Example
-------
`Rebroadcast((0, True), (1, False))(x)` would make `x` broadcastable in
axis 0 and not broadcastable in axis 1.
..note: works inplace and works for CudaNdarrayType
"""
view_map = {0: [0]}
_f16_ok = True
# Mapping from Type to C code (and version) to use.
......@@ -717,17 +763,23 @@ class Rebroadcast(gof.Op):
def register_specify_shape_c_code(typ, code, version=(),
c_support_code_apply=None):
""" Tell SpecifyShape how to generate C code for a Theano Type
:param typ: A Theano type. It must be the Theano class itself and
not an instance of the class.
:param code: C code that checks the shape and returns a view for
the Theano type 'typ'. Use %(iname)s and %(oname)s
for the input and output C variable names
respectively. %(shape)s is the vector of shape of
%(iname)s. Check that its length is good.
:param version: A number indicating the version of the code, for cache.
:param c_support_code_apply: extra code.
"""
Tell SpecifyShape how to generate C code for a Theano Type.
Parameters
----------
typ : Theano type
It must be the Theano class itself and not an instance of the class.
code : C code
Checks the shape and returns a view for the Theano type 'typ'.
Use %(iname)s and %(oname)s for the input and output C variable names
respectively. %(shape)s is the vector of shape of %(iname)s.
Check that its length is good.
version
A number indicating the version of the code, for cache.
c_support_code_apply
Extra code.
"""
SpecifyShape.c_code_and_version[typ] = (code, version,
c_support_code_apply)
......@@ -742,12 +794,16 @@ class SpecifyShape(gof.Op):
the case most of the time if we only take the shape of the output.
Maybe there are other optimizations that will mess with this.
@note: Maybe in the future we will never do the assert!
@note: We currently don't support specifying partial shape information.
Notes
-----
Maybe in the future we will never do the assert!
We currently don't support specifying partial shape information.
TODO : test this op with sparse and cuda ndarray. Do C code for them too.
@todo: test this op with sparse and cuda ndarray.
Do C code for them too.
"""
view_map = {0: [0]}
# Mapping from Type to C code (and version) to use.
# In the C code, the name of the input variable is %(iname)s,
......
"""Provide a simple user friendly API """
"""
Provide a simple user friendly API.
"""
from theano import config
from six import iteritems
from theano.compile import orig_function, In, Out
......@@ -22,42 +25,35 @@ def rebuild_collect_shared(outputs,
no_default_updates=False,
):
"""
Function that allows replacing subgraphs of a computational
graph.
Function that allows replacing subgraphs of a computational graph.
It returns a set of dictionaries and lists which collect (partial?)
different information about shared variables. This info is required by
`pfunc`.
:type outputs: list of Theano Variables ( or Theano expressions)
:param outputs: list of Theano variables or expressions representing the
outputs of the computational graph
:type inputs: list of Theano Variables ( or Theano expressions)
:param inputs: list of Theano variables or expressions representing the
inputs of the computational graph (or None)
:type replace: dict
:param replace: dictionary describing which subgraphs should be
replaced by what. orig_value => new_value
:type updates: dict
:param updates: dictionary describing updates expressions for shared
variables
:type rebuild_strict: bool
:param rebuild_strict: flag, if true the type of all inputs should be
the same as the for the current node
:type copy_inputs_over: bool
:param copy_inputs_over: flag; if False it will clone inputs
:type no_default_updates: either bool or list of Variables
:param no_default_updates: if True, do not perform any automatic update
on Variables. If False (default), perform
them all. Else, perform automatic updates
on all Variables that are neither in
"updates" nor in "no_default_updates".
Parameters
----------
outputs : list of Theano Variables (or Theano expressions)
List of Theano variables or expressions representing the outputs of the
computational graph.
inputs : list of Theano Variables (or Theano expressions)
List of Theano variables or expressions representing the inputs of the
computational graph (or None).
replace : dict
Dictionary describing which subgraphs should be replaced by what.
orig_value => new_value
updates : dict
Dictionary describing updates expressions for shared variables.
rebuild_strict : bool
Flag, if true the type of all inputs should be the same as the one for
the current node.
copy_inputs_over : bool
Flag; if False it will clone inputs.
no_default_updates : either bool or list of Variables
If True, do not perform any automatic update on Variables.
If False (default), perform them all.
Else, perform automatic updates on all Variables that are neither in
"updates" nor in "no_default_updates".
"""
......@@ -73,15 +69,15 @@ def rebuild_collect_shared(outputs,
shared_inputs = []
def clone_v_get_shared_updates(v, copy_inputs_over):
'''
Clones a variable and its inputs recursively until all are in
clone_d. Also appends all shared variables met along the way to
shared inputs, and their default_update (if applicable) to update_d
and update_expr.
"""
Clones a variable and its inputs recursively until all are in clone_d.
Also appends all shared variables met along the way to shared inputs,
and their default_update (if applicable) to update_d and update_expr.
v can have an fgraph attached to it, case in which we want to clone
constants ( to avoid having a constant belonging to two fgraphs)
'''
constants (to avoid having a constant belonging to two fgraphs).
"""
# this co-recurses with clone_a
assert v is not None
if v in clone_d:
......@@ -119,10 +115,11 @@ def rebuild_collect_shared(outputs,
return clone_d.setdefault(v, v)
def clone_a(a, copy_inputs_over):
'''
"""
Clones a variable and its inputs recursively until all are in
clone_d. It occures with clone_v_get_shared_updates
'''
clone_d. It occures with clone_v_get_shared_updates.
"""
if a is None:
return None
if a not in clone_d:
......@@ -275,40 +272,43 @@ def rebuild_collect_shared(outputs,
class Param(object):
def __init__(self, variable, default=None, name=None, mutable=False,
strict=False, allow_downcast=None, implicit=None,
borrow=None):
"""
:param variable: A variable in an expression graph to use as a
compiled-function parameter
:param default: The default value to use at call-time (can
also be a Container where the function will find a value
at call-time.)
:param name: A string to identify this parameter from function kwargs.
:param mutable: True -> function is allowed to modify this argument.
:param borrow: Whether the function is allowed to alias some
output to this input. Using None (default) means we re-use
the same value as the `mutable` flag.
False: do not permit any output to be aliased to the input
"""
:param strict: False -> function arguments may be copied or
cast to match the type required by the parameter
`variable`.
True -> function arguments must exactly match the type
required by `variable`.
Parameters
----------
variable
A variable in an expression graph to use as a compiled-function
parameter.
default
The default value to use at call-time (can also be a Container where
the function will find a value at call-time).
name : str
A string to identify this parameter from function kwargs.
mutable : bool
True : function is allowed to modify this argument.
borrow
Whether the function is allowed to alias some output to this input.
Using None (default) means we re-use the same value as the `mutable`
flag. False: do not permit any output to be aliased to the input.
strict : bool
False : function arguments may be copied or cast to match the type
required by the parameter `variable`.
True : function arguments must exactly match the type required by
`variable`.
allow_downcast : bool or None
Only applies if `strict` is False.
True : allow assigned value to lose precision when cast during
assignment.
False : never allow precision loss.
None : only allow downcasting of a Python float to a scalar floatX.
implicit
See help(theano.io.In)
:param allow_downcast: Only applies if `strict` is False.
True -> allow assigned value to lose precision when cast
during assignment.
False -> never allow precision loss.
None -> only allow downcasting of a Python float to a scalar floatX.
"""
:param implicit: see help(theano.io.In)
"""
def __init__(self, variable, default=None, name=None, mutable=False,
strict=False, allow_downcast=None, implicit=None,
borrow=None):
self.variable = variable
self.default = default
self.name = name
......@@ -340,75 +340,61 @@ def pfunc(params, outputs=None, mode=None, updates=None, givens=None,
no_default_updates=False, accept_inplace=False, name=None,
rebuild_strict=True, allow_input_downcast=None,
profile=None, on_unused_input=None, output_keys=None):
"""Function-constructor for graphs with shared variables.
:type params: list of either Variable or Param instances.
:param params: function parameters, these are not allowed to be shared
variables
:type outputs: list of Variables or Out instances
:param outputs: expressions to compute
:type mode: string or `theano.compile.Mode` instance.
:param mode: compilation mode
:type updates: iterable over pairs (shared_variable,
new_expression). List, tuple or dict.
:param updates: update the values for SharedVariable inputs
according to these expressions
:type givens: iterable over pairs (Var1, Var2) of Variables. List,
tuple or dict. The Var1 and Var2 in each pair must have the
same Type.
:param givens: specific substitutions to make in the computation
graph (Var2 replaces Var1).
:type no_default_updates: either bool or list of Variables
:param no_default_updates: if True, do not perform any automatic
update on Variables. If False (default), perform them
all. Else, perform automatic updates on all Variables that are
neither in "updates" nor in "no_default_updates".
:type name: None or string
:param name: attaches a name to the profiling result of this function.
:type allow_input_downcast: Boolean
:param allow_input_downcast: True means that the values passed as
inputs when calling the function can be silently downcasted to
fit the dtype of the corresponding Variable, which may lose
precision. False means that it will only be cast to a more
"""
Function-constructor for graphs with shared variables.
Parameters
----------
params : list of either Variable or Param instances
Function parameters, these are not allowed to be shared variables.
outputs : list of Variables or Out instances
Expressions to compute.
mode : string or `theano.compile.Mode` instance
Compilation mode.
updates : iterable over pairs (shared_variable, new_expression). List, tuple or dict.
Update the values for SharedVariable inputs according to these
expressions
givens : iterable over pairs (Var1, Var2) of Variables. List, tuple or dict.
The Var1 and Var2 in each pair must have the same Type. Specific
substitutions to make in the computation graph (Var2 replaces Var1).
no_default_updates : either bool or list of Variables
If True, do not perform any automatic update on Variables.
If False (default), perform them all. Else, perform automatic updates
on all Variables that are neither in "updates" nor in
"no_default_updates".
name : None or string
Attaches a name to the profiling result of this function.
allow_input_downcast : bool
True means that the values passed as inputs when calling the function
can be silently downcasted to fit the dtype of the corresponding
Variable, which may lose precision. False means that it will only be cast to a more
general, or precise, type. None (default) is almost like
False, but allows downcasting of Python float scalars to
floatX.
profile : None, True, str, or ProfileStats instance
Accumulate profiling information into a given ProfileStats instance.
None is the default, and means to use the value of config.profile.
If argument is `True` then a new ProfileStats instance will be used.
If argument is a string, a new ProfileStats instance will be created
with that string as its `message` attribute. This profiling object will
be available via self.profile.
on_unused_input : {'raise', 'warn','ignore', None}
What to do if a variable in the 'inputs' list is not used in the graph.
Returns
-------
theano.compile.Function
A callable object that will compute the outputs (given the inputs) and
update the implicit function arguments according to the `updates`.
Notes
-----
Regarding givens: Be careful to make sure that these substitutions are
independent--behaviour when Var1 of one pair appears in the graph leading
to Var2 in another expression is undefined. Replacements specified with
givens are different from optimizations in that Var2 is not expected to be
equivalent to Var1.
:type profile: None, True, str, or ProfileStats instance
:param profile: accumulate profiling information into a given ProfileStats
instance. None is the default, and means to use the value of
config.profile.
If argument is `True` then a new ProfileStats instance will be
used. If argument is a string, a new ProfileStats instance will be created
with that string as its `message` attribute. This profiling object will be
available via self.profile.
:type on_unused_input: str
:param on_unused_input: What to do if a variable in the 'inputs' list
is not used in the graph. Possible values are 'raise', 'warn',
'ignore' and None.
:rtype: theano.compile.Function
:returns: a callable object that will compute the outputs (given
the inputs) and update the implicit function arguments
according to the `updates`.
:note: Regarding givens: Be careful to make sure that these
substitutions are independent--behaviour when Var1 of one pair
appears in the graph leading to Var2 in another expression is
undefined. Replacements specified with givens are different
from optimizations in that Var2 is not expected to be
equivalent to Var1.
"""
#
# This function works by cloning the graph (except for the
......@@ -547,13 +533,17 @@ def iter_over_pairs(pairs):
"""
Return an iterator over pairs present in the 'pairs' input.
:type pairs: dictionary or iterable
:param pairs: The pairs to iterate upon. These may be stored either as
(key, value) items in a dictionary, or directly as pairs in any kind of
iterable structure
:rtype: iterable
:returns: an iterable yielding pairs
Parameters
----------
pairs : dictionary or iterable
The pairs to iterate upon. These may be stored either as (key, value)
items in a dictionary, or directly as pairs in any kind of iterable
structure.
Returns
-------
iterable
An iterable yielding pairs.
"""
if isinstance(pairs, dict):
......
......@@ -122,7 +122,10 @@ class ProfileMode(Mode):
profile_stats))
def function_maker(self, i, o, m, *args, **kwargs):
"""Return an instance of `Profiler_Maker` which init the count"""
"""
Return an instance of `Profiler_Maker` which init the count.
"""
assert m is self
return Profile_Maker(i, o, self, *args, **kwargs)
......@@ -147,7 +150,9 @@ class ProfileMode(Mode):
self.profile_stats = profile_stats
def profile_thunk(i, node, th):
""" Profile only the execution time
"""
Profile only the execution time.
"""
global run_cthunk
if hasattr(th, 'cthunk'):
......@@ -169,7 +174,9 @@ class ProfileMode(Mode):
self.apply_time[node] += max(dt, 1e-14)
def profile_thunk2(i, node, th):
""" Profile the execution time and the memory size.
"""
Profile the execution time and the memory size.
"""
global run_cthunk
if hasattr(th, 'cthunk'):
......@@ -211,7 +218,8 @@ class ProfileMode(Mode):
self.fn_time = 0
def print_summary(self, **kwargs):
""" Print 3 summaries that show where time is spent. The first shows
"""
Print 3 summaries that show where time is spent. The first shows
an Apply-wise summary, the second an Op-wise summary and the
third a type-Op-wise summary.
......@@ -235,10 +243,13 @@ class ProfileMode(Mode):
There is an hack with the Op-wise summary. Go see it if you
want to know more.
:param kwargs: They are passed to print_summary_ expanded.
Currently there is n_apply_to_print,
n_ops_to_print and min_memory_size that are
accepted.
Parameters
----------
kwargs
They are passed to print_summary_ expanded. Currently there is
n_apply_to_print, n_ops_to_print and min_memory_size that are
accepted.
"""
compile_time = sum([ps.compile_time for ps
in self.profile_stats.values()])
......@@ -280,18 +291,23 @@ class ProfileMode(Mode):
**kwargs)
def print_diff_summary(self, other, **kwargs):
""" As print_summary, but print the difference on two different
"""
As print_summary, but print the difference on two different
profile mode.
TODO: Also we don't print the Apply-wise summary as it don't
work for now.
TODO: make comparaison with gpu code.
:param other: the other instance of ProfileMode that we want
to be compared to.
:param kwargs: They are passed to print_summary_ expanded.
Parameters
----------
other
The other instance of ProfileMode that we want to be compared to.
kwargs
They are passed to print_summary_ expanded.
Currently there is n_apply_to_print, n_ops_to_print and
min_memory_size that are accepted.
"""
def diff_dict(a_time, b_time_):
......@@ -343,13 +359,18 @@ class ProfileMode(Mode):
min_memory_size=config.ProfileMode.min_memory_size,
):
"""
do the actual printing of print_summary and print_diff_summary.
:param n_apply_to_print: the number of apply to print. Default 15.
Do the actual printing of print_summary and print_diff_summary.
Parameters
----------
n_apply_to_print
The number of apply to print. Default 15.
n_ops_to_print
The number of ops to print. Default 20.
min_memory_size
Don't print memory profile of apply whose outputs memory size is
lower than that.
:param n_ops_to_print: the number of ops to print. Default 20.
:param min_memory_size: Don't print memory profile of apply
whose outputs memory size is lower then that.
"""
print("ProfileMode is deprecated! Use the new profiler.")
......@@ -700,9 +721,9 @@ Test them first, as they are not guaranteed to always provide a speedup.""")
"""
Create a new instance of this Mode.
Keyword arguments can be provided for the linker,
in which case its `clone` method will be called with these
arguments.
Keyword arguments can be provided for the linker, in which case its
`clone` method will be called with these arguments.
"""
new_linker = self.linker.clone(**link_kwargs)
new_optimizer = self.provided_optimizer
......@@ -727,10 +748,11 @@ prof_mode_instance_to_print = [predefined_modes["PROFILE_MODE"]]
def atexit_print_default_profile_mode():
"""Print the summary of the predefined mode ProfileMode if used.
"""
Print the summary of the predefined mode ProfileMode if used.
This all to have the summary printed at exit when config.mode=ProfileMode.
This all to have the summary printed at exit when
config.mode=ProfileMode
"""
for prof_mode in prof_mode_instance_to_print:
if prof_mode.local_time > 0:
......
"""ProfileStats object for runtime and memory profiling.
"""
ProfileStats object for runtime and memory profiling.
"""
from __future__ import print_function
#
......@@ -76,7 +78,9 @@ AddConfigVar('profiling.destination',
def _atexit_print_fn():
"""Print ProfileStat objects in _atexit_print_list to _atexit_print_file
"""
Print ProfileStat objects in _atexit_print_list to _atexit_print_file.
"""
to_sum = []
......@@ -135,6 +139,16 @@ class ProfileStats(object):
"""
Object to store runtime and memory profiling information for all of
Theano's operations: compilation, optimization, execution.
Parameters
----------
atexit_print : bool
True means that this object will be printed to stderr (using .summary())
at the end of the program.
**kwargs : misc initializers
These should (but need not) match the names of the class vars declared
in this class.
"""
#
......@@ -212,12 +226,6 @@ class ProfileStats(object):
# param is called flag_time_thunks because most other attributes with time
# in the name are times *of* something, rather than configuration flags.
def __init__(self, atexit_print=True, flag_time_thunks=None, **kwargs):
"""
atexit_print - bool. True means that this object will be printed to
stderr (using .summary()) at the end of the program.
**kwargs - misc initializers. These should (but need not) match the
names of the class vars declared in this class.
"""
if (hasattr(theano, 'sandbox') and
hasattr(theano.sandbox, 'cuda') and
theano.sandbox.cuda.cuda_enabled):
......@@ -250,7 +258,10 @@ class ProfileStats(object):
_atexit_registered = True
def class_time(self):
"""dict op -> total time on thunks"""
"""
dict op -> total time on thunks
"""
# timing is stored by node, we compute timing by class on demand
rval = {}
for node, t in iteritems(self.apply_time):
......@@ -260,7 +271,10 @@ class ProfileStats(object):
return rval
def class_callcount(self):
"""dict op -> total number of thunk calls"""
"""
dict op -> total number of thunk calls
"""
# timing is stored by node, we compute timing by class on demand
rval = {}
for node, count in iteritems(self.apply_callcount):
......@@ -270,7 +284,10 @@ class ProfileStats(object):
return rval
def class_nodes(self):
"""dict op -> total number of nodes"""
"""
dict op -> total number of nodes
"""
# timing is stored by node, we compute timing by class on demand
rval = {}
for node, count in iteritems(self.apply_callcount):
......@@ -280,7 +297,10 @@ class ProfileStats(object):
return rval
def class_impl(self):
"""dict op -> total number of nodes"""
"""
dict op -> total number of nodes
"""
# timing is stored by node, we compute timing by class on demand
rval = {}
for node in self.apply_callcount:
......@@ -295,7 +315,10 @@ class ProfileStats(object):
return rval
def op_time(self):
"""dict op -> total time on thunks"""
"""
dict op -> total time on thunks
"""
# timing is stored by node, we compute timing by Op on demand
rval = {}
for node, t in iteritems(self.apply_time):
......@@ -304,7 +327,10 @@ class ProfileStats(object):
return rval
def fill_node_total_time(self, node, total_times):
"""node -> fill total time icluding its parents (returns nothing)"""
"""
node -> fill total time icluding its parents (returns nothing)
"""
# timing is stored by node, we compute total time on demand
total = self.apply_time[node]
for parent in node.get_parents():
......@@ -315,7 +341,10 @@ class ProfileStats(object):
total_times[node] = total
def compute_total_times(self):
"""dict op -> total time icluding the time for parents"""
"""
dict op -> total time icluding the time for parents
"""
rval = {}
for node in self.apply_time:
if node not in rval:
......@@ -323,7 +352,10 @@ class ProfileStats(object):
return rval
def op_callcount(self):
"""dict op -> total number of thunk calls"""
"""
dict op -> total number of thunk calls
"""
# timing is stored by node, we compute timing by Op on demand
rval = {}
for node, count in iteritems(self.apply_callcount):
......@@ -332,7 +364,10 @@ class ProfileStats(object):
return rval
def op_nodes(self):
"""dict op -> total number of nodes"""
"""
dict op -> total number of nodes
"""
# timing is stored by node, we compute timing by Op on demand
rval = {}
for node, count in iteritems(self.apply_callcount):
......@@ -341,7 +376,10 @@ class ProfileStats(object):
return rval
def op_impl(self):
"""dict op -> 'C' or 'Py' depending how the op is implemented"""
"""
dict op -> 'C' or 'Py' depending how the op is implemented
"""
# timing is stored by node, we compute timing by Op on demand
rval = {}
for node in self.apply_callcount:
......@@ -711,21 +749,23 @@ class ProfileStats(object):
def count_running_memory(order, fgraph, nodes_mem):
"""
Calculate memory with specific node order
Calculate memory with specific node order.
Return a list including the following values
1. node_memory_size
Sum of the size of all variables that actually allocate
memory (excluding views, and inplace);
2. running_memory_size
The memory allocated after the current apply node
3. running_max_memory_size
The maximum of running_memory_size during the function
memory (excluding views, and inplace).
2. running_memory_size
The memory allocated after the current apply node.
3. running_max_memory_size
The maximum of running_memory_size during the function.
4. node_memory_saved_by_view
The sum of memory saved by returning view instead of new
allocation
allocation.
5. node_memory_saved_by_inplace
The sum of memory saved by reusing the input instead of
new allocation
new allocation.
"""
from theano.sandbox.cuda import CudaNdarrayType
# Initial Mem info values [CPU, GPU]
......@@ -874,10 +914,14 @@ class ProfileStats(object):
def min_memory_generator(executable_nodes, viewed_by, view_of):
"""
Generate all valid node order from node_list
and compute its memory peak.
Generate all valid node order from node_list and compute its
memory peak.
Parameters
----------
executable_nodes
Set of executable nodes.
:param executable_nodes: Set of executable nodes
"""
global mem_count, mem_bound, max_mem_count
......@@ -1255,9 +1299,13 @@ if False: # old code still to be ported from ProfileMode
"""
Print a readable summary of the stats.
param: n_apply_to_print the number of apply to print. Default 15.
Parameters
----------
n_apply_to_print
The number of apply to print. Default 15.
n_ops_to_print
The number of ops to print. Default 20.
param: n_ops_to_print the number of ops to print. Default 20.
"""
local_time = sum(self.apply_time.values())
......@@ -1483,11 +1531,13 @@ if False: # old code still to be ported from ProfileMode
There is a hack with the Op-wise summary. Go see it if you want to know
more.
:param n_apply_to_print: the number of apply to print. Default 15, or
n_ops_to_print flag.
Parameters
----------
n_apply_to_print
The number of apply to print. Default 15, or n_ops_to_print flag.
n_ops_to_print
The number of ops to print. Default 20, or n_apply_to_print flag.
:param n_ops_to_print: the number of ops to print. Default 20, or
n_apply_to_print flag.
"""
fct_call_time = self.mode.fct_call_time
fct_call = self.mode.fct_call
......@@ -1517,12 +1567,15 @@ if False: # old code still to be ported from ProfileMode
now.
TODO: make comparaison with gpu code.
:param other: the other instance of ProfileMode that we want to be
compared to.
:param n_apply_to_print: the number of apply to print. Default 15.
Parameters
----------
other
The other instance of ProfileMode that we want to be compared to.
n_apply_to_print
The number of apply to print. Default 15.
n_ops_to_print
The number of ops to print. Default 20.
:param n_ops_to_print: the number of ops to print. Default 20.
"""
def diff_dict(a_time, b_time_):
......
"""Provide a simple user friendly API to Theano-managed memory"""
"""
Provide a simple user friendly API to Theano-managed memory.
"""
# Standard imports
import copy
import logging
......@@ -18,6 +21,32 @@ class SharedVariable(Variable):
Variable that is (defaults to being) shared between functions that
it appears in.
Parameters
----------
name : str
The name for this variable (see `Variable`).
type : str
The type for this variable (see `Variable`).
value
A value to associate with this variable (a new container will be
created).
strict
True : assignments to .value will not be cast or copied, so they must
have the correct type.
allow_downcast
Only applies if `strict` is False.
True : allow assigned value to lose precision when cast during
assignment.
False : never allow precision loss.
None : only allow downcasting of a Python float to a scalar floatX.
container
The container to use for this variable. Illegal to pass this as well as
a value.
Notes
-----
For more user-friendly constructor, see `shared`.
"""
# Container object
......@@ -36,29 +65,6 @@ class SharedVariable(Variable):
def __init__(self, name, type, value, strict,
allow_downcast=None, container=None):
"""
:param name: The name for this variable (see `Variable`).
:param type: The type for this variable (see `Variable`).
:param value: A value to associate with this variable (a new
container will be created).
:param strict: True -> assignments to .value will not be cast
or copied, so they must have the correct type.
:param allow_downcast: Only applies if `strict` is False.
True -> allow assigned value to lose precision when cast
during assignment.
False -> never allow precision loss.
None -> only allow downcasting of a Python float to a scalar floatX.
:param container: The container to use for this
variable. Illegal to pass this as well as a value.
:note: For more user-friendly constructor, see `shared`
"""
super(SharedVariable, self).__init__(type=type, name=name,
owner=None, index=None)
......@@ -79,18 +85,21 @@ class SharedVariable(Variable):
allow_downcast=allow_downcast)
def get_value(self, borrow=False, return_internal_type=False):
"""Get the non-symbolic value associated with this SharedVariable.
"""
Get the non-symbolic value associated with this SharedVariable.
:param borrow: True to permit returning of an object aliased
to internal memory.
:param return_internal_type: True to permit the returning of
an arbitrary type object used internally to store the
shared variable.
Parameters
----------
borrow : bool
True to permit returning of an object aliased to internal memory.
return_internal_type : bool
True to permit the returning of an arbitrary type object used
internally to store the shared variable.
Only with borrow=False and return_internal_type=True does this
function guarantee that you actually get the internal object.
But in that case, you may get different return types when
using different compute devices.
Only with borrow=False and return_internal_type=True does this function
guarantee that you actually get the internal object.
But in that case, you may get different return types when using
different compute devices.
"""
if borrow:
......@@ -99,14 +108,18 @@ class SharedVariable(Variable):
return copy.deepcopy(self.container.value)
def set_value(self, new_value, borrow=False):
"""Set the non-symbolic value associated with this SharedVariable.
"""
Set the non-symbolic value associated with this SharedVariable.
:param borrow:
Parameters
----------
borrow : bool
True to use the new_value directly, potentially creating problems
related to aliased memory.
Changes to this value will be visible to all functions using
this SharedVariable.
"""
if borrow:
self.container.value = new_value
......@@ -114,15 +127,19 @@ class SharedVariable(Variable):
self.container.value = copy.deepcopy(new_value)
def zero(self, borrow=False):
"""Set the values of a shared variable to 0.
"""
Set the values of a shared variable to 0.
:param borrow:
Parameters
----------
borrow : bbol
True to modify the value of a shared variable directly by using
its previous value. Potentially this can cause problems
regarding to the aliased memory.
Changes done with this function will be visible to all functions using
this SharedVariable.
"""
if borrow:
self.container.value[...] = 0
......@@ -183,7 +200,8 @@ def shared_constructor(ctor, remove=False):
def shared(value, name=None, strict=False, allow_downcast=None, **kwargs):
"""Return a SharedVariable Variable, initialized with a copy or
"""
Return a SharedVariable Variable, initialized with a copy or
reference of `value`.
This function iterates over
......@@ -196,23 +214,25 @@ def shared(value, name=None, strict=False, allow_downcast=None, **kwargs):
``theano.shared`` is a shortcut to this function.
:note: By passing kwargs, you effectively limit the set of
potential constructors to those that can accept those kwargs.
Notes
-----
By passing kwargs, you effectively limit the set of potential constructors
to those that can accept those kwargs.
:note: Some shared variable have ``borrow`` as extra kwargs.
`See <http://deeplearning.net/software/theano/tutorial/aliasing.\
html#borrowing-when-creating-shared-variables>`_ for detail.
Some shared variable have ``borrow`` as extra kwargs.
`See <http://deeplearning.net/software/theano/tutorial/aliasing.\
html#borrowing-when-creating-shared-variables>`_ for details.
:note: Some shared variable have ``broadcastable`` as extra kwargs.
As shared variable shapes can change, all dimensions default
to not being broadcastable, even if ``value`` has a shape of 1
along some dimension. This parameter allows you to create
for example a `row` or `column` 2d tensor.
Some shared variable have ``broadcastable`` as extra kwargs. As shared
variable shapes can change, all dimensions default to not being
broadcastable, even if ``value`` has a shape of 1 along some dimension.
This parameter allows you to create for example a `row` or `column` 2d
tensor.
.. attribute:: constructors
A list of shared variable constructors that will be tried in reverse
order.
A list of shared variable constructors that will be tried in reverse
order.
"""
......@@ -251,6 +271,9 @@ shared.constructors = []
@shared_constructor
def generic_constructor(value, name=None, strict=False, allow_downcast=None):
"""SharedVariable Constructor"""
"""
SharedVariable Constructor.
"""
return SharedVariable(type=generic, value=value, name=name, strict=strict,
allow_downcast=allow_downcast)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论