提交 69860a07 authored 作者: James Bergstra's avatar James Bergstra

merge

......@@ -135,7 +135,7 @@ class _test_CAReduce(unittest.TestCase):
((2, 3, 4, 5), (0, 1, 3)),
((), ())]:
x = modes.build(Tensor('float64', [1 * (entry == 1) for entry in xsh], name = 'x'))
e = CAReduce(Add, [x], dimensions_to_reduce = tosum).out
e = CAReduce(Add, [x], axis = tosum).out
f = linker(env([x], [e])).make_function(inplace = False)
xv = numpy.asarray(numpy.random.rand(*xsh))
zv = xv
......
......@@ -402,7 +402,7 @@ def make_broadcast(scalar_opclass, inplace_pattern = {}, name = None):
scalar_name = scalar_opclass.__name__
previous_doc = Broadcast.__doc__
scalar_doc = scalar_opclass.__doc__
scalar_doc = scalar_opclass.__doc__ or ""
if scalar_doc:
scalar_doc = """
%(scalar_name)s documentation:
......@@ -411,7 +411,7 @@ def make_broadcast(scalar_opclass, inplace_pattern = {}, name = None):
doc = """
Usage: %(name)s(*inputs)
Equivalent to: Broadcast(%(scalar_name)s, inputs, %(inplace_pattern)s)
Equivalent to: Broadcast(scalar.%(scalar_name)s, inputs, %(inplace_pattern)s)
Performs Scalar %(scalar_name)s on each element of the
input tensors.
......@@ -460,15 +460,16 @@ def wrap_broadcast(op):
class CAReduce(Op):
"""
Usage: CAReduce(scalar_opclass, inputs, dimensions_to_reduce = None)
Usage: CAReduce(scalar_opclass, inputs, axis = None)
* scalar_opclass: a binary scalar op with only one output.
It will be instantiated as such:
scalar_opclass.__init__([Scalar(t.dtype) for t in inputs])
It must be commutative and associative.
* inputs: list of Tensor instances
* dimensions_to_reduce: list of dimensions that we want to reduce
if None, all dimensions are reduced
* axis: - the dimension along which we want to reduce
- list of dimensions that we want to reduce
- if None, all dimensions are reduced
The output will have the same shape as the input minus the reduced
dimensions. It will contain the result of accumulating all values
......@@ -489,7 +490,7 @@ class CAReduce(Op):
or/and/xor - but not subtract, divide or power).
"""
def __init__(self, scalar_opclass, inputs, dimensions_to_reduce = None):
def __init__(self, scalar_opclass, inputs, axis = None):
inputs = map(astensor, inputs)
self.shadow = scalar_opclass(*[Scalar(dtype = inputs[0].dtype) for i in xrange(len(inputs) + 1)])
......@@ -498,32 +499,34 @@ class CAReduce(Op):
raise NotImplementedError("CAReduce only supports binary functions with a single output.")
if len(inputs) != 1:
raise TypeError("Only one argument expected.")
if dimensions_to_reduce is None:
dimensions_to_reduce = range(len(inputs[0].broadcastable))
if axis is None:
axis = range(len(inputs[0].broadcastable))
elif isinstance(axis, int):
axis = [axis]
self.inputs = inputs
self.outputs = [Tensor(dtype = inputs[0].dtype,
broadcastable = [x for i, x in enumerate(inputs[0].broadcastable) if i not in dimensions_to_reduce])]
broadcastable = [x for i, x in enumerate(inputs[0].broadcastable) if i not in axis])]
self.dimensions_to_reduce = dimensions_to_reduce
self.axis = axis
self.scalar_opclass = scalar_opclass
self.ufunc = numpy.frompyfunc(self.shadow.impl, self.shadow.nin, self.shadow.nout)
def desc(self):
return (self.__class__, self.scalar_opclass, tuple(self.dimensions_to_reduce))
return (self.__class__, self.scalar_opclass, tuple(self.axis))
def strdesc(self):
if set(self.dimensions_to_reduce) != set(xrange(len(self.inputs[0].broadcastable))):
return "Reduce{%s}{%s}" % (self.scalar_opclass.__name__, "".join(str(x) for x in self.dimensions_to_reduce))
if set(self.axis) != set(xrange(len(self.inputs[0].broadcastable))):
return "Reduce{%s}{%s}" % (self.scalar_opclass.__name__, "".join(str(x) for x in self.axis))
else:
return "Reduce{%s}" % self.scalar_opclass.__name__
def clone_with_new_inputs(self, *new_inputs):
return CAReduce(self.scalar_opclass, new_inputs, self.dimensions_to_reduce)
return CAReduce(self.scalar_opclass, new_inputs, self.axis)
def perform(self):
result = self.inputs[0].data
to_reduce = reversed(sorted(self.dimensions_to_reduce))
to_reduce = reversed(sorted(self.axis))
if to_reduce:
for dimension in to_reduce:
result = self.ufunc.reduce(result, dimension)
......@@ -542,7 +545,7 @@ class CAReduce(Op):
idtype = input.dtype_specs()[1]
odtype = output.dtype_specs()[1]
tosum = self.dimensions_to_reduce
tosum = self.axis
if tosum == ():
return Broadcast(scalar.Identity, (input, ))._c_all(inames, onames, sub)
......@@ -604,7 +607,7 @@ class CAReduce(Op):
def __str__(self):
input = self.inputs[0]
if len(input.broadcastable) == len(self.dimensions_to_reduce):
if len(input.broadcastable) == len(self.axis):
return "%s:%s(%s)" % (self.__class__.__name__,
self.scalar_opclass.__name__,
str(input))
......@@ -612,7 +615,7 @@ class CAReduce(Op):
return "%s:%s(%s, axis = %s)" % (self.__class__.__name__,
self.scalar_opclass.__name__,
str(input),
self.dimensions_to_reduce)
self.axis)
......@@ -645,16 +648,16 @@ def make_reduce(scalar_opclass, name = None):
def __init__(self, *inputs, **kwargs):
reducer.__init__(self, scalar_opclass, inputs, kwargs.get('axis', None))
def clone_with_new_inputs(self, *new_inputs):
return New(*new_inputs, **dict(axis = self.dimensions_to_reduce))
return New(*new_inputs, **dict(axis = self.axis))
def __str__(self):
input = self.inputs[0]
if len(input.broadcastable) == len(self.dimensions_to_reduce):
if len(input.broadcastable) == len(self.axis):
return "%s(%s)" % (self.__class__.__name__,
str(input))
else:
return "%s(%s, axis = %s)" % (self.__class__.__name__,
str(input),
self.dimensions_to_reduce)
self.axis)
New.__name__ = name
return New
......@@ -662,12 +665,12 @@ _Sum = make_reduce(scalar.Add, '_Sum')
class Sum(_Sum):
__doc__ = _Sum.__doc__
def grad(self, (x, ), (gz, )):
if self.dimensions_to_reduce == ():
if self.axis == ():
return gz,
new_dims = []
i = 0
for j, _ in enumerate(x.broadcastable):
if j in self.dimensions_to_reduce:
if j in self.axis:
new_dims.append('x')
else:
new_dims.append(i)
......@@ -681,7 +684,7 @@ def reduce(op):
else:
raise NotImplementedError("The scalar op class to reduce must be commutative and associative.")
def instantiate(*inputs):
return reducer(op, inputs, dimensions_to_reduce)
return reducer(op, inputs, axis)
return instantiate
......
......@@ -421,7 +421,7 @@ class CLinker(Linker):
# orphans are not inputs so we'll just get fetch them when we initialize the struct and assume they stay the same
policy = [[get_c_declare, get_c_extract, get_c_cleanup],
[get_nothing, get_nothing, get_nothing]]
elif result in self.temps or not reuse_storage:
elif result in self.temps:
# temps don't need to be extracted from Python, so we call c_init rather than c_extract
# they do not need to be relayed to Python, so we don't sync
if result.c_is_simple() or not reuse_storage:
......@@ -441,6 +441,8 @@ class CLinker(Linker):
# it is useful for complex outputs to reuse storage at each run, so we only clean up in the destructor
policy = [[get_c_declare, get_c_init, get_c_cleanup],
[get_nothing, get_nothing, get_c_sync]]
else:
raise Exception("what the fuck")
builder, block = struct_result_codeblocks(result, policy, id, symbol, sub)
......
......@@ -155,12 +155,6 @@ class Env(graph.Graph):
"""
if feature_class in self._features:
return # the feature is already present
else:
for other_feature_class in self._features:
if issubclass(other_feature_class, feature_class):
return
elif issubclass(feature_class, other_feature_class):
self.__del_feature__(other_feature_class)
self.__add_feature__(feature_class, do_import)
def __add_feature__(self, feature_class, do_import):
......@@ -193,14 +187,7 @@ class Env(graph.Graph):
pass
def get_feature(self, feature_class):
try:
return self._features[feature_class]
except KeyError:
for other_feature_class in self._features:
if issubclass(other_feature_class, feature_class):
return self._features[other_feature_class]
else:
raise
return self._features[feature_class]
def has_feature(self, feature_class):
try:
......@@ -213,6 +200,18 @@ class Env(graph.Graph):
"Same as len(self.clients(r))."
return len(self.clients(r))
def edge(self, r):
return r in self.inputs or r in self.orphans()
def follow(self, r):
op = r.owner
if self.edge(r):
return None
else:
if op is None:
raise Exception("what the fuck")
return op.inputs
def ops(self):
"All ops within the subgraph bound by env.inputs and env.outputs."
return self._ops
......
......@@ -67,11 +67,9 @@ class DimShuffleLifter(opt.Optimizer):
if r in seen:
return
seen.add(r)
op = r.owner
if op is None \
or op in env.inputs \
or op in env.orphans():
if env.edge(r):
return
op = r.owner
if isinstance(op, DimShuffle):
in_op = op.inputs[0].owner
if isinstance(in_op, DimShuffle):
......@@ -121,9 +119,7 @@ def find_cliques(env, through_broadcast = False):
# is False) a Result which needs to be broadcasted.
op = r.owner
if r in env.inputs \
or r in env.orphans() \
or op is None \
if env.edge(r) \
or not isinstance(op, Broadcast) \
or len(op.outputs) > 1:
# todo: handle multiple-output broadcast ops
......@@ -155,7 +151,7 @@ def find_cliques(env, through_broadcast = False):
cliques = []
def find_cliques_helper(r):
if r in env.inputs or r in env.orphans():
if env.edge(r):
return
clique_inputs = seek_from(r)
if clique_inputs is None:
......@@ -218,7 +214,7 @@ class CliqueOptimizer(opt.Optimizer):
if r in equiv:
return equiv[r]
op = r.owner
if r in env.inputs or r in env.orphans():
if env.edge(r):
# For each leave we make a Scalar of the corresponding dtype
s = scalar.Scalar(dtype = r.dtype)
_r = r
......
......@@ -71,17 +71,22 @@ class Canonizer(gof.Optimizer):
def canonize(r):
if r in env.inputs or r in env.orphans():
# if r in env.inputs or r in env.orphans():
# return
next = env.follow(r)
if next is None:
return
def flatten(r, nclients_check = True):
# Collapses a tree of main/inverse/reciprocal Ops (aka Mul/Div/Inv or Add/Sub/Neg)
# into a list of numerators and a list of denominators
# e.g. (x*(1/y))*(x/(z/a)) aka Mul(Mul(x, (Inv, y)), Div(x, Div(z, a))) -> [x, x, a], [z, y]
op = r.owner
if op is None or r in env.inputs or r in env.orphans():
if env.edge(r):
return [r], []
op = r.owner
# if op is None or r in env.inputs or r in env.orphans():
# return [r], []
results = [r2.dtype == r.dtype and flatten(r2) or ([r2], []) for r2 in op.inputs]
if isinstance(op, self.main) and (not nclients_check or env.nclients(r) == 1):
......@@ -103,12 +108,15 @@ class Canonizer(gof.Optimizer):
num, denum = flatten(r, False)
if (num, denum) == ([r], []):
if r.owner is None:
return
else:
for input in r.owner.inputs:
canonize(input)
return
for input in (env.follow(r) or []):
canonize(input)
return
# if r.owner is None:
# return
# else:
# for input in r.owner.inputs:
# canonize(input)
# return
# Terms that are both in the num and denum lists cancel each other
for d in list(denum):
......@@ -194,7 +202,7 @@ def group_powers(env, num, denum):
# and does d[base].append(power).
for factor in list(seq):
op = factor.owner
if op is None or factor in env.inputs or factor in env.orphans():
if env.edge(factor):
continue
if isinstance(op, Exp):
d.setdefault('e', []).append(op.inputs[0])
......
......@@ -14,6 +14,8 @@ import blas # for gemm, dot
import elemwise as s2t
import scalar as scal
from functools import partial
class Tensor(BaseTensor):
"""
......@@ -100,6 +102,49 @@ def astensor(data, broadcastable=None, name=None):
s2t.astensor = astensor
# Easy constructors
def _multi(*fns):
def f2(f, names):
if len(names) == 1:
return f(names)
else:
return [f(name) for name in names]
if len(fns) == 1:
return partial(f2, fns)
else:
return [partial(f2, f) for f in fns]
def _int_float(f):
return partial(f, dtype = 'int64'), partial(f, dtype = 'float64')
def scalar(name, dtype = 'float64'):
return Tensor(name = name, dtype = dtype, broadcastable = ())
iscalar, fscalar = _int_float(scalar)
scalars, iscalars, fscalars = _multi(scalar, iscalar, fscalar)
def vector(name, dtype = 'float64'):
return Tensor(name = name, dtype = dtype, broadcastable = (False))
ivector, fvector = _int_float(vector)
vectors, ivectors, fvectors = _multi(vector, ivector, fvector)
def matrix(name, dtype = 'float64'):
return Tensor(name = name, dtype = dtype, broadcastable = (False, False))
imatrix, fmatrix = _int_float(matrix)
matrices, imatrices, fmatrices = _multi(matrix, imatrix, fmatrix)
def row(name, dtype = 'float64'):
return Tensor(name = name, dtype = dtype, broadcastable = (True, False))
irow, frow = _int_float(row)
rows, irows, frows = _multi(row, irow, frow)
def col(name, dtype = 'float64'):
return Tensor(name = name, dtype = dtype, broadcastable = (False, True))
icol, fcol = _int_float(col)
cols, icols, fcols = _multi(col, icol, fcol)
############################
# Supporting Ops
############################
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论