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

merge

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