提交 ea32b4db authored 作者: Olivier Breuleux's avatar Olivier Breuleux

too many things to list

上级 d2cf55aa
import unittest import unittest
import gof, gof.modes, gof.opt import gof, gof.opt
import compile
from compile import * from compile import *
from scalar import *
import tensor
class Double(gof.result.Result): # class Double(gof.result.Result):
def __init__(self, data, name = "oignon"): # def __init__(self, data, name = "oignon"):
assert isinstance(data, float) # assert isinstance(data, float)
gof.result.Result.__init__(self, role = None, name = name) # gof.result.Result.__init__(self, role = None, name = name)
self.data = data # self.data = data
def __str__(self): # def __str__(self):
return self.name # return self.name
def __repr__(self): # def __repr__(self):
return self.name # return self.name
def __copy__(self): # def __copy__(self):
return self.__class__(self.data, self.name) # return self.__class__(self.data, self.name)
class MyOp(gof.op.Op): # class MyOp(gof.op.Op):
nin = -1 # nin = -1
def __init__(self, *inputs): # def __init__(self, *inputs):
assert len(inputs) == self.nin # assert len(inputs) == self.nin
for input in inputs: # for input in inputs:
if not isinstance(input, Double): # if not isinstance(input, Double):
raise Exception("Error 1") # raise Exception("Error 1")
self.inputs = inputs # self.inputs = inputs
self.outputs = [Double(0.0, self.__class__.__name__ + "_R")] # self.outputs = [Double(0.0, self.__class__.__name__ + "_R")]
def perform(self): # def perform(self):
self.outputs[0].data = self.impl(*[input.data for input in self.inputs]) # self.outputs[0].data = self.impl(*[input.data for input in self.inputs])
class Unary(MyOp): # class Unary(MyOp):
nin = 1 # nin = 1
class Binary(MyOp): # class Binary(MyOp):
nin = 2 # nin = 2
class Add(Binary): # class Add(Binary):
def impl(self, x, y): # def impl(self, x, y):
return x + y # return x + y
class Sub(Binary): # class Sub(Binary):
def impl(self, x, y): # def impl(self, x, y):
return x - y # return x - y
class Mul(Binary): # class Mul(Binary):
def impl(self, x, y): # def impl(self, x, y):
return x * y # return x * y
class Div(Binary): # class Div(Binary):
def impl(self, x, y): # def impl(self, x, y):
return x / y # return x / y
def env(inputs, outputs, validate = True, features = []): # def env(inputs, outputs, validate = True, features = []):
return gof.env.Env(inputs, outputs, features = features, consistency_check = validate) # return gof.env.Env(inputs, outputs, features = features, consistency_check = validate)
def perform_linker(env): # def perform_linker(env):
lnk = gof.link.PerformLinker(env) # lnk = gof.link.PerformLinker(env)
return lnk # return lnk
def graph1(): # (x+y) * (x/z) # def graph1(): # (x+y) * (x/z)
x = gof.modes.build(Double(1.0, 'x')) # x = gof.modes.build(Double(1.0, 'x'))
y = gof.modes.build(Double(3.0, 'y')) # y = gof.modes.build(Double(3.0, 'y'))
z = gof.modes.build(Double(4.0, 'z')) # z = gof.modes.build(Double(4.0, 'z'))
o = Mul(Add(x, y).out, Div(x, z).out).out # o = Mul(Add(x, y).out, Div(x, z).out).out
return [x,y,z], [o] # return [x,y,z], [o]
def graph1(): # (x+y) * (x/z)
x, y, z = floats('xyz')
o = mul(add(x, y), div(x, z))
return [x,y,z], [o]
class T_what:
def test_nothing(self):
pass
class T_Function(unittest.TestCase): class T_Function(unittest.TestCase):
def test_noopt(self): def test_noopt(self):
gi, go = graph1() gi, go = graph1()
p = Function(gi,go) p = function(gi, go, optimizer = None, linker = 'py')
self.failUnless(p(1.0,3.0,4.0) == 1.0) self.failUnless(p(1.0,3.0,4.0) == 1.0)
# def test_link_noopt(self):
# gi, go = graph1()
# fn, i, o = perform_linker(env(gi, go)).make_thunk(True)
# fn()
# self.failUnless(go[0].data == 1.0)
# def test_link_opt(self):
# opt = gof.opt.PatternOptimizer((Div, '1', '2'), (Div, '2', '1'))
# gi, go = graph1()
# e = env(gi, go)
# opt.optimize(e)
# fn, i, o = perform_linker(e).make_thunk(True)
# fn()
# self.failUnless(go[0].data == 16.0)
def test_link_noopt(self):
gi, go = graph1()
fn, i, o = perform_linker(env(gi, go)).make_thunk(True)
fn()
self.failUnless(go[0].data == 1.0)
def test_link_opt(self):
opt = gof.opt.PatternOptimizer((Div, '1', '2'), (Div, '2', '1'))
gi, go = graph1()
e = env(gi, go)
opt.optimize(e)
fn, i, o = perform_linker(e).make_thunk(True)
fn()
self.failUnless(go[0].data == 16.0)
def test_opt(self): def test_opt(self):
opt = gof.opt.PatternOptimizer((Div, '1', '2'), (Div, '2', '1')) opt = gof.opt.PatternOptimizer((div, '1', '2'), (div, '2', '1'))
gi, go = graph1() gi, go = graph1()
p = Function(gi,go, optimizer=opt.optimize) p = function(gi,go, optimizer=opt.optimize, linker = 'py')
self.failUnless(p(1.,3.,4.) == 16.0) self.failUnless(p(1.,3.,4.) == 16.0)
def test_multiout(self): def test_multiout(self):
def graph2(): def graph2():
x = gof.modes.build(Double(1.0, 'x')) x, y, z = floats('xyz')
y = gof.modes.build(Double(3.0, 'y')) o = mul(add(x, y), div(x, z))
z = gof.modes.build(Double(4.0, 'z'))
o = Mul(Add(x, y).out, Div(x, z).out).out
return [x,y,z], [o, o.owner.inputs[1]] return [x,y,z], [o, o.owner.inputs[1]]
opt = gof.opt.PatternOptimizer((Div, '1', '2'), (Div, '2', '1')) opt = gof.opt.PatternOptimizer((div, '1', '2'), (div, '2', '1'))
gi, go = graph2() gi, go = graph2()
p = Function(gi,go, optimizer=opt.optimize) p = function(gi,go, optimizer=opt.optimize)
a,b = p(1.,3.,4.) a,b = p(1.,3.,4.)
self.failUnless(a == 16.0) self.failUnless(a == 16.0)
self.failUnless(b == 4.0) self.failUnless(b == 4.0)
def test_orphans(self): def test_make_many_functions(self):
gi, go = graph1() x, y, z = tensor.scalars('xyz')
opt = None e0, e1, e2 = x+y+z, x*y-z, z*z+x*x+y*y
p0 = Function(gi[0:0], go) f1 = function([x, y, z], [e0])
f2 = function([x, y, z], [e0])
self.failUnless(p0() == 1.0) f3 = function([x, y, z], [e1])
f4 = function([x, y, z], [e2])
p3 = Function(gi,go) f5 = function([e0], [e0 * e0])
p2 = Function(gi[0:2], go) ff = FunctionFactory([x, y, z], [e0])
p1 = Function(gi[0:1], go) f6 = ff.create()
try: f7 = ff.create()
self.failUnless(p3() == 6.0) f8 = ff.create()
self.fail() f9 = ff.partial(1.0, 2.0)
except TypeError, e: assert f1(1.0, 2.0, 3.0) == 6.0
self.failUnless(e[0].split()[0:3] == ['Function','call', 'takes']) assert f2(1.0, 2.0, 3.0) == 6.0
assert f3(1.0, 2.0, 3.0) == -1.0
self.failUnless(p3(1.,3.,4.) == 1.0) assert f4(1.0, 2.0, 3.0) == 14.0
self.failUnless(p2(1.,3.) == 1.0) assert f5(7.0) == 49.0
self.failUnless(p1(1.,) == 1.0) assert f6(1.0, 2.0, 3.0) == 6.0
assert f7(1.0, 2.0, 3.0) == 6.0
assert f8(1.0, 2.0, 3.0) == 6.0
def test_some_constant_outputs(self): assert f9(3.0) == 6.0
x = gof.modes.build(Double(1.0, 'x'))
y = gof.modes.build(Double(3.0, 'y')) def test_no_inputs(self):
z = gof.modes.build(Double(4.0, 'z')) x, y, z = tensor.value(1.0), tensor.value(2.0), tensor.value(3.0)
xy = Mul(x,y).out e = x*x + y*y + z*z
zz = Mul(z,z).out assert function([], [e], linker = 'py')() == 14.0
assert function([], [e], linker = 'c')() == 14.0
p0 = Function([x,y], [xy, zz]) assert function([], [e], linker = 'c|py')() == 14.0
self.failUnless(p0(1.,3.) == [3.0,16.0]) assert function([], [e], linker = 'c&py')() == 14.0
self.failUnless(p0(1.5,4.) == [6.0,16.0]) assert eval_outputs([e]) == 14.0
self.failUnless(x.data == 1.0) assert fast_compute(e) == 14.0
self.failUnless(y.data == 3.0)
self.failUnless(z.data == 4.0) def test_borrow_true(self):
x, y, z = tensor.scalars('xyz')
p1 = Function([z], [xy, zz],unpack_single=False) e = x + y + z
self.failUnless(p1(4.) == [3.0,16.0]) #returns 6.0, 16.10 f = function([x, y, z], [e], borrow_outputs = True)
self.failUnless(p1(5.) == [3.0,25.0]) res1 = f(1.0, 2.0, 3.0)
assert res1 == 6.0
res2 = f(1.0, 3.0, 5.0)
assert res1 is res2
assert res1 == 9.0
assert res2 == 9.0
def test_borrow_false(self):
x, y, z = tensor.scalars('xyz')
e = x + y + z
for linker in 'py c c|py c&py'.split():
f = function([x, y, z], [e], borrow_outputs = False, linker = linker)
res1 = f(1.0, 2.0, 3.0)
self.failUnless(res1 == 6.0, (res1, linker))
res2 = f(1.0, 3.0, 5.0)
self.failUnless(res1 is not res2, (res1, res2, linker))
self.failUnless(res1 == 6.0, (res1, linker))
self.failUnless(res2 == 9.0, (res2, linker))
def test_borrow_false_through_inplace(self):
x, y, z = tensor.scalars('xyz')
# if borrow_outputs is False, we must not reuse the temporary created for x+y
e = tensor.add_inplace(x + y, z)
for linker in 'py c c|py c&py'.split():
f = function([x, y, z], [e], borrow_outputs = False, linker = linker)
res1 = f(1.0, 2.0, 3.0)
self.failUnless(res1 == 6.0, (res1, linker))
res2 = f(1.0, 3.0, 5.0)
self.failUnless(res1 is not res2, (res1, res2, linker))
self.failUnless(res1 == 6.0, (res1, linker))
self.failUnless(res2 == 9.0, (res2, linker))
class T_fast_compute(unittest.TestCase):
def test_straightforward(self):
x, y, z = tensor.value(1.0), tensor.value(2.0), tensor.value(3.0)
e = x*x + y*y + z*z
assert fast_compute(e) == 14.0
assert compile._fcache[(e, )]() == 14.0
# def test_orphans(self):
# gi, go = graph1()
# opt = None
# p0 = function(gi[0:0], go, optimizer = None, linker = 'py')
# self.failUnless(p0() == 1.0)
# p3 = Function(gi,go)
# p2 = Function(gi[0:2], go)
# p1 = Function(gi[0:1], go)
# try:
# self.failUnless(p3() == 6.0)
# self.fail()
# except TypeError, e:
# self.failUnless(e[0].split()[0:3] == ['Function','call', 'takes'])
# self.failUnless(p3(1.,3.,4.) == 1.0)
# self.failUnless(p2(1.,3.) == 1.0)
# self.failUnless(p1(1.,) == 1.0)
# def test_some_constant_outputs(self):
# x = gof.modes.build(Double(1.0, 'x'))
# y = gof.modes.build(Double(3.0, 'y'))
# z = gof.modes.build(Double(4.0, 'z'))
# xy = Mul(x,y).out
# zz = Mul(z,z).out
# p0 = Function([x,y], [xy, zz])
# self.failUnless(p0(1.,3.) == [3.0,16.0])
# self.failUnless(p0(1.5,4.) == [6.0,16.0])
# self.failUnless(x.data == 1.0)
# self.failUnless(y.data == 3.0)
# self.failUnless(z.data == 4.0)
# p1 = Function([z], [xy, zz],unpack_single=False)
# self.failUnless(p1(4.) == [3.0,16.0]) #returns 6.0, 16.10
# self.failUnless(p1(5.) == [3.0,25.0])
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -13,12 +13,13 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -13,12 +13,13 @@ class _test_grad_sources_inputs(unittest.TestCase):
def test_retNone1(self): def test_retNone1(self):
"""Test that it is not ok to return None from op.grad()""" """Test that it is not ok to return None from op.grad()"""
class retNone(gof.op.Op): class retNone(gof.op.Op):
def __init__(self, arg): def make_node(self):
self.inputs = [gof.result.Result()] inputs = [gof.generic()]
self.outputs = [gof.result.Result()] outputs = [gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
pass pass
a = retNone(5) a = retNone().make_node()
try: try:
grad_sources_inputs([(a.out, 1)], None) grad_sources_inputs([(a.out, 1)], None)
except ValueError, e: except ValueError, e:
...@@ -28,30 +29,30 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -28,30 +29,30 @@ class _test_grad_sources_inputs(unittest.TestCase):
def test_retNone1_b(self): def test_retNone1_b(self):
"""Test that it is ok to return [None] from op.grad()""" """Test that it is ok to return [None] from op.grad()"""
class retNone(gof.op.Op): class retNone(gof.op.Op):
def __init__(self, arg): def make_node(self, *inputs):
self.inputs = arg outputs = [gof.generic()]
self.outputs = [gof.result.Result()] return gof.Apply(self, inputs, outputs)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
return [None] return [None]
i = gof.result.Result() i = gof.generic()
a = retNone([i]) a = retNone().make_node(i)
g = grad_sources_inputs([(a.out, 1)], None) g = grad_sources_inputs([(a.out, 1)], None)
self.failUnless(not i in g) self.failUnless(not i in g)
def test_wrong_rval_len1(self): def test_wrong_rval_len1(self):
"""Test that it is not ok to return the wrong number of gradients""" """Test that it is not ok to return the wrong number of gradients"""
class retNone(gof.op.Op): class retNone(gof.op.Op):
def __init__(self, arg): def make_node(self, *inputs):
self.inputs = arg outputs = [gof.generic()]
self.outputs = [gof.result.Result()] return gof.Apply(self, inputs, outputs)
def grad(self, inputs, (gz, )): def grad(self, inputs, (gz, )):
return [None] return [None]
i = gof.result.Result() i = gof.generic()
j = gof.result.Result() j = gof.generic()
a1 = retNone([i]) a1 = retNone().make_node(i)
g = grad_sources_inputs([(a1.out, 1)], None) g = grad_sources_inputs([(a1.out, 1)], None)
a2 = retNone([i,j]) a2 = retNone().make_node(i,j)
try: try:
g = grad_sources_inputs([(a2.out, 1)], None) g = grad_sources_inputs([(a2.out, 1)], None)
except ValueError, e: except ValueError, e:
...@@ -63,118 +64,126 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -63,118 +64,126 @@ class _test_grad_sources_inputs(unittest.TestCase):
def test_stop_on_all_none(self): def test_stop_on_all_none(self):
"""Test that op.grad() is not called when output grads are all None""" """Test that op.grad() is not called when output grads are all None"""
class retNone(gof.op.Op): class retNone(gof.op.Op):
def __init__(self, arg, tst): def __init__(self, tst):
self.inputs = arg
self.outputs = [gof.result.Result()]
self.tst = tst self.tst = tst
def make_node(self, *inputs):
outputs = [gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, inputs, (gz, )): def grad(self, inputs, (gz, )):
self.tst.fail() self.tst.fail()
i = gof.result.Result() i = gof.generic()
a1 = retNone([i],self) a1 = retNone(self).make_node(i)
g = grad_sources_inputs([(a1.out, None)], None) g = grad_sources_inputs([(a1.out, None)], None)
def test_1in_1out(self): def test_1in_1out(self):
"""Test grad is called correctly for a 1-to-1 op""" """Test grad is called correctly for a 1-to-1 op"""
gval = gof.result.Result() gval = gof.generic()
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self): def make_node(self):
self.inputs = [gof.result.Result()] inputs = [gof.generic()]
self.outputs = [gof.result.Result()] outputs = [gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x, ), (gz, )): def grad(self, (x, ), (gz, )):
return gval, return gval,
a1 = O() a1 = O().make_node()
g = grad_sources_inputs([(a1.outputs[0], 1)], None) g = grad_sources_inputs([(a1.outputs[0], 1)], None)
self.failUnless(g[a1.inputs[0]] is gval) self.failUnless(g[a1.inputs[0]] is gval)
def test_1in_Nout(self): def test_1in_Nout(self):
"""Test grad is called correctly for a 1-to-many op""" """Test grad is called correctly for a 1-to-many op"""
gval = gof.result.Result() gval = gof.generic()
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self): def make_node(self):
self.inputs = [gof.result.Result()] inputs = [gof.generic()]
self.outputs = [gof.result.Result(),gof.result.Result()] outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x, ), (gz1, gz2)): def grad(self, (x, ), (gz1, gz2)):
return gval, return gval,
a1 = O() a1 = O().make_node()
g = grad_sources_inputs([(a1.outputs[0], 1)], None) g = grad_sources_inputs([(a1.outputs[0], 1)], None)
self.failUnless(g[a1.inputs[0]] is gval) self.failUnless(g[a1.inputs[0]] is gval)
def test_Nin_1out(self): def test_Nin_1out(self):
"""Test grad is called correctly for a many-to-1 op""" """Test grad is called correctly for a many-to-1 op"""
gval0 = gof.result.Result() gval0 = gof.generic()
gval1 = gof.result.Result() gval1 = gof.generic()
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self): def make_node(self):
self.inputs = [gof.result.Result(),gof.result.Result()] inputs = [gof.generic(),gof.generic()]
self.outputs = [gof.result.Result()] outputs = [gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x0,x1), (gz, )): def grad(self, (x0,x1), (gz, )):
return (gval0, gval1) return (gval0, gval1)
a1 = O() a1 = O().make_node()
g = grad_sources_inputs([(a1.outputs[0], 1)], None) g = grad_sources_inputs([(a1.outputs[0], 1)], None)
self.failUnless(g[a1.inputs[0]] is gval0) self.failUnless(g[a1.inputs[0]] is gval0)
self.failUnless(g[a1.inputs[1]] is gval1) self.failUnless(g[a1.inputs[1]] is gval1)
def test_Nin_Nout(self): def test_Nin_Nout(self):
"""Test grad is called correctly for a many-to-many op""" """Test grad is called correctly for a many-to-many op"""
gval0 = gof.result.Result() gval0 = gof.generic()
gval1 = gof.result.Result() gval1 = gof.generic()
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self): def make_node(self):
self.inputs = [gof.result.Result(),gof.result.Result()] inputs = [gof.generic(),gof.generic()]
self.outputs = [gof.result.Result(),gof.result.Result()] outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x0,x1), (gz0,gz1)): def grad(self, (x0,x1), (gz0,gz1)):
return gval0, gval1 return gval0, gval1
a1 = O() a1 = O().make_node()
g = grad_sources_inputs([(a1.outputs[0], 1)], None) g = grad_sources_inputs([(a1.outputs[0], 1)], None)
self.failUnless(g[a1.inputs[0]] is gval0) self.failUnless(g[a1.inputs[0]] is gval0)
self.failUnless(g[a1.inputs[1]] is gval1) self.failUnless(g[a1.inputs[1]] is gval1)
def test_some_None_ograds(self): def test_some_None_ograds(self):
"""Test grad is called when some output gradients are None""" """Test grad is called when some output gradients are None"""
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self, arg, tst): def __init__(self, tst):
self.inputs = arg
self.outputs = [gof.result.Result(),gof.result.Result()]
self.tst = tst self.tst = tst
def make_node(self, *inputs):
outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, inputs, g_out): def grad(self, inputs, g_out):
return [1] return [1]
i = gof.result.Result() i = gof.generic()
a1 = O([i],self) a1 = O(self).make_node(i)
g = grad_sources_inputs([(a1.outputs[0], 1)], None) g = grad_sources_inputs([(a1.outputs[0], 1)], None)
self.failUnless(g[i] is 1) self.failUnless(g[i] is 1)
def test_some_None_igrads(self): def test_some_None_igrads(self):
"""Test that traversal works properly when an op return some None""" """Test that traversal works properly when an op return some None"""
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self, arg, tst, grad_ok): def __init__(self, tst, grad_ok):
self.inputs = arg
self.outputs = [gof.result.Result(),gof.result.Result()]
self.tst = tst self.tst = tst
self.grad_ok = grad_ok self.grad_ok = grad_ok
def make_node(self, *inputs):
outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, inputs, g_out): def grad(self, inputs, g_out):
if not self.grad_ok: if not self.grad_ok:
self.tst.fail() self.tst.fail()
else: else:
return [1, None] return [1, None]
i = gof.result.Result() i = gof.generic()
j = gof.result.Result() j = gof.generic()
k = gof.result.Result() k = gof.generic()
a1 = O([i,j],self,True) a1 = O(self, True).make_node(i,j)
a2 = O([a1.outputs[1], k], self, True) a2 = O(self, True).make_node(a1.outputs[1], k)
g = grad_sources_inputs([(a2.outputs[0], 1)], None) g = grad_sources_inputs([(a2.outputs[0], 1)], None)
self.failUnless(g[i] is 1 and j not in g and k not in g) self.failUnless(g[i] is 1 and j not in g and k not in g)
a1 = O([i,j],self,True) a1 = O(self, True).make_node(i,j)
a2 = O([k, a1.outputs[1]], self, True) a2 = O(self, True).make_node(k, a1.outputs[1])
g = grad_sources_inputs([(a2.outputs[0], 1)], None) g = grad_sources_inputs([(a2.outputs[0], 1)], None)
self.failUnless(g[k] is 1 and i not in g and j not in g) self.failUnless(g[k] is 1 and i not in g and j not in g)
def test_inputs(self): def test_inputs(self):
"""Test that passing inputs shortens the traversal""" """Test that passing inputs shortens the traversal"""
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self, arg, tst, grad_ok): def __init__(self, tst, grad_ok):
self.inputs = arg
self.outputs = [gof.result.Result(),gof.result.Result()]
self.tst = tst self.tst = tst
self.grad_ok = grad_ok self.grad_ok = grad_ok
def make_node(self, *inputs):
outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, inputs, (g0,g1)): def grad(self, inputs, (g0,g1)):
if not self.grad_ok: if not self.grad_ok:
self.tst.fail() self.tst.fail()
...@@ -183,11 +192,11 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -183,11 +192,11 @@ class _test_grad_sources_inputs(unittest.TestCase):
return [g0, g0+g1] return [g0, g0+g1]
else: else:
return [g0, g0] return [g0, g0]
i = gof.result.Result() i = gof.generic()
j = gof.result.Result() j = gof.generic()
k = gof.result.Result() k = gof.generic()
a1 = O([i,j],self,True) a1 = O(self, True).make_node(i,j)
a2 = O([k,a1.outputs[1]], self, True) a2 = O(self, True).make_node(k,a1.outputs[1])
g = grad_sources_inputs([(a2.outputs[0], 1), (a1.outputs[1],4), g = grad_sources_inputs([(a2.outputs[0], 1), (a1.outputs[1],4),
(a1.outputs[0], 3), (a1.outputs[0], 3)], a1.outputs) (a1.outputs[0], 3), (a1.outputs[0], 3)], a1.outputs)
self.failUnless(g[a2.inputs[0]] == 1) self.failUnless(g[a2.inputs[0]] == 1)
...@@ -200,11 +209,12 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -200,11 +209,12 @@ class _test_grad_sources_inputs(unittest.TestCase):
def test_multiple_sources(self): def test_multiple_sources(self):
"""Test that passing multiple sources works""" """Test that passing multiple sources works"""
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self, arg, tst, grad_ok): def __init__(self, tst, grad_ok):
self.inputs = arg
self.outputs = [gof.result.Result(),gof.result.Result()]
self.tst = tst self.tst = tst
self.grad_ok = grad_ok self.grad_ok = grad_ok
def make_node(self, *inputs):
outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, inputs, (g0,g1)): def grad(self, inputs, (g0,g1)):
if not self.grad_ok: if not self.grad_ok:
self.tst.fail() self.tst.fail()
...@@ -213,11 +223,11 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -213,11 +223,11 @@ class _test_grad_sources_inputs(unittest.TestCase):
return [g0, g0+g1] return [g0, g0+g1]
else: else:
return [g0, g0] return [g0, g0]
i = gof.result.Result() i = gof.generic()
j = gof.result.Result() j = gof.generic()
k = gof.result.Result() k = gof.generic()
a1 = O([i,j],self,True) a1 = O(self,True).make_node(i,j)
a2 = O([k,a1.outputs[1]], self, True) a2 = O(self,True).make_node(k,a1.outputs[1])
g = grad_sources_inputs([(a2.outputs[0], 1), (a1.outputs[1],4), g = grad_sources_inputs([(a2.outputs[0], 1), (a1.outputs[1],4),
(a1.outputs[0], 3), (a1.outputs[0], 3)], None) (a1.outputs[0], 3), (a1.outputs[0], 3)], None)
self.failUnless(g[a2.inputs[0]] == 1) self.failUnless(g[a2.inputs[0]] == 1)
...@@ -231,47 +241,47 @@ class _test_grad_sources_inputs(unittest.TestCase): ...@@ -231,47 +241,47 @@ class _test_grad_sources_inputs(unittest.TestCase):
class _test_grad(unittest.TestCase): class _test_grad(unittest.TestCase):
class O(gof.op.Op): class O(gof.op.Op):
def __init__(self): def __init__(self):
self.inputs = [gof.result.Result(),gof.result.Result()] self.gval0 = gof.generic()
self.outputs = [gof.result.Result(),gof.result.Result()] self.gval1 = gof.generic()
self.gval0 = gof.result.Result() def make_node(self):
self.gval1 = gof.result.Result() inputs = [gof.generic(),gof.generic()]
outputs = [gof.generic(),gof.generic()]
return gof.Apply(self, inputs, outputs)
def grad(self, (x0,x1), (gz0,gz1)): def grad(self, (x0,x1), (gz0,gz1)):
return self.gval0, self.gval1 return self.gval0, self.gval1
def test_1param(self): def test_1param(self):
"""grad: Test passing a single result param""" """grad: Test passing a single result param"""
a1 = _test_grad.O() o = _test_grad.O()
self.failUnless(a1.gval0 is grad(a1.outputs[0], a1.inputs[0])) a1 = o.make_node()
self.failUnless(o.gval0 is grad(a1.outputs[0], a1.inputs[0]))
def test_Nparam(self): def test_Nparam(self):
"""grad: Test passing multiple result params""" """grad: Test passing multiple result params"""
a1 = _test_grad.O() o = _test_grad.O()
a1 = o.make_node()
g0,g1 = grad(a1.outputs[0], a1.inputs) g0,g1 = grad(a1.outputs[0], a1.inputs)
self.failUnless(a1.gval0 is g0) self.failUnless(o.gval0 is g0)
self.failUnless(a1.gval1 is g1) self.failUnless(o.gval1 is g1)
def test_1None_rval(self): def test_1None_rval(self):
"""grad: Test returning a single None from grad""" """grad: Test returning a single None from grad"""
a1 = _test_grad.O() o = _test_grad.O()
a1 = o.make_node()
self.failUnless(None is grad(a1.outputs[0], a1.outputs[1])) self.failUnless(None is grad(a1.outputs[0], a1.outputs[1]))
self.failUnless(None is grad(a1.outputs[0], 'wtf')) self.failUnless(None is grad(a1.outputs[0], 'wtf'))
def test_NNone_rval(self): def test_NNone_rval(self):
"""grad: Test returning some Nones from grad""" """grad: Test returning some Nones from grad"""
a1 = _test_grad.O() o = _test_grad.O()
a1 = o.make_node()
g0,g1,g2 = grad(a1.outputs[0], a1.inputs + ['wtf']) g0,g1,g2 = grad(a1.outputs[0], a1.inputs + ['wtf'])
self.failUnless(a1.gval0 is g0) self.failUnless(o.gval0 is g0)
self.failUnless(a1.gval1 is g1) self.failUnless(o.gval1 is g1)
self.failUnless(None is g2) self.failUnless(None is g2)
def matrix():
return tensor.Tensor('float64', [0,0])
def matrices(n):
return [matrix() for i in xrange(n)]
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -12,25 +12,25 @@ class T_transpose(unittest.TestCase): ...@@ -12,25 +12,25 @@ class T_transpose(unittest.TestCase):
numpy.random.seed(44) numpy.random.seed(44)
def test_transpose_csc(self): def test_transpose_csc(self):
sp = sparse.csc_matrix(sparse.speye(5,3)) sp = sparse.csc_matrix(sparse.speye(5,3))
a = assparse(sp) a = as_sparse(sp)
self.failUnless(a.data is sp) self.failUnless(a.data is sp)
self.failUnless(a.data.shape == (5,3)) self.failUnless(a.data.shape == (5,3))
self.failUnless(a.dtype == 'float64') self.failUnless(a.type.dtype == 'float64', a.type.dtype)
self.failUnless(a.format == 'csc', a.format) self.failUnless(a.type.format == 'csc', a.type.format)
ta = transpose(a) ta = transpose(a)
self.failUnless(ta.dtype == 'float64', ta.dtype) self.failUnless(ta.type.dtype == 'float64', ta.type.dtype)
self.failUnless(ta.format == 'csr', ta.format) self.failUnless(ta.type.format == 'csr', ta.type.format)
vta = compile.eval_outputs([ta]) vta = compile.eval_outputs([ta])
self.failUnless(vta.shape == (3,5)) self.failUnless(vta.shape == (3,5))
def test_transpose_csr(self): def test_transpose_csr(self):
a = assparse(sparse.csr_matrix(sparse.speye(5,3))) a = as_sparse(sparse.csr_matrix(sparse.speye(5,3)))
self.failUnless(a.data.shape == (5,3)) self.failUnless(a.data.shape == (5,3))
self.failUnless(a.dtype == 'float64') self.failUnless(a.type.dtype == 'float64')
self.failUnless(a.format == 'csr') self.failUnless(a.type.format == 'csr')
ta = transpose(a) ta = transpose(a)
self.failUnless(ta.dtype == 'float64', ta.dtype) self.failUnless(ta.type.dtype == 'float64', ta.type.dtype)
self.failUnless(ta.format == 'csc', ta.format) self.failUnless(ta.type.format == 'csc', ta.type.format)
vta = compile.eval_outputs([ta]) vta = compile.eval_outputs([ta])
self.failUnless(vta.shape == (3,5)) self.failUnless(vta.shape == (3,5))
...@@ -39,13 +39,13 @@ class T_Add(unittest.TestCase): ...@@ -39,13 +39,13 @@ class T_Add(unittest.TestCase):
def testSS(self): def testSS(self):
for mtype in _mtypes: for mtype in _mtypes:
a = mtype(numpy.array([[1., 0], [3, 0], [0, 6]])) a = mtype(numpy.array([[1., 0], [3, 0], [0, 6]]))
aR = assparse(a) aR = as_sparse(a)
self.failUnless(aR.data is a) self.failUnless(aR.data is a)
self.failUnless(_is_sparse(a)) self.failUnless(_is_sparse(a))
self.failUnless(_is_sparse_result(aR)) self.failUnless(_is_sparse_result(aR))
b = mtype(numpy.asarray([[0, 2.], [0, 4], [5, 0]])) b = mtype(numpy.asarray([[0, 2.], [0, 4], [5, 0]]))
bR = assparse(b) bR = as_sparse(b)
self.failUnless(bR.data is b) self.failUnless(bR.data is b)
self.failUnless(_is_sparse(b)) self.failUnless(_is_sparse(b))
self.failUnless(_is_sparse_result(bR)) self.failUnless(_is_sparse_result(bR))
...@@ -53,10 +53,10 @@ class T_Add(unittest.TestCase): ...@@ -53,10 +53,10 @@ class T_Add(unittest.TestCase):
apb = add(aR, bR) apb = add(aR, bR)
self.failUnless(_is_sparse_result(apb)) self.failUnless(_is_sparse_result(apb))
self.failUnless(apb.dtype == aR.dtype, apb.dtype) self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype)
self.failUnless(apb.dtype == bR.dtype, apb.dtype) self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype)
self.failUnless(apb.format == aR.format, apb.format) self.failUnless(apb.type.format == aR.type.format, apb.type.format)
self.failUnless(apb.format == bR.format, apb.format) self.failUnless(apb.type.format == bR.type.format, apb.type.format)
val = compile.eval_outputs([apb]) val = compile.eval_outputs([apb])
self.failUnless(val.shape == (3,2)) self.failUnless(val.shape == (3,2))
...@@ -66,13 +66,13 @@ class T_Add(unittest.TestCase): ...@@ -66,13 +66,13 @@ class T_Add(unittest.TestCase):
def testSD(self): def testSD(self):
for mtype in _mtypes: for mtype in _mtypes:
a = numpy.array([[1., 0], [3, 0], [0, 6]]) a = numpy.array([[1., 0], [3, 0], [0, 6]])
aR = tensor.astensor(a) aR = tensor.as_tensor(a)
self.failUnless(aR.data is a) self.failUnless(aR.data is a)
self.failUnless(_is_dense(a)) self.failUnless(_is_dense(a))
self.failUnless(_is_dense_result(aR)) self.failUnless(_is_dense_result(aR))
b = mtype(numpy.asarray([[0, 2.], [0, 4], [5, 0]])) b = mtype(numpy.asarray([[0, 2.], [0, 4], [5, 0]]))
bR = assparse(b) bR = as_sparse(b)
self.failUnless(bR.data is b) self.failUnless(bR.data is b)
self.failUnless(_is_sparse(b)) self.failUnless(_is_sparse(b))
self.failUnless(_is_sparse_result(bR)) self.failUnless(_is_sparse_result(bR))
...@@ -80,8 +80,8 @@ class T_Add(unittest.TestCase): ...@@ -80,8 +80,8 @@ class T_Add(unittest.TestCase):
apb = add(aR, bR) apb = add(aR, bR)
self.failUnless(_is_dense_result(apb)) self.failUnless(_is_dense_result(apb))
self.failUnless(apb.dtype == aR.dtype, apb.dtype) self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype)
self.failUnless(apb.dtype == bR.dtype, apb.dtype) self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype)
val = compile.eval_outputs([apb]) val = compile.eval_outputs([apb])
self.failUnless(val.shape == (3, 2)) self.failUnless(val.shape == (3, 2))
...@@ -91,13 +91,13 @@ class T_Add(unittest.TestCase): ...@@ -91,13 +91,13 @@ class T_Add(unittest.TestCase):
def testDS(self): def testDS(self):
for mtype in _mtypes: for mtype in _mtypes:
a = mtype(numpy.array([[1., 0], [3, 0], [0, 6]])) a = mtype(numpy.array([[1., 0], [3, 0], [0, 6]]))
aR = assparse(a) aR = as_sparse(a)
self.failUnless(aR.data is a) self.failUnless(aR.data is a)
self.failUnless(_is_sparse(a)) self.failUnless(_is_sparse(a))
self.failUnless(_is_sparse_result(aR)) self.failUnless(_is_sparse_result(aR))
b = numpy.asarray([[0, 2.], [0, 4], [5, 0]]) b = numpy.asarray([[0, 2.], [0, 4], [5, 0]])
bR = tensor.astensor(b) bR = tensor.as_tensor(b)
self.failUnless(bR.data is b) self.failUnless(bR.data is b)
self.failUnless(_is_dense(b)) self.failUnless(_is_dense(b))
self.failUnless(_is_dense_result(bR)) self.failUnless(_is_dense_result(bR))
...@@ -105,8 +105,8 @@ class T_Add(unittest.TestCase): ...@@ -105,8 +105,8 @@ class T_Add(unittest.TestCase):
apb = add(aR, bR) apb = add(aR, bR)
self.failUnless(_is_dense_result(apb)) self.failUnless(_is_dense_result(apb))
self.failUnless(apb.dtype == aR.dtype, apb.dtype) self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype)
self.failUnless(apb.dtype == bR.dtype, apb.dtype) self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype)
val = compile.eval_outputs([apb]) val = compile.eval_outputs([apb])
self.failUnless(val.shape == (3, 2)) self.failUnless(val.shape == (3, 2))
...@@ -118,15 +118,15 @@ class T_conversion(unittest.TestCase): ...@@ -118,15 +118,15 @@ class T_conversion(unittest.TestCase):
numpy.random.seed(44) numpy.random.seed(44)
def test0(self): def test0(self):
a = tensor.astensor(numpy.random.rand(5)) a = tensor.as_tensor(numpy.random.rand(5))
s = sparse_from_dense(a, 'csc') s = csc_from_dense(a)
val = compile.eval_outputs([s]) val = compile.eval_outputs([s])
self.failUnless(str(val.dtype)=='float64') self.failUnless(str(val.dtype)=='float64')
self.failUnless(val.format == 'csc') self.failUnless(val.format == 'csc')
def test1(self): def test1(self):
a = tensor.astensor(numpy.random.rand(5)) a = tensor.as_tensor(numpy.random.rand(5))
s = sparse_from_dense(a,'csr') s = csr_from_dense(a)
val = compile.eval_outputs([s]) val = compile.eval_outputs([s])
self.failUnless(str(val.dtype)=='float64') self.failUnless(str(val.dtype)=='float64')
self.failUnless(val.format == 'csr') self.failUnless(val.format == 'csr')
...@@ -147,7 +147,7 @@ class _testCase_dot(unittest.TestCase): ...@@ -147,7 +147,7 @@ class _testCase_dot(unittest.TestCase):
def test_basicSS(self): def test_basicSS(self):
for mtype in _mtypes: for mtype in _mtypes:
x = assparse(mtype((500,3))) x = as_sparse(mtype((500,3)))
x.data[(10, 1)] = 1 x.data[(10, 1)] = 1
x.data[(20, 2)] = 2 x.data[(20, 2)] = 2
self.failUnless(_is_sparse_result(x)) self.failUnless(_is_sparse_result(x))
...@@ -176,126 +176,126 @@ class _testCase_dot(unittest.TestCase): ...@@ -176,126 +176,126 @@ class _testCase_dot(unittest.TestCase):
w = w.todense() w = w.todense()
self.failUnless((z == w).all() == True) self.failUnless((z == w).all() == True)
def test_basicSD(self): # def test_basicSD(self):
for mtype in _mtypes: # for mtype in _mtypes:
x = assparse(mtype((500,3))) # x = as_sparse(mtype((500,3)))
x.data[(10, 1)] = 1 # x.data[(10, 1)] = 1
x.data[(20, 2)] = 2 # x.data[(20, 2)] = 2
self.failUnless(_is_sparse_result(x)) # self.failUnless(_is_sparse_result(x))
y = tensor.astensor([[1., 2], [3, 4], [2, 1]]) # y = tensor.as_tensor([[1., 2], [3, 4], [2, 1]])
self.failUnless(_is_dense_result(y)) # self.failUnless(_is_dense_result(y))
zop = dot(x,y) # zop = dot(x,y)
self.failUnless(_is_sparse_result(zop)) # self.failUnless(_is_sparse_result(zop))
z = compile.eval_outputs([zop]) # z = compile.eval_outputs([zop])
self.failUnless(_is_sparse(z)) # self.failUnless(_is_sparse(z))
self.failUnless(z.shape == (500,2)) # self.failUnless(z.shape == (500,2))
self.failUnless(type(z) is mtype)
w = mtype((500,2))
w[(10, 0)] = 3.
w[(20, 0)] = 4
w[(10, 1)] = 4
w[(20, 1)] = 2
self.failUnless(z.shape == w.shape)
self.failUnless(type(z) == type(w))
self.failUnless(z.dtype == w.dtype)
#self.failUnless(z == w)
self.failUnless(abs(z-w).nnz == 0)
z = z.todense()
w = w.todense()
self.failUnless((z == w).all() == True)
def test_basicDS(self):
for mtype in _mtypes:
x = assparse(mtype((500,3)))
x.data[(10, 1)] = 1
x.data[(20, 2)] = 2
self.failUnless(_is_sparse_result(x))
y = tensor.astensor([[1., 2], [3, 4], [2, 1]])
self.failUnless(_is_dense_result(y))
x.data = x.data.T
y.data = y.data.T
# zop = dot(y, x)
zop = transpose(dot(y, x))
self.failUnless(_is_sparse_result(zop))
z = compile.eval_outputs([zop])
self.failUnless(_is_sparse(z))
self.failUnless(z.shape == (500,2))
# self.failUnless(type(z) is mtype) # self.failUnless(type(z) is mtype)
w = mtype((500,2)) # w = mtype((500,2))
w[(10, 0)] = 3. # w[(10, 0)] = 3.
w[(20, 0)] = 4 # w[(20, 0)] = 4
w[(10, 1)] = 4 # w[(10, 1)] = 4
w[(20, 1)] = 2 # w[(20, 1)] = 2
self.failUnless(z.shape == w.shape) # self.failUnless(z.shape == w.shape)
# Type should switch from csr to csc and vice-versa, so don't perform this test # self.failUnless(type(z) == type(w))
#self.failUnless(type(z) == type(w)) # self.failUnless(z.dtype == w.dtype)
self.failUnless(z.dtype == w.dtype)
# #self.failUnless(z == w)
# Type should switch from csr to csc and vice-versa, so don't perform this test # self.failUnless(abs(z-w).nnz == 0)
#self.failUnless(z == w)
self.failUnless(abs(z-w).nnz == 0) # z = z.todense()
# w = w.todense()
z = z.todense() # self.failUnless((z == w).all() == True)
w = w.todense()
self.failUnless((z == w).all() == True) # def test_basicDS(self):
# for mtype in _mtypes:
def test_graph_bprop0(self): # x = as_sparse(mtype((500,3)))
for mtype in _mtypes: # x.data[(10, 1)] = 1
x = tensor.Tensor('float64', broadcastable=[False,False], name='x') # x.data[(20, 2)] = 2
w = SparseResult('float64', _mtype_to_str[mtype]) # self.failUnless(_is_sparse_result(x))
xw = dense_from_sparse(dot(w, x))
y = dense_from_sparse(dot(w.T, xw)) # y = tensor.as_tensor([[1., 2], [3, 4], [2, 1]])
diff = x-y # self.failUnless(_is_dense_result(y))
loss = tensor.sum(tensor.sqr(diff))
gw = gradient.grad(loss, w) # x.data = x.data.T
trainfn = compile.Function([x, w], [y, loss, gw]) # y.data = y.data.T
x = numpy.asarray([[1., 2], [3, 4], [2, 1]]) # # zop = dot(y, x)
w = mtype((500,3)) # zop = transpose(dot(y, x))
w[(10, 1)] = 1 # self.failUnless(_is_sparse_result(zop))
w[(20, 2)] = 2 # z = compile.eval_outputs([zop])
lr = 0.001 # self.failUnless(_is_sparse(z))
y, origloss, gw = trainfn(x, w) # self.failUnless(z.shape == (500,2))
for epoch in xrange(50): # # self.failUnless(type(z) is mtype)
y, loss, gw = trainfn(x, w)
w = w - (lr * gw) # w = mtype((500,2))
# w[(10, 0)] = 3.
self.failUnless(origloss > loss) # w[(20, 0)] = 4
self.failUnless('1.0543172285' == str(loss)) # w[(10, 1)] = 4
# w[(20, 1)] = 2
def test_graph_bprop_rand(self): # self.failUnless(z.shape == w.shape)
for i in range(10): # # Type should switch from csr to csc and vice-versa, so don't perform this test
xorig = numpy.random.rand(3,2) # #self.failUnless(type(z) == type(w))
for mtype in _mtypes: # self.failUnless(z.dtype == w.dtype)
x = tensor.Tensor('float64', broadcastable=[False,False], name='x')
w = SparseResult('float64', _mtype_to_str[mtype]) # # Type should switch from csr to csc and vice-versa, so don't perform this test
xw = dense_from_sparse(dot(w, x)) # #self.failUnless(z == w)
y = dense_from_sparse(dot(w.T, xw)) # self.failUnless(abs(z-w).nnz == 0)
diff = x-y
loss = tensor.sum(tensor.sqr(diff)) # z = z.todense()
gw = gradient.grad(loss, w) # w = w.todense()
trainfn = compile.Function([x, w], [y, loss, gw]) # self.failUnless((z == w).all() == True)
x = xorig # def test_graph_bprop0(self):
w = mtype((500,3)) # for mtype in _mtypes:
w[(10, 1)] = 1 # x = tensor.Tensor('float64', broadcastable=[False,False], name='x')
w[(20, 2)] = 2 # w = SparseResult('float64', _mtype_to_str[mtype])
lr = 0.001 # xw = dense_from_sparse(dot(w, x))
y, origloss, gw = trainfn(x, w) # y = dense_from_sparse(dot(w.T, xw))
for epoch in xrange(50): # diff = x-y
y, loss, gw = trainfn(x, w) # loss = tensor.sum(tensor.sqr(diff))
w = w - (lr * gw) # gw = gradient.grad(loss, w)
# trainfn = compile.Function([x, w], [y, loss, gw])
self.failUnless(origloss > loss)
# x = numpy.asarray([[1., 2], [3, 4], [2, 1]])
# w = mtype((500,3))
# w[(10, 1)] = 1
# w[(20, 2)] = 2
# lr = 0.001
# y, origloss, gw = trainfn(x, w)
# for epoch in xrange(50):
# y, loss, gw = trainfn(x, w)
# w = w - (lr * gw)
# self.failUnless(origloss > loss)
# self.failUnless('1.0543172285' == str(loss))
# def test_graph_bprop_rand(self):
# for i in range(10):
# xorig = numpy.random.rand(3,2)
# for mtype in _mtypes:
# x = tensor.Tensor('float64', broadcastable=[False,False], name='x')
# w = SparseResult('float64', _mtype_to_str[mtype])
# xw = dense_from_sparse(dot(w, x))
# y = dense_from_sparse(dot(w.T, xw))
# diff = x-y
# loss = tensor.sum(tensor.sqr(diff))
# gw = gradient.grad(loss, w)
# trainfn = compile.Function([x, w], [y, loss, gw])
# x = xorig
# w = mtype((500,3))
# w[(10, 1)] = 1
# w[(20, 2)] = 2
# lr = 0.001
# y, origloss, gw = trainfn(x, w)
# for epoch in xrange(50):
# y, loss, gw = trainfn(x, w)
# w = w - (lr * gw)
# self.failUnless(origloss > loss)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -3,7 +3,7 @@ import tensor # for hidden symbols ...@@ -3,7 +3,7 @@ import tensor # for hidden symbols
import unittest import unittest
from copy import copy from copy import copy
from compile import Function, eval_outputs from compile import function, FunctionFactory, eval_outputs
import gradient import gradient
import gof, gof.graph import gof, gof.graph
from gof.python25 import any from gof.python25 import any
...@@ -41,38 +41,38 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -41,38 +41,38 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
def test_good(self): def test_good(self):
for testname, inputs in self.good.items(): for testname, inputs in self.good.items():
inputs = [copy(input) for input in inputs] inputs = [copy(input) for input in inputs]
inputrs = [constant(input).type() for input in inputs] inputrs = [value(input) for input in inputs]
try: try:
node = self.op.make_node(*inputrs) node = self.op.make_node(*inputrs)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while making a node with inputs %s" \ err_msg = "Test %s::%s: Error occurred while making a node with inputs %s" \
% (self.op, testname, inputs) % (self.op, testname, inputs)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
try: try:
f = Function(inputrs, node.outputs, f = function(inputrs, node.outputs,
linker_cls = lambda env: gof.DualLinker(env, checker = _numpy_checker), linker = lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs),
unpack_single = False, unpack_single = False,
optimizer = None) optimizer = None)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while trying to make a Function" \ err_msg = "Test %s::%s: Error occurred while trying to make a Function" \
% (self.op, testname) % (self.op, testname)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
expecteds = self.expected(*inputs) expecteds = self.expected(*inputs)
try: try:
results = f(*inputs) results = f(*inputs)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while calling the Function on the inputs %s" \ err_msg = "Test %s::%s: Error occurred while calling the Function on the inputs %s" \
% (self.op, testname, inputs) % (self.op, testname, inputs)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
if not isinstance(expecteds, (list, tuple)): if not isinstance(expecteds, (list, tuple)):
expecteds = (expecteds, ) expecteds = (expecteds, )
...@@ -89,7 +89,7 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -89,7 +89,7 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
def test_bad_build(self): def test_bad_build(self):
for testname, inputs in self.bad_build.items(): for testname, inputs in self.bad_build.items():
inputs = [copy(input) for input in inputs] inputs = [copy(input) for input in inputs]
inputrs = [constant(input).type() for input in inputs] inputrs = [value(input) for input in inputs]
try: try:
node = self.op.make_node(*inputrs) node = self.op.make_node(*inputrs)
except: except:
...@@ -100,27 +100,27 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -100,27 +100,27 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
def test_bad_runtime(self): def test_bad_runtime(self):
for testname, inputs in self.bad_runtime.items(): for testname, inputs in self.bad_runtime.items():
inputs = [copy(input) for input in inputs] inputs = [copy(input) for input in inputs]
inputrs = [constant(input).type() for input in inputs] inputrs = [value(input) for input in inputs]
try: try:
node = self.op.make_node(*inputrs) node = self.op.make_node(*inputrs)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while trying to make a node with inputs %s" \ err_msg = "Test %s::%s: Error occurred while trying to make a node with inputs %s" \
% (self.op, testname, inputs) % (self.op, testname, inputs)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
try: try:
f = Function(inputrs, node.outputs, f = function(inputrs, node.outputs,
linker_cls = lambda env: gof.DualLinker(env, checker = _numpy_checker), linker = lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs),
unpack_single = False, unpack_single = False,
optimizer = None) optimizer = None)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while trying to make a Function" \ err_msg = "Test %s::%s: Error occurred while trying to make a Function" \
% (self.op, testname) % (self.op, testname)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
try: try:
results = f(*inputs) results = f(*inputs)
...@@ -133,15 +133,15 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -133,15 +133,15 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
def test_grad(self): def test_grad(self):
for testname, inputs in self.grad.items(): for testname, inputs in self.grad.items():
inputs = [copy(input) for input in inputs] inputs = [copy(input) for input in inputs]
inputrs = [constant(input).type() for input in inputs] inputrs = [value(input) for input in inputs]
try: try:
verify_grad(self, self.op, inputs) verify_grad(self, self.op, inputs)
except: except:
type, value, traceback = sys.exc_info() type, exc_value, traceback = sys.exc_info()
err_msg = "Test %s::%s: Error occurred while computing the gradient on the following inputs: %s" \ err_msg = "Test %s::%s: Error occurred while computing the gradient on the following inputs: %s" \
% (self.op, testname, inputs) % (self.op, testname, inputs)
value.args = value.args + (err_msg, ) exc_value.args = exc_value.args + (err_msg, )
raise type, value, traceback raise type, exc_value, traceback
Checker.__name__ = name Checker.__name__ = name
return Checker return Checker
...@@ -194,287 +194,287 @@ _grad_broadcast_binary_normal = dict(same_shapes = (rand(2, 3), rand(2, 3)), ...@@ -194,287 +194,287 @@ _grad_broadcast_binary_normal = dict(same_shapes = (rand(2, 3), rand(2, 3)),
column = (rand(2, 3), rand(2, 1))) column = (rand(2, 3), rand(2, 1)))
# AddTester = make_broadcast_tester(op = add, AddTester = make_broadcast_tester(op = add,
# expected = lambda *inputs: reduce(lambda x, y: x + y, inputs), expected = lambda *inputs: reduce(lambda x, y: x + y, inputs),
# good = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)), good = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)),
# four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)), four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)),
# **_good_broadcast_binary_normal), **_good_broadcast_binary_normal),
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal) bad_runtime = _bad_runtime_broadcast_binary_normal)
# AddInplaceTester = make_broadcast_tester(op = add_inplace, AddInplaceTester = make_broadcast_tester(op = add_inplace,
# expected = lambda x, y: x + y, expected = lambda x, y: x + y,
# good = _good_broadcast_binary_normal, good = _good_broadcast_binary_normal,
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal, bad_runtime = _bad_runtime_broadcast_binary_normal,
# inplace = True) inplace = True)
# SubTester = make_broadcast_tester(op = sub, SubTester = make_broadcast_tester(op = sub,
# expected = lambda x, y: x - y, expected = lambda x, y: x - y,
# good = _good_broadcast_binary_normal, good = _good_broadcast_binary_normal,
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal, bad_runtime = _bad_runtime_broadcast_binary_normal,
# grad = _grad_broadcast_binary_normal) grad = _grad_broadcast_binary_normal)
# SubInplaceTester = make_broadcast_tester(op = sub_inplace, SubInplaceTester = make_broadcast_tester(op = sub_inplace,
# expected = lambda x, y: x - y, expected = lambda x, y: x - y,
# good = _good_broadcast_binary_normal, good = _good_broadcast_binary_normal,
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal, bad_runtime = _bad_runtime_broadcast_binary_normal,
# grad = _grad_broadcast_binary_normal, grad = _grad_broadcast_binary_normal,
# inplace = True) inplace = True)
# MulTester = make_broadcast_tester(op = mul, MulTester = make_broadcast_tester(op = mul,
# expected = lambda *inputs: reduce(lambda x, y: x * y, inputs), expected = lambda *inputs: reduce(lambda x, y: x * y, inputs),
# good = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)), good = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)),
# four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)), four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)),
# **_good_broadcast_binary_normal), **_good_broadcast_binary_normal),
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal, bad_runtime = _bad_runtime_broadcast_binary_normal,
# grad = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)), grad = dict(three_inputs_same_shapes = (rand(2, 3), rand(2, 3), rand(2, 3)),
# four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)), four_inputs_broadcast = (rand(2, 3), rand(1, 3), rand(2, 1), rand(1, 1)),
# **_grad_broadcast_binary_normal)) **_grad_broadcast_binary_normal))
# MulInplaceTester = make_broadcast_tester(op = mul_inplace, MulInplaceTester = make_broadcast_tester(op = mul_inplace,
# expected = lambda x, y: x * y, expected = lambda x, y: x * y,
# good = _good_broadcast_binary_normal, good = _good_broadcast_binary_normal,
# bad_build = _bad_build_broadcast_binary_normal, bad_build = _bad_build_broadcast_binary_normal,
# bad_runtime = _bad_runtime_broadcast_binary_normal, bad_runtime = _bad_runtime_broadcast_binary_normal,
# grad = _grad_broadcast_binary_normal, grad = _grad_broadcast_binary_normal,
# inplace = True) inplace = True)
# DivTester = make_broadcast_tester(op = div, DivTester = make_broadcast_tester(op = div,
# expected = lambda x, y: x / y, expected = lambda x, y: x / y,
# good = dict(same_shapes = (rand(2, 3), rand(2, 3)), good = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# scalar = (rand(2, 3), rand(1, 1)), scalar = (rand(2, 3), rand(1, 1)),
# row = (rand(2, 3), rand(1, 3)), row = (rand(2, 3), rand(1, 3)),
# column = (rand(2, 3), rand(2, 1)), column = (rand(2, 3), rand(2, 1)),
# dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)), dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)),
# dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3)), dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3)),
# # integers_positive = (randint_ranged(4, 10, (2, 3)), randint_ranged(1, 6, (2, 3))), # integers_positive = (randint_ranged(4, 10, (2, 3)), randint_ranged(1, 6, (2, 3))),
# # integers_known_to_fail = (numpy.array(-1), numpy.array(5)) # integers_known_to_fail = (numpy.array(-1), numpy.array(5))
# ), ),
# # integers = (randint(2, 3), randint_nonzero(2, 3)), # integers = (randint(2, 3), randint_nonzero(2, 3)),
# # dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)),
# # dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3))),
# grad = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# scalar = (rand(2, 3), rand(1, 1)),
# row = (rand(2, 3), rand(1, 3)),
# column = (rand(2, 3), rand(2, 1))))
# DivInplaceTester = make_broadcast_tester(op = div_inplace,
# expected = lambda x, y: x / y,
# good = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# scalar = (rand(2, 3), rand(1, 1)),
# row = (rand(2, 3), rand(1, 3)),
# column = (rand(2, 3), rand(2, 1)),
# dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)), # dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)),
# dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3)) # dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3))),
# ), grad = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# grad = dict(same_shapes = (rand(2, 3), rand(2, 3)), scalar = (rand(2, 3), rand(1, 1)),
# scalar = (rand(2, 3), rand(1, 1)), row = (rand(2, 3), rand(1, 3)),
# row = (rand(2, 3), rand(1, 3)), column = (rand(2, 3), rand(2, 1))))
# column = (rand(2, 3), rand(2, 1))), DivInplaceTester = make_broadcast_tester(op = div_inplace,
# inplace = True) expected = lambda x, y: x / y,
good = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# PowTester = make_broadcast_tester(op = pow, scalar = (rand(2, 3), rand(1, 1)),
# expected = lambda x, y: x ** y, row = (rand(2, 3), rand(1, 3)),
# good = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))), column = (rand(2, 3), rand(2, 1)),
# scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))), dtype_mixup_1 = (rand(2, 3), randint_nonzero(2, 3)),
# row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))), dtype_mixup_2 = (randint_nonzero(2, 3), rand(2, 3))
# column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1))), ),
# dtype_mixup = (rand_ranged(-3, 3, (2, 3)), randint_ranged(-3, 3, (2, 3)))), grad = dict(same_shapes = (rand(2, 3), rand(2, 3)),
# grad = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))), scalar = (rand(2, 3), rand(1, 1)),
# scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))), row = (rand(2, 3), rand(1, 3)),
# row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))), column = (rand(2, 3), rand(2, 1))),
# column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1)))) inplace = True)
# )
# PowInplaceTester = make_broadcast_tester(op = pow_inplace, PowTester = make_broadcast_tester(op = pow,
# expected = lambda x, y: x ** y, expected = lambda x, y: x ** y,
# good = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))), good = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))),
# scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))), scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))),
# row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))), row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))),
# column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1))), column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1))),
# dtype_mixup = (rand_ranged(-3, 3, (2, 3)), randint_ranged(-3, 3, (2, 3)))), dtype_mixup = (rand_ranged(-3, 3, (2, 3)), randint_ranged(-3, 3, (2, 3)))),
# grad = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))), grad = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))),
# scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))), scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))),
# row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))), row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))),
# column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1)))), column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1))))
# inplace = True) )
PowInplaceTester = make_broadcast_tester(op = pow_inplace,
expected = lambda x, y: x ** y,
good = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))),
# _good_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),), scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))),
# integers = (randint_ranged(-5, 5, (2, 3)),)) row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))),
column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1))),
# _grad_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),)) dtype_mixup = (rand_ranged(-3, 3, (2, 3)), randint_ranged(-3, 3, (2, 3)))),
grad = dict(same_shapes = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 3))),
scalar = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 1))),
# AbsTester = make_broadcast_tester(op = tensor._abs, row = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (1, 3))),
# expected = lambda x: abs(x), column = (rand_ranged(1, 5, (2, 3)), rand_ranged(-3, 3, (2, 1)))),
# good = _good_broadcast_unary_normal, inplace = True)
# grad = _grad_broadcast_unary_normal)
# AbsInplaceTester = make_broadcast_tester(op = abs_inplace,
# expected = lambda x: abs(x),
# good = _good_broadcast_unary_normal, _good_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),),
# grad = _grad_broadcast_unary_normal, integers = (randint_ranged(-5, 5, (2, 3)),))
# inplace = True)
_grad_broadcast_unary_normal = dict(normal = (rand_ranged(-5, 5, (2, 3)),))
# NegTester = make_broadcast_tester(op = neg,
# expected = lambda x: -x,
# good = _good_broadcast_unary_normal, AbsTester = make_broadcast_tester(op = tensor._abs,
# grad = _grad_broadcast_unary_normal) expected = lambda x: abs(x),
# NegInplaceTester = make_broadcast_tester(op = neg_inplace, good = _good_broadcast_unary_normal,
# expected = lambda x: -x, grad = _grad_broadcast_unary_normal)
# good = _good_broadcast_unary_normal, AbsInplaceTester = make_broadcast_tester(op = abs_inplace,
# grad = _grad_broadcast_unary_normal, expected = lambda x: abs(x),
# inplace = True) good = _good_broadcast_unary_normal,
grad = _grad_broadcast_unary_normal,
# SgnTester = make_broadcast_tester(op = sgn, inplace = True)
# expected = numpy.sign,
# good = _good_broadcast_unary_normal) NegTester = make_broadcast_tester(op = neg,
# SgnInplaceTester = make_broadcast_tester(op = sgn_inplace, expected = lambda x: -x,
# expected = numpy.sign, good = _good_broadcast_unary_normal,
# good = _good_broadcast_unary_normal, grad = _grad_broadcast_unary_normal)
# inplace = True) NegInplaceTester = make_broadcast_tester(op = neg_inplace,
expected = lambda x: -x,
# SqrTester = make_broadcast_tester(op = sqr, good = _good_broadcast_unary_normal,
# expected = numpy.square, grad = _grad_broadcast_unary_normal,
# good = _good_broadcast_unary_normal, inplace = True)
# grad = _grad_broadcast_unary_normal)
# SqrInplaceTester = make_broadcast_tester(op = sqr_inplace, SgnTester = make_broadcast_tester(op = sgn,
# expected = numpy.square, expected = numpy.sign,
# good = _good_broadcast_unary_normal, good = _good_broadcast_unary_normal)
# grad = _grad_broadcast_unary_normal, SgnInplaceTester = make_broadcast_tester(op = sgn_inplace,
# inplace = True) expected = numpy.sign,
good = _good_broadcast_unary_normal,
# ExpTester = make_broadcast_tester(op = exp, inplace = True)
# expected = numpy.exp,
# good = _good_broadcast_unary_normal, SqrTester = make_broadcast_tester(op = sqr,
# grad = _grad_broadcast_unary_normal) expected = numpy.square,
# ExpInplaceTester = make_broadcast_tester(op = exp_inplace, good = _good_broadcast_unary_normal,
# expected = numpy.exp, grad = _grad_broadcast_unary_normal)
# good = _good_broadcast_unary_normal, SqrInplaceTester = make_broadcast_tester(op = sqr_inplace,
# grad = _grad_broadcast_unary_normal, expected = numpy.square,
# inplace = True) good = _good_broadcast_unary_normal,
grad = _grad_broadcast_unary_normal,
inplace = True)
# _good_broadcast_unary_positive = dict(normal = (rand_ranged(0.001, 5, (2, 3)),),
# integers = (randint_ranged(1, 5, (2, 3)),)) ExpTester = make_broadcast_tester(op = exp,
expected = numpy.exp,
# _grad_broadcast_unary_positive = dict(normal = (rand_ranged(0.001, 5, (2, 3)),)) good = _good_broadcast_unary_normal,
grad = _grad_broadcast_unary_normal)
# LogTester = make_broadcast_tester(op = log, ExpInplaceTester = make_broadcast_tester(op = exp_inplace,
# expected = numpy.log, expected = numpy.exp,
# good = _good_broadcast_unary_positive, good = _good_broadcast_unary_normal,
# grad = _grad_broadcast_unary_positive) grad = _grad_broadcast_unary_normal,
# LogInplaceTester = make_broadcast_tester(op = log_inplace, inplace = True)
# expected = numpy.log,
# good = _good_broadcast_unary_positive,
# grad = _grad_broadcast_unary_positive, _good_broadcast_unary_positive = dict(normal = (rand_ranged(0.001, 5, (2, 3)),),
# inplace = True) integers = (randint_ranged(1, 5, (2, 3)),))
# Log2Tester = make_broadcast_tester(op = log2, _grad_broadcast_unary_positive = dict(normal = (rand_ranged(0.001, 5, (2, 3)),))
# expected = numpy.log2,
# good = _good_broadcast_unary_positive, LogTester = make_broadcast_tester(op = log,
# grad = _grad_broadcast_unary_positive) expected = numpy.log,
# Log2InplaceTester = make_broadcast_tester(op = log2_inplace, good = _good_broadcast_unary_positive,
# expected = numpy.log2, grad = _grad_broadcast_unary_positive)
# good = _good_broadcast_unary_positive, LogInplaceTester = make_broadcast_tester(op = log_inplace,
# grad = _grad_broadcast_unary_positive, expected = numpy.log,
# inplace = True) good = _good_broadcast_unary_positive,
grad = _grad_broadcast_unary_positive,
# SqrtTester = make_broadcast_tester(op = sqrt, inplace = True)
# expected = numpy.sqrt,
# good = _good_broadcast_unary_positive, Log2Tester = make_broadcast_tester(op = log2,
# grad = _grad_broadcast_unary_positive) expected = numpy.log2,
# SqrtInplaceTester = make_broadcast_tester(op = sqrt_inplace, good = _good_broadcast_unary_positive,
# expected = numpy.sqrt, grad = _grad_broadcast_unary_positive)
# good = _good_broadcast_unary_positive, Log2InplaceTester = make_broadcast_tester(op = log2_inplace,
# grad = _grad_broadcast_unary_positive, expected = numpy.log2,
# inplace = True) good = _good_broadcast_unary_positive,
grad = _grad_broadcast_unary_positive,
inplace = True)
# _good_broadcast_unary_wide = dict(normal = (rand_ranged(-1000, 1000, (2, 3)),), SqrtTester = make_broadcast_tester(op = sqrt,
# integers = (randint_ranged(-1000, 1000, (2, 3)),)) expected = numpy.sqrt,
good = _good_broadcast_unary_positive,
# _grad_broadcast_unary_wide = dict(normal = (rand_ranged(-1000, 1000, (2, 3)),)) grad = _grad_broadcast_unary_positive)
SqrtInplaceTester = make_broadcast_tester(op = sqrt_inplace,
expected = numpy.sqrt,
# SinTester = make_broadcast_tester(op = sin, good = _good_broadcast_unary_positive,
# expected = numpy.sin, grad = _grad_broadcast_unary_positive,
# good = _good_broadcast_unary_wide, inplace = True)
# grad = _grad_broadcast_unary_wide)
# SinInplaceTester = make_broadcast_tester(op = sin_inplace,
# expected = numpy.sin,
# good = _good_broadcast_unary_wide, _good_broadcast_unary_wide = dict(normal = (rand_ranged(-1000, 1000, (2, 3)),),
# grad = _grad_broadcast_unary_wide, integers = (randint_ranged(-1000, 1000, (2, 3)),))
# inplace = True)
_grad_broadcast_unary_wide = dict(normal = (rand_ranged(-1000, 1000, (2, 3)),))
# CosTester = make_broadcast_tester(op = cos,
# expected = numpy.cos,
# good = _good_broadcast_unary_wide, SinTester = make_broadcast_tester(op = sin,
# grad = _grad_broadcast_unary_wide) expected = numpy.sin,
# CosInplaceTester = make_broadcast_tester(op = cos_inplace, good = _good_broadcast_unary_wide,
# expected = numpy.cos, grad = _grad_broadcast_unary_wide)
# good = _good_broadcast_unary_wide, SinInplaceTester = make_broadcast_tester(op = sin_inplace,
# grad = _grad_broadcast_unary_wide, expected = numpy.sin,
# inplace = True) good = _good_broadcast_unary_wide,
grad = _grad_broadcast_unary_wide,
# TanTester = make_broadcast_tester(op = tan, inplace = True)
# expected = numpy.tan,
# good = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),), CosTester = make_broadcast_tester(op = cos,
# shifted = (rand_ranged(3.15, 6.28, (2, 3)),)), expected = numpy.cos,
# grad = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),), good = _good_broadcast_unary_wide,
# shifted = (rand_ranged(3.15, 6.28, (2, 3)),))) grad = _grad_broadcast_unary_wide)
# TanInplaceTester = make_broadcast_tester(op = tan_inplace, CosInplaceTester = make_broadcast_tester(op = cos_inplace,
# expected = numpy.tan, expected = numpy.cos,
# good = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),), good = _good_broadcast_unary_wide,
# shifted = (rand_ranged(3.15, 6.28, (2, 3)),)), grad = _grad_broadcast_unary_wide,
# grad = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),), inplace = True)
# shifted = (rand_ranged(3.15, 6.28, (2, 3)),)),
# inplace = True) TanTester = make_broadcast_tester(op = tan,
expected = numpy.tan,
good = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),),
# CoshTester = make_broadcast_tester(op = cosh, shifted = (rand_ranged(3.15, 6.28, (2, 3)),)),
# expected = numpy.cosh, grad = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),),
# good = _good_broadcast_unary_normal, shifted = (rand_ranged(3.15, 6.28, (2, 3)),)))
# grad = _grad_broadcast_unary_normal) TanInplaceTester = make_broadcast_tester(op = tan_inplace,
# CoshInplaceTester = make_broadcast_tester(op = cosh_inplace, expected = numpy.tan,
# expected = numpy.cosh, good = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),),
# good = _good_broadcast_unary_normal, shifted = (rand_ranged(3.15, 6.28, (2, 3)),)),
# grad = _grad_broadcast_unary_normal, grad = dict(normal = (rand_ranged(-3.14, 3.14, (2, 3)),),
# inplace = True) shifted = (rand_ranged(3.15, 6.28, (2, 3)),)),
inplace = True)
# SinhTester = make_broadcast_tester(op = sinh,
# expected = numpy.sinh,
# good = _good_broadcast_unary_normal, CoshTester = make_broadcast_tester(op = cosh,
# grad = _grad_broadcast_unary_normal) expected = numpy.cosh,
# SinhInplaceTester = make_broadcast_tester(op = sinh_inplace, good = _good_broadcast_unary_normal,
# expected = numpy.sinh, grad = _grad_broadcast_unary_normal)
# good = _good_broadcast_unary_normal, CoshInplaceTester = make_broadcast_tester(op = cosh_inplace,
# grad = _grad_broadcast_unary_normal, expected = numpy.cosh,
# inplace = True) good = _good_broadcast_unary_normal,
grad = _grad_broadcast_unary_normal,
# TanhTester = make_broadcast_tester(op = tanh, inplace = True)
# expected = numpy.tanh,
# good = _good_broadcast_unary_normal, SinhTester = make_broadcast_tester(op = sinh,
# grad = _grad_broadcast_unary_normal) expected = numpy.sinh,
# TanhInplaceTester = make_broadcast_tester(op = tanh_inplace, good = _good_broadcast_unary_normal,
# expected = numpy.tanh, grad = _grad_broadcast_unary_normal)
# good = _good_broadcast_unary_normal, SinhInplaceTester = make_broadcast_tester(op = sinh_inplace,
# grad = _grad_broadcast_unary_normal, expected = numpy.sinh,
# inplace = True) good = _good_broadcast_unary_normal,
grad = _grad_broadcast_unary_normal,
inplace = True)
# DotTester = make_tester(name = 'DotTester', TanhTester = make_broadcast_tester(op = tanh,
# op = dot, expected = numpy.tanh,
# expected = lambda x, y: numpy.dot(x, y), good = _good_broadcast_unary_normal,
# checks = {}, grad = _grad_broadcast_unary_normal)
# good = dict(correct1 = (rand(5, 7), rand(7, 5)), TanhInplaceTester = make_broadcast_tester(op = tanh_inplace,
# correct2 = (rand(5, 7), rand(7, 9))), expected = numpy.tanh,
# bad_build = dict(), good = _good_broadcast_unary_normal,
# bad_runtime = dict(bad1 = (rand(5, 7), rand(5, 7)), grad = _grad_broadcast_unary_normal,
# bad2 = (rand(5, 7), rand(8, 3)))) inplace = True)
DotTester = make_tester(name = 'DotTester',
op = dot,
expected = lambda x, y: numpy.dot(x, y),
checks = {},
good = dict(correct1 = (rand(5, 7), rand(7, 5)),
correct2 = (rand(5, 7), rand(7, 9))),
bad_build = dict(),
bad_runtime = dict(bad1 = (rand(5, 7), rand(5, 7)),
bad2 = (rand(5, 7), rand(8, 3))))
...@@ -500,14 +500,14 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to ...@@ -500,14 +500,14 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to
# we could make loop over outputs making random projections R for each, # we could make loop over outputs making random projections R for each,
# but this doesn't handle the case where not all the outputs are # but this doesn't handle the case where not all the outputs are
# differentiable... so I leave this as TODO for now -JB. # differentiable... so I leave this as TODO for now -JB.
o_fn = Function(tensor_pt, o_outputs) o_fn = function(tensor_pt, o_outputs)
o_fn_out = o_fn(*pt) o_fn_out = o_fn(*pt)
random_projection = rng.rand(*o_fn_out.shape) random_projection = rng.rand(*o_fn_out.shape)
t_r = as_tensor(random_projection) t_r = as_tensor(random_projection)
#random projection of o onto t_r #random projection of o onto t_r
cost = sum(t_r * o_outputs[0]) cost = sum(t_r * o_outputs[0])
cost_fn = Function(tensor_pt, [cost]) cost_fn = function(tensor_pt, [cost])
num_grad = gradient.numeric_grad(cost_fn, pt) num_grad = gradient.numeric_grad(cost_fn, pt)
...@@ -518,7 +518,7 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to ...@@ -518,7 +518,7 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to
for op in gof.graph.io_toposort(tensor_pt, symbolic_grad): for op in gof.graph.io_toposort(tensor_pt, symbolic_grad):
print op print op
grad_fn = Function(tensor_pt, symbolic_grad) grad_fn = function(tensor_pt, symbolic_grad)
analytic_grad = grad_fn(*pt) analytic_grad = grad_fn(*pt)
if not isinstance(analytic_grad, (list, tuple)): if not isinstance(analytic_grad, (list, tuple)):
...@@ -635,7 +635,7 @@ class T_transpose(unittest.TestCase): ...@@ -635,7 +635,7 @@ class T_transpose(unittest.TestCase):
n = as_tensor(numpy.ones(())) n = as_tensor(numpy.ones(()))
t = transpose(n) t = transpose(n)
self.failUnless(t.owner.op == transpose_inplace) self.failUnless(t.owner.op == transpose_inplace)
f = Function([n], [t]) f = function([n], [t])
tval = f(n.data) tval = f(n.data)
self.failUnless(tval.shape == n.data.shape) self.failUnless(tval.shape == n.data.shape)
...@@ -647,7 +647,7 @@ class T_transpose(unittest.TestCase): ...@@ -647,7 +647,7 @@ class T_transpose(unittest.TestCase):
n = as_tensor(numpy.ones(5)) n = as_tensor(numpy.ones(5))
t = transpose(n) t = transpose(n)
self.failUnless(t.owner.op == transpose_inplace) self.failUnless(t.owner.op == transpose_inplace)
f = Function([n], [t]) f = function([n], [t])
tval = f(n.data) tval = f(n.data)
self.failUnless(tval.shape == n.data.shape) self.failUnless(tval.shape == n.data.shape)
#test aliasing #test aliasing
...@@ -658,7 +658,7 @@ class T_transpose(unittest.TestCase): ...@@ -658,7 +658,7 @@ class T_transpose(unittest.TestCase):
n = as_tensor(numpy.ones((5,3))) n = as_tensor(numpy.ones((5,3)))
t = transpose(n) t = transpose(n)
self.failUnless(t.owner.op == transpose_inplace) self.failUnless(t.owner.op == transpose_inplace)
f = Function([n], [t]) f = function([n], [t])
tval = f(n.data) tval = f(n.data)
self.failUnless(tval.shape == (3,5)) self.failUnless(tval.shape == (3,5))
#test aliasing #test aliasing
...@@ -670,7 +670,7 @@ class T_transpose(unittest.TestCase): ...@@ -670,7 +670,7 @@ class T_transpose(unittest.TestCase):
n = as_tensor(numpy.ones((5,3,2))) n = as_tensor(numpy.ones((5,3,2)))
t = transpose_inplace(n) t = transpose_inplace(n)
self.failUnless(t.owner.op == transpose_inplace) self.failUnless(t.owner.op == transpose_inplace)
f = Function([n], [t]) f = function([n], [t])
tval = f(n.data) tval = f(n.data)
self.failUnless(tval.shape == (2,3,5)) self.failUnless(tval.shape == (2,3,5))
#test aliasing #test aliasing
...@@ -1036,7 +1036,7 @@ class _testCase_matinv(unittest.TestCase): ...@@ -1036,7 +1036,7 @@ class _testCase_matinv(unittest.TestCase):
# compilation to function # compilation to function
# [a,b] are the inputs, [ssdiff,g_b] are the outputs # [a,b] are the inputs, [ssdiff,g_b] are the outputs
fn = Function([a,b], [ssdiff,g_b]) fn = function([a,b], [ssdiff,g_b])
# use the function # use the function
x = numpy.random.rand(dim,dim)+0.1 # Initialized s.t. x is not too tiny x = numpy.random.rand(dim,dim)+0.1 # Initialized s.t. x is not too tiny
...@@ -1133,7 +1133,7 @@ class t_gemm(unittest.TestCase): ...@@ -1133,7 +1133,7 @@ class t_gemm(unittest.TestCase):
z_orig = z.copy() z_orig = z.copy()
tz,ta,tx,ty,tb = [as_tensor(p).type() for p in z,a,x,y,b] tz,ta,tx,ty,tb = [as_tensor(p).type() for p in z,a,x,y,b]
f = Function([tz,ta,tx,ty,tb], [gemm(tz,ta,tx,ty,tb)], linker_cls=l) f = function([tz,ta,tx,ty,tb], [gemm(tz,ta,tx,ty,tb)], linker=l)
new_z = f(z,a,x,y,b) new_z = f(z,a,x,y,b)
z_after = self._gemm(z_orig, a, x, y, b) z_after = self._gemm(z_orig, a, x, y, b)
...@@ -1236,8 +1236,8 @@ class t_gemm(unittest.TestCase): ...@@ -1236,8 +1236,8 @@ class t_gemm(unittest.TestCase):
def test_destroy_map4(self): def test_destroy_map4(self):
"""test that dot args can be aliased""" """test that dot args can be aliased"""
Z = as_tensor(self.rand(2,2)) Z = value(self.rand(2,2))
A = as_tensor(self.rand(2,2)) A = value(self.rand(2,2))
eval_outputs([gemm(Z, 1.0, A, A, 1.0)]) eval_outputs([gemm(Z, 1.0, A, A, 1.0)])
eval_outputs([gemm(Z, 1.0, A, A.T, 1.0)]) eval_outputs([gemm(Z, 1.0, A, A.T, 1.0)])
...@@ -1253,9 +1253,9 @@ class t_gemm(unittest.TestCase): ...@@ -1253,9 +1253,9 @@ class t_gemm(unittest.TestCase):
z_orig = z.copy() z_orig = z.copy()
z_after = self._gemm(z, a, x, y, b) z_after = self._gemm(z, a, x, y, b)
tz,ta,tx,ty,tb = [as_tensor(p) for p in z,a,x,y,b] tz,ta,tx,ty,tb = [value(p) for p in z,a,x,y,b]
f = Function([tz,ta,tx,ty,tb], [gemm(tz,ta,tx,ty,tb)], linker_cls=l) f = function([tz,ta,tx,ty,tb], [gemm(tz,ta,tx,ty,tb)], linker=l)
f(z, a, x, y, b) f(z, a, x, y, b)
self.failUnless(_approx_eq(z_after, z), (z_orig, z_after, z)) self.failUnless(_approx_eq(z_after, z), (z_orig, z_after, z))
f(z.T, a, y.T, x.T, b) f(z.T, a, y.T, x.T, b)
...@@ -1424,3 +1424,4 @@ class t_gemm(unittest.TestCase): ...@@ -1424,3 +1424,4 @@ class t_gemm(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
#AddTester('test_grad').debug()
...@@ -6,7 +6,8 @@ from cc import * ...@@ -6,7 +6,8 @@ from cc import *
from type import Type from type import Type
from graph import Result, as_result, Apply, Constant from graph import Result, as_result, Apply, Constant
from op import Op from op import Op
from env import Env import env
import toolbox
class TDouble(Type): class TDouble(Type):
def filter(self, data): def filter(self, data):
...@@ -125,6 +126,11 @@ def inputs(): ...@@ -125,6 +126,11 @@ def inputs():
return x, y, z return x, y, z
def Env(inputs, outputs):
e = env.Env(inputs, outputs)
return e
class _test_CLinker(unittest.TestCase): class _test_CLinker(unittest.TestCase):
def test_straightforward(self): def test_straightforward(self):
......
...@@ -257,7 +257,6 @@ class _test_all(unittest.TestCase): ...@@ -257,7 +257,6 @@ class _test_all(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
#unittest.main() unittest.main()
_test_all('test_usage_loop_through_views').debug()
...@@ -161,14 +161,14 @@ class _test_clone(unittest.TestCase): ...@@ -161,14 +161,14 @@ class _test_clone(unittest.TestCase):
def test_accurate(self): def test_accurate(self):
r1, r2 = MyResult(1), MyResult(2) r1, r2 = MyResult(1), MyResult(2)
node = MyOp.make_node(r1, r2) node = MyOp.make_node(r1, r2)
new = clone([r1, r2], node.outputs) _, new = clone([r1, r2], node.outputs, False)
assert self.str([r1, r2], new) == ["MyOp(1, 2)"] assert self.str([r1, r2], new) == ["MyOp(1, 2)"]
def test_copy(self): def test_copy(self):
r1, r2, r5 = MyResult(1), MyResult(2), MyResult(5) r1, r2, r5 = MyResult(1), MyResult(2), MyResult(5)
node = MyOp.make_node(r1, r2) node = MyOp.make_node(r1, r2)
node2 = MyOp.make_node(node.outputs[0], r5) node2 = MyOp.make_node(node.outputs[0], r5)
new = clone([r1, r2, r5], node2.outputs) _, new = clone([r1, r2, r5], node2.outputs, False)
assert node2.outputs[0].type == new[0].type and node2.outputs[0] is not new[0] # the new output is like the old one but not the same object assert node2.outputs[0].type == new[0].type and node2.outputs[0] is not new[0] # the new output is like the old one but not the same object
assert node2 is not new[0].owner # the new output has a new owner assert node2 is not new[0].owner # the new output has a new owner
assert new[0].owner.inputs[1] is r5 # the inputs are not copied assert new[0].owner.inputs[1] is r5 # the inputs are not copied
...@@ -178,7 +178,7 @@ class _test_clone(unittest.TestCase): ...@@ -178,7 +178,7 @@ class _test_clone(unittest.TestCase):
# Checks that manipulating a cloned graph leaves the original unchanged. # Checks that manipulating a cloned graph leaves the original unchanged.
r1, r2, r5 = MyResult(1), MyResult(2), MyResult(5) r1, r2, r5 = MyResult(1), MyResult(2), MyResult(5)
node = MyOp.make_node(MyOp.make_node(r1, r2).outputs[0], r5) node = MyOp.make_node(MyOp.make_node(r1, r2).outputs[0], r5)
new = clone([r1, r2, r5], node.outputs) _, new = clone([r1, r2, r5], node.outputs, False)
new_node = new[0].owner new_node = new[0].owner
new_node.inputs = MyResult(7), MyResult(8) new_node.inputs = MyResult(7), MyResult(8)
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
import unittest import unittest
from graph import Result, as_result, Apply import graph
from graph import Result, as_result, Apply, Constant
from type import Type from type import Type
from op import Op from op import Op
from env import Env import env
import toolbox
from link import * from link import *
...@@ -67,6 +69,10 @@ def perform_linker(env): ...@@ -67,6 +69,10 @@ def perform_linker(env):
lnk = PerformLinker(env) lnk = PerformLinker(env)
return lnk return lnk
def Env(inputs, outputs):
e = env.Env(inputs, outputs)
return e
class _test_PerformLinker(unittest.TestCase): class _test_PerformLinker(unittest.TestCase):
...@@ -94,16 +100,14 @@ class _test_PerformLinker(unittest.TestCase): ...@@ -94,16 +100,14 @@ class _test_PerformLinker(unittest.TestCase):
def test_input_output_same(self): def test_input_output_same(self):
x, y, z = inputs() x, y, z = inputs()
a,d = add(x,y), div(x,y) fn = perform_linker(Env([x], [x])).make_function()
e = mul(a,d)
fn = perform_linker(Env([e], [e])).make_function()
self.failUnless(1.0 is fn(1.0)) self.failUnless(1.0 is fn(1.0))
def test_input_dependency0(self): def test_input_dependency0(self):
x, y, z = inputs() x, y, z = inputs()
a,d = add(x,y), div(x,y) a,d = add(x,y), div(x,y)
e = mul(a,d) e = mul(a,d)
fn = perform_linker(Env([x, y, a], [e])).make_function() fn = perform_linker(Env(*graph.clone([x, y, a], [e]))).make_function()
self.failUnless(fn(1.0,2.0,9.0) == 4.5) self.failUnless(fn(1.0,2.0,9.0) == 4.5)
def test_skiphole(self): def test_skiphole(self):
...@@ -111,9 +115,11 @@ class _test_PerformLinker(unittest.TestCase): ...@@ -111,9 +115,11 @@ class _test_PerformLinker(unittest.TestCase):
a = add(x,y) a = add(x,y)
r = raise_err(a) r = raise_err(a)
e = add(r,a) e = add(r,a)
fn = perform_linker(Env([x, y,r], [e])).make_function() fn = perform_linker(Env(*graph.clone([x, y,r], [e]))).make_function()
self.failUnless(fn(1.0,2.0,4.5) == 7.5) self.failUnless(fn(1.0,2.0,4.5) == 7.5)
# def test_disconnected_input_output(self): # def test_disconnected_input_output(self):
# x,y,z = inputs() # x,y,z = inputs()
# a = add(x,y) # a = add(x,y)
......
...@@ -415,4 +415,3 @@ if __name__ == '__main__': ...@@ -415,4 +415,3 @@ if __name__ == '__main__':
unittest.main() unittest.main()
from graph import Constant import graph
from graph import Constant, Value
from link import Linker, LocalLinker, raise_with_op, Filter, map_storage, PerformLinker from link import Linker, LocalLinker, raise_with_op, Filter, map_storage, PerformLinker
from copy import copy from copy import copy
from utils import AbstractFunctionError from utils import AbstractFunctionError
...@@ -284,10 +285,11 @@ def apply_policy(policy, r, name, sub): ...@@ -284,10 +285,11 @@ def apply_policy(policy, r, name, sub):
@type r: L{Result} @type r: L{Result}
@return: C{policy[0](r) + policy[1](r) + ...} @return: C{policy[0](r) + policy[1](r) + ...}
""" """
if isinstance(r, (list, tuple)): if isinstance(policy, (list, tuple)):
ret = "" ret = ""
for sub_policy in policy: for sub_policy in policy:
ret += sub_policy(r, name, sub) ret += sub_policy(r, name, sub)
return ret
return policy(r, name, sub) return policy(r, name, sub)
...@@ -345,7 +347,7 @@ class CLinker(Linker): ...@@ -345,7 +347,7 @@ class CLinker(Linker):
self.outputs = env.outputs self.outputs = env.outputs
self.results = list(env.results) self.results = list(env.results)
# The orphans field is listified to ensure a consistent order. # The orphans field is listified to ensure a consistent order.
self.orphans = list(env.orphans.difference(self.outputs)) self.orphans = list(r for r in self.results if isinstance(r, Value) and r not in self.inputs) #list(env.orphans.difference(self.outputs))
self.temps = list(set(self.results).difference(self.inputs).difference(self.outputs).difference(self.orphans)) self.temps = list(set(self.results).difference(self.inputs).difference(self.outputs).difference(self.orphans))
self.node_order = env.toposort() self.node_order = env.toposort()
...@@ -403,8 +405,9 @@ class CLinker(Linker): ...@@ -403,8 +405,9 @@ class CLinker(Linker):
policy = [[get_nothing, get_nothing, get_nothing], policy = [[get_nothing, get_nothing, get_nothing],
[get_c_declare, get_c_extract, get_c_cleanup]] [get_c_declare, get_c_extract, get_c_cleanup]]
elif result in self.orphans: elif result in self.orphans:
if not isinstance(result, Constant): if not isinstance(result, Value):
raise TypeError("All orphans to CLinker must be Constant.", result) raise TypeError("All orphans to CLinker must be Value instances.", result)
if isinstance(result, Constant):
try: try:
symbol[result] = "(" + result.type.c_literal(result.data) + ")" symbol[result] = "(" + result.type.c_literal(result.data) + ")"
consts.append(result) consts.append(result)
...@@ -428,7 +431,6 @@ class CLinker(Linker): ...@@ -428,7 +431,6 @@ class CLinker(Linker):
elif result in self.outputs: elif result in self.outputs:
# outputs don't need to be extracted from Python, so we call c_init rather than c_extract # outputs don't need to be extracted from Python, so we call c_init rather than c_extract
if result.type.c_is_simple() or result in no_recycling: if result.type.c_is_simple() or result in no_recycling:
policy = [[get_nothing, get_nothing, get_nothing], policy = [[get_nothing, get_nothing, get_nothing],
[get_c_declare, get_c_init, (get_c_sync, get_c_cleanup)]] [get_c_declare, get_c_init, (get_c_sync, get_c_cleanup)]]
else: else:
...@@ -599,7 +601,12 @@ class CLinker(Linker): ...@@ -599,7 +601,12 @@ class CLinker(Linker):
if input_storage is None: if input_storage is None:
input_storage = [[None] for result in self.inputs] input_storage = [[None] for result in self.inputs]
if output_storage is None: if output_storage is None:
output_storage = [[None] for result in self.outputs] map = {}
output_storage = []
for result in self.outputs:
if result not in map:
map[result] = [None]
output_storage.append(map[result])
thunk = self.cthunk_factory(error_storage, thunk = self.cthunk_factory(error_storage,
input_storage, input_storage,
output_storage) output_storage)
...@@ -642,13 +649,13 @@ class CLinker(Linker): ...@@ -642,13 +649,13 @@ class CLinker(Linker):
if not getattr(self, 'instantiate', False): if not getattr(self, 'instantiate', False):
self.code_gen() self.code_gen()
module_name = self.hash
# Eliminate duplicate inputs and outputs from the storage that we will pass to instantiate # Eliminate duplicate inputs and outputs from the storage that we will pass to instantiate
out_storage = [x for i, x in enumerate(out_storage) if (i+len(in_storage)) not in self.dupidx] out_storage = [x for i, x in enumerate(out_storage) if (i+len(in_storage)) not in self.dupidx]
in_storage = [x for i, x in enumerate(in_storage) if i not in self.dupidx] in_storage = [x for i, x in enumerate(in_storage) if i not in self.dupidx]
cthunk = object() # dummy so weave can get the type cthunk = object() # dummy so weave can get the type
module_name = self.hash
mod = weave.ext_tools.ext_module(module_name) mod = weave.ext_tools.ext_module(module_name)
argnames = ["i%i" % i for i in xrange(len(in_storage))] \ argnames = ["i%i" % i for i in xrange(len(in_storage))] \
...@@ -710,8 +717,11 @@ class CLinker(Linker): ...@@ -710,8 +717,11 @@ class CLinker(Linker):
# Eliminate duplicate inputs and outputs from the storage that we will pass to instantiate # Eliminate duplicate inputs and outputs from the storage that we will pass to instantiate
out_storage = [x for i, x in enumerate(out_storage) if (i+len(in_storage)) not in self.dupidx] out_storage = [x for i, x in enumerate(out_storage) if (i+len(in_storage)) not in self.dupidx]
in_storage = [x for i, x in enumerate(in_storage) if i not in self.dupidx] in_storage = [x for i, x in enumerate(in_storage) if i not in self.dupidx]
module_name = self.hash
module = __import__("%s" % (module_name), {}, {}, [module_name])
ret = module.instantiate(error_storage, *(in_storage + out_storage + [orphan.data for orphan in self.orphans])) orphd = [[orphan.data] for orphan in self.orphans]
ret = module.instantiate(error_storage, *(in_storage + out_storage + orphd))
assert sys.getrefcount(ret) == 2 # refcount leak check assert sys.getrefcount(ret) == 2 # refcount leak check
return ret return ret
...@@ -751,7 +761,9 @@ class OpWiseCLinker(LocalLinker): ...@@ -751,7 +761,9 @@ class OpWiseCLinker(LocalLinker):
node_input_storage = [storage_map[r] for r in node.inputs] node_input_storage = [storage_map[r] for r in node.inputs]
node_output_storage = [storage_map[r] for r in node.outputs] node_output_storage = [storage_map[r] for r in node.outputs]
try: try:
cl = CLinker(Env(node.inputs, node.outputs)) e = Env(*graph.clone(node.inputs, node.outputs))
e.toposort = lambda: e.nodes
cl = CLinker(e, [r for r, r2 in zip(e.outputs, node.outputs) if r2 in no_recycling])
thunk, node_input_filters, node_output_filters = cl.make_thunk( thunk, node_input_filters, node_output_filters = cl.make_thunk(
input_storage = node_input_storage, input_storage = node_input_storage,
output_storage = node_output_storage) output_storage = node_output_storage)
...@@ -823,7 +835,7 @@ class DualLinker(Linker): ...@@ -823,7 +835,7 @@ class DualLinker(Linker):
function. function.
""" """
def __init__(self, env, checker = _default_checker): def __init__(self, env, checker = _default_checker, no_recycling = []):
""" """
Initialize a DualLinker. Initialize a DualLinker.
...@@ -844,6 +856,7 @@ class DualLinker(Linker): ...@@ -844,6 +856,7 @@ class DualLinker(Linker):
""" """
self.env = env self.env = env
self.checker = checker self.checker = checker
self.no_recycling = no_recycling
def make_thunk(self, **kwargs): def make_thunk(self, **kwargs):
# if inplace: # if inplace:
...@@ -865,8 +878,10 @@ class DualLinker(Linker): ...@@ -865,8 +878,10 @@ class DualLinker(Linker):
# thunks2 = [c_make_thunk(op) for op in op_order_2] # thunks2 = [c_make_thunk(op) for op in op_order_2]
env = self.env env = self.env
_f, i1, o1, thunks1, order1 = PerformLinker(env).make_all(**kwargs) no_recycling = self.no_recycling
_f, i2, o2, thunks2, order2 = OpWiseCLinker(env).make_all(**kwargs)
_f, i1, o1, thunks1, order1 = PerformLinker(env, no_recycling = no_recycling).make_all(**kwargs)
_f, i2, o2, thunks2, order2 = OpWiseCLinker(env, no_recycling = no_recycling).make_all(**kwargs)
def f(): def f():
for input1, input2 in zip(i1, i2): for input1, input2 in zip(i1, i2):
...@@ -874,6 +889,12 @@ class DualLinker(Linker): ...@@ -874,6 +889,12 @@ class DualLinker(Linker):
# the copy is necessary in order for inplace ops not to interfere # the copy is necessary in order for inplace ops not to interfere
input2.storage[0] = copy(input1.storage[0]) input2.storage[0] = copy(input1.storage[0])
for thunk1, thunk2, node1, node2 in zip(thunks1, thunks2, order1, order2): for thunk1, thunk2, node1, node2 in zip(thunks1, thunks2, order1, order2):
for output, storage in zip(node1.outputs, thunk1.outputs):
if output in no_recycling:
storage[0] = None
for output, storage in zip(node2.outputs, thunk2.outputs):
if output in no_recycling:
storage[0] = None
try: try:
thunk1() thunk1()
thunk2() thunk2()
......
...@@ -26,15 +26,7 @@ class Env(object): #(graph.Graph): ...@@ -26,15 +26,7 @@ class Env(object): #(graph.Graph):
The Env supports the replace operation which allows to replace a The Env supports the replace operation which allows to replace a
result in the subgraph by another, e.g. replace (x + x).out by (2 result in the subgraph by another, e.g. replace (x + x).out by (2
* x).out. This is the basis for optimization in omega. * x).out. This is the basis for optimization in theano.
Regarding inputs and orphans:
In the context of a computation graph, the inputs and orphans are
both results that are the source nodes of computation. Those
results that are named as inputs will be assumed to contain fresh.
In other words, the backward search from outputs will stop at any
node that has been explicitly named as an input.
""" """
### Special ### ### Special ###
...@@ -69,10 +61,6 @@ class Env(object): #(graph.Graph): ...@@ -69,10 +61,6 @@ class Env(object): #(graph.Graph):
self.node_locks = {} self.node_locks = {}
self.result_locks = {} self.result_locks = {}
# # List of functions that undo the replace operations performed.
# # e.g. to recover the initial graph one could write: for u in self.history.__reversed__(): u()
# self.history = []
### Setup a Result ### ### Setup a Result ###
...@@ -237,99 +225,13 @@ class Env(object): #(graph.Graph): ...@@ -237,99 +225,13 @@ class Env(object): #(graph.Graph):
raise TypeError("The type of the replacement must be the same as the type of the original Result.", r, new_r) raise TypeError("The type of the replacement must be the same as the type of the original Result.", r, new_r)
assert r in self.results assert r in self.results
for node, i in r.clients: for node, i in list(r.clients):
assert node == 'output' and self.outputs[i] is r or node.inputs[i] is r assert node == 'output' and self.outputs[i] is r or node.inputs[i] is r
self.change_input(node, i, new_r) self.change_input(node, i, new_r)
# # Save where we are so we can backtrack def replace_all(self, pairs):
# if consistency_check: for r, new_r in pairs:
# chk = self.checkpoint() self.replace(r, new_r)
# # The copy is required so undo can know what clients to move back!
# clients = copy(self.clients(r))
# # Messy checks so we know what to do if we are replacing an output
# # result. Note that if v is an input result, we do nothing at all for
# # now (it's not clear what it means to replace an input result).
# was_output = False
# if r in self.outputs:
# was_output = True
# self.outputs[self.outputs.index(r)] = new_r
# was_input = False
# if r in self.inputs:
# was_input = True
# self.inputs[self.inputs.index(r)] = new_r
# # The actual replacement operation occurs here. This might raise
# # an error.
# self.__move_clients__(clients, r, new_r) # not sure how to order this wrt to adjusting the outputs
# # This function undoes the replacement.
# def undo():
# # Restore self.outputs
# if was_output:
# self.outputs[self.outputs.index(new_r)] = r
# # Restore self.inputs
# if was_input:
# self.inputs[self.inputs.index(new_r)] = r
# # Move back the clients. This should never raise an error.
# self.__move_clients__(clients, new_r, r)
# self.history.append(undo)
# if consistency_check:
# try:
# self.validate()
# except InconsistencyError, e:
# self.revert(chk)
# raise
def replace_all(self, d):
"""
For (r, new_r) in d.items(), replaces r with new_r. Checks for
consistency at the end and raises an InconsistencyError if the
graph is not consistent. If an error is raised, the graph is
restored to what it was before.
"""
for r, new_r in d.items():
self.replace(r, new_r, False)
# chk = self.checkpoint()
# try:
# for r, new_r in d.items():
# self.replace(r, new_r, False)
# except Exception, e:
# self.revert(chk)
# raise
# try:
# self.validate()
# except InconsistencyError, e:
# self.revert(chk)
# raise
# def checkpoint(self):
# """
# Returns an object that can be passed to self.revert in order to backtrack
# to a previous state.
# """
# return len(self.history)
# def consistent(self):
# """
# Returns True iff the subgraph is consistent and does not violate the
# constraints set by the listeners.
# """
# try:
# self.validate()
# except InconsistencyError:
# return False
# return True
### features ### ### features ###
...@@ -386,6 +288,16 @@ class Env(object): #(graph.Graph): ...@@ -386,6 +288,16 @@ class Env(object): #(graph.Graph):
### misc ### ### misc ###
def toposort(self):
env = self
ords = {}
for feature in env._features:
if hasattr(feature, 'orderings'):
for op, prereqs in feature.orderings(env).items():
ords.setdefault(op, set()).update(prereqs)
order = graph.io_toposort(env.inputs, env.outputs, ords)
return order
def nclients(self, r): def nclients(self, r):
"Same as len(self.clients(r))." "Same as len(self.clients(r))."
return len(self.clients(r)) return len(self.clients(r))
...@@ -439,117 +351,9 @@ class Env(object): #(graph.Graph): ...@@ -439,117 +351,9 @@ class Env(object): #(graph.Graph):
if node.inputs[i] is not result: if node.inputs[i] is not result:
raise Exception("Inconsistent clients list.", result, node.inputs[i]) raise Exception("Inconsistent clients list.", result, node.inputs[i])
# def revert(self, checkpoint):
# """
# Reverts the graph to whatever it was at the provided
# checkpoint (undoes all replacements). A checkpoint at any
# given time can be obtained using self.checkpoint().
# """
# while len(self.history) > checkpoint:
# f = self.history.pop()
# f()
# def supplemental_orderings(self):
# """
# Returns a dictionary of {op: set(prerequisites)} that must
# be satisfied in addition to the order defined by the structure
# of the graph (returns orderings that not related to input/output
# relationships).
# """
# ords = {}
# for feature in self._features:
# if hasattr(feature, 'orderings'):
# for op, prereqs in feature.orderings().items():
# ords.setdefault(op, set()).update(prereqs)
# return ords
# def toposort(self):
# """
# Returns a list of nodes in the order that they must be executed
# in order to preserve the semantics of the graph and respect
# the constraints put forward by the listeners.
# """
# ords = self.supplemental_orderings()
# order = graph.io_toposort(self.inputs, self.outputs, ords)
# return order
# def validate(self):
# """
# Raises an error if the graph is inconsistent.
# """
# self.execute_callbacks('validate')
# # for constraint in self._constraints.values():
# # constraint.validate()
# return True
### Private interface ###
# def __move_clients__(self, clients, r, new_r):
# if not (r.type == new_r.type):
# raise TypeError("Cannot move clients between Results that have different types.", r, new_r)
# # We import the new result in the fold
# self.__import_r__([new_r])
# for op, i in clients:
# op.inputs[i] = new_r
# # try:
# # # Try replacing the inputs
# # for op, i in clients:
# # op.set_input(i, new_r)
# # except:
# # # Oops!
# # for op, i in clients:
# # op.set_input(i, r)
# # self.__prune_r__([new_r])
# # raise
# self.__remove_clients__(r, clients)
# self.__add_clients__(new_r, clients)
# # # We import the new result in the fold
# # # why was this line AFTER the set_inputs???
# # # if we do it here then satisfy in import fucks up...
# # self.__import_r__([new_r])
# self.execute_callbacks('on_rewire', clients, r, new_r)
# # for listener in self._listeners.values():
# # try:
# # listener.on_rewire(clients, r, new_r)
# # except AbstractFunctionError:
# # pass
# # We try to get rid of the old one
# self.__prune_r__([r])
def __str__(self): def __str__(self):
return "[%s]" % ", ".join(graph.as_string(self.inputs, self.outputs)) return "[%s]" % ", ".join(graph.as_string(self.inputs, self.outputs))
# def clone_get_equiv(self, clone_inputs = True):
# equiv = graph.clone_get_equiv(self.inputs, self.outputs, clone_inputs)
# new = self.__class__([equiv[input] for input in self.inputs],
# [equiv[output] for output in self.outputs])
# for feature in self._features:
# new.extend(feature)
# return new, equiv
# def clone(self, clone_inputs = True):
# equiv = graph.clone_get_equiv(self.inputs, self.outputs, clone_inputs)
# new = self.__class__([equiv[input] for input in self.inputs],
# [equiv[output] for output in self.outputs])
# for feature in self._features:
# new.extend(feature)
# try:
# new.set_equiv(equiv)
# except AttributeError:
# pass
# return new
# def __copy__(self):
# return self.clone()
......
#from features import Listener, Constraint, Orderings, Tool #from features import Listener, Constraint, Orderings, Tool
import graph
import utils import utils
from utils import AbstractFunctionError from utils import AbstractFunctionError
...@@ -253,7 +256,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -253,7 +256,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
""" """
self.seen.add(op) self.seen.add(op)
op.deps['destroy'] = []
view_map, destroy_map = self.get_maps(op) view_map, destroy_map = self.get_maps(op)
for input in op.inputs: for input in op.inputs:
...@@ -334,7 +336,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -334,7 +336,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
del self.children[output] del self.children[output]
self.seen.remove(op) self.seen.remove(op)
del op.deps['destroy']
def __add_destroyer__(self, path): def __add_destroyer__(self, path):
...@@ -350,9 +351,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -350,9 +351,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
destroyers = self.destroyers.setdefault(foundation, {}) destroyers = self.destroyers.setdefault(foundation, {})
path = destroyers.setdefault(node, path) path = destroyers.setdefault(node, path)
print "add", path
node.deps['destroy'] += [user.owner for user in self.__users__(foundation) if user not in node.outputs]
# for foundation, destroyers in self.destroyers.items(): # for foundation, destroyers in self.destroyers.items():
# for op in destroyers.keys(): # for op in destroyers.keys():
# ords.setdefault(op, set()).update([user.owner for user in self.__users__(foundation) if user not in op.outputs]) # ords.setdefault(op, set()).update([user.owner for user in self.__users__(foundation) if user not in op.outputs])
...@@ -361,7 +359,7 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -361,7 +359,7 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
self.dups.add(foundation) self.dups.add(foundation)
# results marked 'indestructible' must not be destroyed. # results marked 'indestructible' must not be destroyed.
if getattr(foundation, 'indestructible', False): if getattr(foundation, 'indestructible', False) or isinstance(foundation, graph.Constant):
self.illegal.add(foundation) self.illegal.add(foundation)
...@@ -374,13 +372,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -374,13 +372,6 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
target = path[-1] target = path[-1]
node = target.owner node = target.owner
print "rm", path
print node.deps['destroy']
for user in self.__users__(foundation):
print " -- ", user
if user not in node.outputs:
node.deps['destroy'].remove(user.owner)
destroyers = self.destroyers[foundation] destroyers = self.destroyers[foundation]
del destroyers[node] del destroyers[node]
...@@ -477,6 +468,7 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool): ...@@ -477,6 +468,7 @@ class DestroyHandler(Bookkeeper): #(Listener, Constraint, Orderings, Tool):
In particular, all the users of a destroyed result have priority over the In particular, all the users of a destroyed result have priority over the
L{Op} that destroys the result. L{Op} that destroys the result.
""" """
self.validate(env)
ords = {} ords = {}
for foundation, destroyers in self.destroyers.items(): for foundation, destroyers in self.destroyers.items():
for op in destroyers.keys(): for op in destroyers.keys():
......
...@@ -163,7 +163,6 @@ def as_apply(x): ...@@ -163,7 +163,6 @@ def as_apply(x):
@deprecated @deprecated
def inputs(o): def inputs(o):
""" """
...@@ -173,7 +172,6 @@ def inputs(o): ...@@ -173,7 +172,6 @@ def inputs(o):
Returns the set of inputs necessary to compute the outputs in o Returns the set of inputs necessary to compute the outputs in o
such that input.owner is None. such that input.owner is None.
""" """
print 'gof.graph.inputs deprecated: April 29'
results = set() results = set()
def seek(r): def seek(r):
op = r.owner op = r.owner
...@@ -187,53 +185,71 @@ def inputs(o): ...@@ -187,53 +185,71 @@ def inputs(o):
return results return results
def results_and_orphans(i, o, except_unreachable_input=False): # def results_and_orphans(i, o, except_unreachable_input=False):
""" # """
@type i: list # @type i: list
@param i: input L{Result}s # @param i: input L{Result}s
@type o: list # @type o: list
@param o: output L{Result}s # @param o: output L{Result}s
# Returns the pair (results, orphans). The former is the set of
# L{Result}s that are involved in the subgraph that lies between i and
# o. This includes i, o, orphans(i, o) and all results of all
# intermediary steps from i to o. The second element of the returned
# pair is orphans(i, o).
# """
# results = set()
# i = set(i)
# # results.update(i)
# incomplete_paths = []
# reached = set()
# def helper(r, path):
# if r in i:
# reached.add(r)
# results.update(path)
# elif r.owner is None:
# incomplete_paths.append(path)
# else:
# op = r.owner
# for r2 in op.inputs:
# helper(r2, path + [r2])
Returns the pair (results, orphans). The former is the set of # for output in o:
L{Result}s that are involved in the subgraph that lies between i and # helper(output, [output])
o. This includes i, o, orphans(i, o) and all results of all
intermediary steps from i to o. The second element of the returned
pair is orphans(i, o).
"""
results = set()
i = set(i)
# results.update(i)
incomplete_paths = []
reached = set()
def helper(r, path):
if r in i:
reached.add(r)
results.update(path)
elif r.owner is None:
incomplete_paths.append(path)
else:
op = r.owner
for r2 in op.inputs:
helper(r2, path + [r2])
for output in o: # orphans = set()
helper(output, [output]) # for path in incomplete_paths:
# for r in path:
# if r not in results:
# orphans.add(r)
# break
orphans = set() # if except_unreachable_input and len(i) != len(reached):
for path in incomplete_paths: # raise Exception(results_and_orphans.E_unreached)
for r in path:
if r not in results:
orphans.add(r)
break
if except_unreachable_input and len(i) != len(reached): # results.update(orphans)
raise Exception(results_and_orphans.E_unreached)
results.update(orphans) # return results, orphans
# results_and_orphans.E_unreached = 'there were unreachable inputs'
def results_and_orphans(i, o):
results = set()
orphans = set()
def helper(r):
if r in results:
return
results.add(r)
if r.owner is None:
if r not in i:
orphans.add(r)
else:
for r2 in r.owner.inputs:
helper(r2)
for output in o:
helper(output)
return results, orphans return results, orphans
results_and_orphans.E_unreached = 'there were unreachable inputs'
def ops(i, o): def ops(i, o):
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from utils import AbstractFunctionError from utils import AbstractFunctionError
import utils import utils
from graph import Constant from graph import Value
import sys import sys
import traceback import traceback
...@@ -135,16 +135,20 @@ def map_storage(env, order, input_storage, output_storage): ...@@ -135,16 +135,20 @@ def map_storage(env, order, input_storage, output_storage):
storage_map = {} storage_map = {}
for r, storage in zip(env.inputs, input_storage): for r, storage in zip(env.inputs, input_storage):
storage_map[r] = storage storage_map[r] = storage
for orphan in env.orphans: # for orphan in env.orphans:
if not isinstance(orphan, Constant): # if not isinstance(orphan, Constant):
raise TypeError("Cannot link a graph with non-constant orphans.", orphan) # raise TypeError("Cannot link a graph with non-constant orphans.", orphan)
storage_map[orphan] = [orphan.data] # storage_map[orphan] = [orphan.data]
if output_storage is not None: if output_storage is not None:
assert len(env.outputs) == len(output_storage) assert len(env.outputs) == len(output_storage)
for r, storage in zip(env.outputs, output_storage): for r, storage in zip(env.outputs, output_storage):
storage_map[r] = storage storage_map[r] = storage
thunks = [] thunks = []
for node in order: for node in order:
for r in node.inputs:
if r not in storage_map:
assert isinstance(r, Value)
storage_map[r] = [r.data]
for r in node.outputs: for r in node.outputs:
storage_map.setdefault(r, [None]) storage_map.setdefault(r, [None])
......
...@@ -430,11 +430,16 @@ class MergeOptimizer(Optimizer): ...@@ -430,11 +430,16 @@ class MergeOptimizer(Optimizer):
are constant. are constant.
""" """
def add_requirements(self, env):
try:
env.extend(toolbox.ReplaceValidate())
except: pass
def apply(self, env): def apply(self, env):
cid = _metadict() #result -> result.desc() (for constants) cid = _metadict() #result -> result.desc() (for constants)
inv_cid = _metadict() #desc -> result (for constants) inv_cid = _metadict() #desc -> result (for constants)
for i, r in enumerate(env.orphans.union(env.inputs)): for i, r in enumerate([r for r in env.results if isinstance(r, Constant)]): #env.orphans.union(env.inputs)):
if isinstance(r, Constant): #if isinstance(r, Constant):
sig = r.signature() sig = r.signature()
other_r = inv_cid.get(sig, None) other_r = inv_cid.get(sig, None)
if other_r is not None: if other_r is not None:
...@@ -446,20 +451,19 @@ class MergeOptimizer(Optimizer): ...@@ -446,20 +451,19 @@ class MergeOptimizer(Optimizer):
# and it's more efficient to give them an integer cid like the other Results # and it's more efficient to give them an integer cid like the other Results
cid.clear() cid.clear()
inv_cid.clear() inv_cid.clear()
for i, r in enumerate(env.orphans.union(env.inputs)): for i, r in enumerate(r for r in env.results if r.owner is None):
cid[r] = i cid[r] = i
inv_cid[i] = r inv_cid[i] = r
for node in env.io_toposort(): for node in graph.io_toposort(env.inputs, env.outputs):
node_cid = (node.op, tuple([cid[input] for input in node.inputs])) node_cid = (node.op, tuple([cid[input] for input in node.inputs]))
dup = inv_cid.get(node_cid, None) dup = inv_cid.get(node_cid, None)
success = False success = False
if dup is not None: if dup is not None:
success = True success = True
d = dict(zip(node.outputs, dup.outputs))
try: try:
env.replace_all(d) env.replace_all_validate(zip(node.outputs, dup.outputs))
except Exception, e: except InconsistencyError, e:
success = False success = False
if not success: if not success:
cid[node] = node_cid cid[node] = node_cid
......
...@@ -16,6 +16,51 @@ class Bookkeeper: ...@@ -16,6 +16,51 @@ class Bookkeeper:
self.on_prune(env, node) self.on_prune(env, node)
class Toposorter:
def on_attach(self, env):
if hasattr(env, 'toposort'):
raise Exception("Toposorter feature is already present or in conflict with another plugin.")
env.toposort = partial(self.toposort, env)
def on_deattach(self, env):
del env.toposort
def toposort(self, env):
ords = {}
for feature in env._features:
if hasattr(feature, 'orderings'):
for op, prereqs in feature.orderings(env).items():
ords.setdefault(op, set()).update(prereqs)
order = graph.io_toposort(env.inputs, env.outputs, ords)
return order
# def supplemental_orderings(self):
# """
# Returns a dictionary of {op: set(prerequisites)} that must
# be satisfied in addition to the order defined by the structure
# of the graph (returns orderings that not related to input/output
# relationships).
# """
# ords = {}
# for feature in self._features:
# if hasattr(feature, 'orderings'):
# for op, prereqs in feature.orderings().items():
# ords.setdefault(op, set()).update(prereqs)
# return ords
# def toposort(self):
# """
# Returns a list of nodes in the order that they must be executed
# in order to preserve the semantics of the graph and respect
# the constraints put forward by the listeners.
# """
# ords = self.supplemental_orderings()
# order = graph.io_toposort(self.inputs, self.outputs, ords)
# return order
class History: class History:
def __init__(self): def __init__(self):
......
...@@ -25,10 +25,6 @@ def as_scalar(x, name = None): ...@@ -25,10 +25,6 @@ def as_scalar(x, name = None):
if not isinstance(x.type, Scalar): if not isinstance(x.type, Scalar):
raise TypeError("Result type field must be a Scalar.", x, x.type) raise TypeError("Result type field must be a Scalar.", x, x.type)
return x return x
if isinstance(x, Constant):
if not isinstance(x.type, Scalar):
raise TypeError("Constant type field must be a Scalar.", x, x.type)
return x
try: try:
return constant(x) return constant(x)
except TypeError: except TypeError:
...@@ -582,7 +578,7 @@ tanh = Tanh(upgrade_to_float, name = 'tanh') ...@@ -582,7 +578,7 @@ tanh = Tanh(upgrade_to_float, name = 'tanh')
class Composite(ScalarOp): class Composite(ScalarOp):
def __init__(self, inputs, outputs): def __init__(self, inputs, outputs):
env = Env(inputs, outputs).clone() env = Env(*gof.graph.clone(inputs, outputs))
inputs, outputs = env.inputs, env.outputs inputs, outputs = env.inputs, env.outputs
for node in env.nodes: for node in env.nodes:
...@@ -594,7 +590,8 @@ class Composite(ScalarOp): ...@@ -594,7 +590,8 @@ class Composite(ScalarOp):
zip(outputs, zip(outputs,
["%%(o%i)s"%i for i in range(len(outputs))])) ["%%(o%i)s"%i for i in range(len(outputs))]))
for orphan in env.orphans: for orphan in env.results: #env.orphans:
if orphan.owner is None and orphan not in env.inputs:
if isinstance(orphan, Constant): if isinstance(orphan, Constant):
subd[orphan] = orphan.type.c_literal(orphan.data) subd[orphan] = orphan.type.c_literal(orphan.data)
else: else:
...@@ -611,7 +608,7 @@ class Composite(ScalarOp): ...@@ -611,7 +608,7 @@ class Composite(ScalarOp):
name = "V%%(id)s_tmp%i" % i name = "V%%(id)s_tmp%i" % i
subd[output] = name subd[output] = name
_c_code += "%s %s;\n" % (output.type.dtype_specs()[1], name) _c_code += "%s %s;\n" % (output.type.dtype_specs()[1], name)
_c_code += node.op.c_code(node.inputs, _c_code += node.op.c_code(node,
"%(name)s", "%(name)s",
[subd[input] for input in node.inputs], [subd[input] for input in node.inputs],
[subd[output] for output in node.outputs], [subd[output] for output in node.outputs],
...@@ -629,7 +626,7 @@ class Composite(ScalarOp): ...@@ -629,7 +626,7 @@ class Composite(ScalarOp):
if r in env.inputs: if r in env.inputs:
idx = env.inputs.index(r) idx = env.inputs.index(r)
return lambda inputs: inputs[idx] return lambda inputs: inputs[idx]
elif r in env.orphans: elif r.owner is None: # in env.orphans:
return lambda inputs: r.data return lambda inputs: r.data
node = r.owner node = r.owner
producers = [compose_impl(input) for input in node.inputs] producers = [compose_impl(input) for input in node.inputs]
......
...@@ -6,11 +6,11 @@ To read about different sparse formats, see U{http://www-users.cs.umn.edu/~saad/ ...@@ -6,11 +6,11 @@ To read about different sparse formats, see U{http://www-users.cs.umn.edu/~saad/
@todo: Automatic methods for determining best sparse format? @todo: Automatic methods for determining best sparse format?
""" """
import copy #for __copy__
import numpy import numpy
from scipy import sparse from scipy import sparse
import gof.op, gof.result import gof
import gof.op
import tensor import tensor
...@@ -20,24 +20,22 @@ _mtypes = [sparse.csc_matrix, sparse.csr_matrix] ...@@ -20,24 +20,22 @@ _mtypes = [sparse.csc_matrix, sparse.csr_matrix]
_mtype_to_str = {sparse.csc_matrix: "csc", sparse.csr_matrix: "csr"} _mtype_to_str = {sparse.csc_matrix: "csc", sparse.csr_matrix: "csr"}
## Type checking
def _is_sparse_result(x): def _is_sparse_result(x):
""" """
@rtype: boolean @rtype: boolean
@return: True iff x is a L{SparseResult} (and not a L{tensor.Tensor}) @return: True iff x is a L{SparseResult} (and not a L{tensor.Tensor})
""" """
if not isinstance(x, SparseResult) and not isinstance(x, tensor.Tensor): if not isinstance(x.type, Sparse) and not isinstance(x.type, tensor.Tensor):
raise NotImplementedError("_is_sparse should only be called on sparse.SparseResult or tensor.Tensor, not,", x) raise NotImplementedError("this function should only be called on results of type sparse.Sparse or tensor.Tensor, not,", x)
return isinstance(x, SparseResult) return isinstance(x.type, Sparse)
def _is_dense_result(x): def _is_dense_result(x):
""" """
@rtype: boolean @rtype: boolean
@return: True unless x is a L{SparseResult} (and not a L{tensor.Tensor}) @return: True unless x is a L{SparseResult} (and not a L{tensor.Tensor})
""" """
if not isinstance(x, SparseResult) and not isinstance(x, tensor.Tensor): if not isinstance(x.type, Sparse) and not isinstance(x.type, tensor.Tensor):
raise NotImplementedError("_is_sparse should only be called on sparse.SparseResult or tensor.Tensor, not,", x) raise NotImplementedError("this function should only be called on results of type sparse.Sparse or tensor.Tensor, not,", x)
return isinstance(x, tensor.Tensor) return isinstance(x.type, tensor.Tensor)
def _is_sparse(x): def _is_sparse(x):
""" """
...@@ -45,7 +43,7 @@ def _is_sparse(x): ...@@ -45,7 +43,7 @@ def _is_sparse(x):
@return: True iff x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray}) @return: True iff x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray})
""" """
if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray): if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray):
raise NotImplementedError("_is_sparse should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x) raise NotImplementedError("this function should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x)
return isinstance(x, sparse.spmatrix) return isinstance(x, sparse.spmatrix)
def _is_dense(x): def _is_dense(x):
""" """
...@@ -53,37 +51,61 @@ def _is_dense(x): ...@@ -53,37 +51,61 @@ def _is_dense(x):
@return: True unless x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray}) @return: True unless x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray})
""" """
if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray): if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray):
raise NotImplementedError("_is_sparse should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x) raise NotImplementedError("this function should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x)
return isinstance(x, numpy.ndarray) return isinstance(x, numpy.ndarray)
# Wrapper type # Wrapper type
def assparse(sp, **kwargs): def as_sparse(x):
""" """
Wrapper around SparseResult constructor. Wrapper around SparseResult constructor.
@param sp: A sparse matrix. assparse reads dtype and format properties @param x: A sparse matrix. as_sparse reads dtype and format properties
out of this sparse matrix. out of this sparse matrix.
@return: SparseResult version of sp. @return: SparseResult version of sp.
@todo Verify that sp is sufficiently sparse, and raise a warning if it is not @todo Verify that sp is sufficiently sparse, and raise a warning if it is not
""" """
if isinstance(sp, SparseResult): if isinstance(x, gof.Apply):
rval = sp if len(x.outputs) != 1:
raise ValueError("It is ambiguous which output of a multi-output Op has to be fetched.", x)
else: else:
# @todo Verify that sp is sufficiently sparse, and raise a x = x.outputs[0]
# warning if it is not if isinstance(x, gof.Result):
rval = SparseResult(str(sp.dtype), sp.format, **kwargs) if not isinstance(x.type, Sparse):
rval.data = sp raise TypeError("Result type field must be a Sparse.", x, x.type)
assert _is_sparse_result(rval) return x
return rval try:
return constant(x)
class SparseResult(gof.result.Result): except TypeError:
raise TypeError("Cannot convert %s to Sparse" % x, type(x))
def constant(x):
if not isinstance(x, sparse.spmatrix):
raise TypeError("sparse.constant must be called on a scipy.sparse.spmatrix")
try:
return SparseConstant(Sparse(format = x.format,
dtype = x.dtype), x)
except TypeError:
raise TypeError("Could not convert %s to Sparse" % x, type(x))
def value(x):
if not isinstance(x, sparse.spmatrix):
raise TypeError("sparse.value must be called on a scipy.sparse.spmatrix")
try:
return SparseValue(Sparse(format = x.format,
dtype = x.dtype), x)
except TypeError:
raise TypeError("Could not convert %s to Sparse" % x, type(x))
class Sparse(gof.Type):
""" """
@type _dtype: numpy dtype string such as 'int64' or 'float64' (among others) @type dtype: numpy dtype string such as 'int64' or 'float64' (among others)
@type _format: string @type format: string
@ivar _format: The sparse storage strategy. @ivar format: The sparse storage strategy.
@note As far as I can tell, L{scipy.sparse} objects must be matrices, i.e. have dimension 2. @note As far as I can tell, L{scipy.sparse} objects must be matrices, i.e. have dimension 2.
""" """
...@@ -92,8 +114,9 @@ class SparseResult(gof.result.Result): ...@@ -92,8 +114,9 @@ class SparseResult(gof.result.Result):
'csc' : sparse.csc_matrix 'csc' : sparse.csc_matrix
} }
dtype_set = set(['int', 'int32', 'int64', 'float32', 'float64']) dtype_set = set(['int', 'int32', 'int64', 'float32', 'float64'])
ndim = 2
def __init__(self, dtype, format, **kwargs): def __init__(self, format, dtype = 'float64'):
""" """
Fundamental way to create a sparse node. Fundamental way to create a sparse node.
@param dtype: Type of numbers in the matrix. @param dtype: Type of numbers in the matrix.
...@@ -101,147 +124,169 @@ class SparseResult(gof.result.Result): ...@@ -101,147 +124,169 @@ class SparseResult(gof.result.Result):
@return An empty SparseResult instance. @return An empty SparseResult instance.
""" """
gof.Result.__init__(self, **kwargs) dtype = str(dtype)
if dtype in SparseResult.dtype_set: if dtype in self.dtype_set:
self._dtype = dtype self.dtype = dtype
assert isinstance(format, str) else:
raise NotImplementedError('unsupported dtype "%s" not in list' % dtype, list(self.dtype_set))
#print format, type(format), SparseResult.format_cls.keys(), format in SparseResult.format_cls assert isinstance(format, str)
if format in SparseResult.format_cls: if format in self.format_cls:
self._format = format self.format = format
else: else:
raise NotImplementedError('unsupported format "%s" not in list' % format, SparseResult.format_cls.keys()) raise NotImplementedError('unsupported format "%s" not in list' % format, self.format_cls.keys())
def filter(self, value): def filter(self, value, strict = False):
if isinstance(value, SparseResult.format_cls[self.format])\ if isinstance(value, self.format_cls[self.format])\
and value.dtype == self.dtype: and value.dtype == self.dtype:
return value return value
#print 'pass-through failed', type(value) if strict:
sp = SparseResult.format_cls[self.format](value) raise TypeError("%s is not sparse" % value)
sp = self.format_cls[self.format](value)
if str(sp.dtype) != self.dtype: if str(sp.dtype) != self.dtype:
raise NotImplementedError() raise NotImplementedError()
if sp.format != self.format: if sp.format != self.format:
raise NotImplementedError() raise NotImplementedError()
return sp return sp
def __copy__(self): def make_result(self, name = None):
if self.name is not None: return SparseResult(self, name = name)
rval = SparseResult(self._dtype, self._format, name=self.name)
else:
rval = SparseResult(self._dtype, self._format)
rval.data = copy.copy(self.data)
return rval
def __eq__(self, other):
return type(self) == type(other) and other.dtype == self.dtype and other.format == self.format
dtype = property(lambda self: self._dtype) def __hash__(self):
format = property(lambda self: self._format) return hash(self.dtype) ^ hash(self.format)
T = property(lambda self: transpose(self), doc = "Return aliased transpose of self (read-only)")
def __str__(self):
return "Sparse[%s, %s]" % (str(self.dtype), str(self.format))
def __repr__(self):
return "Sparse[%s, %s]" % (str(self.dtype), str(self.format))
class _sparse_py_operators:
T = property(lambda self: transpose(self), doc = "Return aliased transpose of self (read-only)")
def __add__(left, right): return add(left, right) def __add__(left, right): return add(left, right)
def __radd__(right, left): return add(left, right) def __radd__(right, left): return add(left, right)
class SparseResult(gof.Result, _sparse_py_operators):
pass
class SparseConstant(gof.Constant, _sparse_py_operators):
pass
class SparseValue(gof.Value, _sparse_py_operators):
pass
# #
# Conversion # Conversion
# #
# convert a sparse matrix to an ndarray # convert a sparse matrix to an ndarray
class DenseFromSparse(gof.op.Op): class DenseFromSparse(gof.op.Op):
def __init__(self, x, **kwargs): def make_node(self, x):
gof.op.Op.__init__(self, **kwargs) x = as_sparse(x)
self.inputs = [assparse(x)] return gof.Apply(self,
self.outputs = [tensor.Tensor(x.dtype,[0,0])] [x],
def impl(self, x): [tensor.Tensor(dtype = x.type.dtype,
assert _is_sparse(x) broadcastable = (False, False)).make_result()])
return numpy.asarray(x.todense()) def perform(self, node, (x, ), (out, )):
def grad(self, (x,), (gz,)): out[0] = numpy.asarray(x.todense())
assert _is_sparse_result(x) and _is_dense_result(gz) def grad(self, (x, ), (gz, )):
return sparse_from_dense(gz, x.format), return SparseFromDense(x.type.format)(gz),
dense_from_sparse = gof.op.constructor(DenseFromSparse) dense_from_sparse = DenseFromSparse()
class SparseFromDense(gof.op.Op): class SparseFromDense(gof.op.Op):
def __init__(self, x, format, **kwargs): def __init__(self, format):
gof.op.Op.__init__(self, **kwargs) self.format = format
if isinstance(format, gof.result.Result): def make_node(self, x):
self.inputs = [tensor.astensor(x), format] x = tensor.as_tensor(x)
else: return gof.Apply(self,
self.inputs = [tensor.astensor(x), gof.result.PythonResult()] [x],
self.inputs[1].data = format [Sparse(dtype = x.type.dtype,
self.outputs = [SparseResult(x.dtype, self.inputs[1].data)] format = self.format).make_result()])
def impl(self, x, fmt): def perform(self, node, (x, ), (out, )):
# this would actually happen anyway when we try to assign to out[0] = Sparse.format_cls[self.format](x)
# self.outputs[0].data, but that seems hackish -JB def grad(self, (x, ), (gz, )):
assert _is_dense(x) return dense_from_sparse(gz),
return SparseResult.format_cls[fmt](x) def __eq__(self, other):
def grad(self, (x, fmt), (gz,)): return type(self) == type(other) and self.format == other.format
assert _is_dense_result(x) and _is_sparse_result(gz) def __hash__(self):
return dense_from_sparse(gz), None return hash(self.format)
sparse_from_dense = gof.op.constructor(SparseFromDense) csr_from_dense = SparseFromDense('csr')
csc_from_dense = SparseFromDense('csc')
# Linear Algebra # Linear Algebra
class Transpose(gof.op.Op): class Transpose(gof.op.Op):
format_map = { format_map = {'csr' : 'csc',
'csr' : 'csc',
'csc' : 'csr'} 'csc' : 'csr'}
def __init__(self, x, **kwargs): def make_node(self, x):
gof.op.Op.__init__(self, **kwargs) x = as_sparse(x)
x = assparse(x) return gof.Apply(self,
self.inputs = [x] [x],
self.outputs = [SparseResult(x.dtype, Transpose.format_map[x.format])] [Sparse(dtype = x.type.dtype,
def impl(self, x): format = self.format_map[x.type.format]).make_result()])
def perform(self, node, (x, ), (out, )):
assert _is_sparse(x) assert _is_sparse(x)
return x.transpose() out[0] = x.transpose()
def grad(self, (x,), (gz,)): def grad(self, (x,), (gz,)):
assert _is_sparse_result(x) and _is_sparse_result(gz) assert _is_sparse_result(x) and _is_sparse_result(gz)
return transpose(gz), return transpose(gz),
transpose = gof.op.constructor(Transpose) transpose = Transpose()
class AddSS(gof.op.Op): class AddSS(gof.op.Op):
''' Add two sparse matrices ''' ''' Add two sparse matrices '''
def __init__(self, x, y, **kwargs): def make_node(self, x, y):
gof.op.Op.__init__(self, **kwargs) x, y = map(as_sparse, [x, y])
x, y = [assparse(x), assparse(y)] if x.type.dtype != y.type.dtype:
self.inputs = [x, y]
if x.dtype != y.dtype:
raise NotImplementedError() raise NotImplementedError()
if x.format != y.format: if x.type.format != y.type.format:
raise NotImplementedError() raise NotImplementedError()
self.outputs = [SparseResult(x.dtype, x.format)] return gof.Apply(self,
def impl(self, x,y): [x, y],
[Sparse(dtype = x.type.dtype,
format = x.type.format).make_result()])
def perform(self, node, (x, y), (out, )):
assert _is_sparse(x) and _is_sparse(y) assert _is_sparse(x) and _is_sparse(y)
return x + y out[0] = x + y
def grad(self, (x, y), (gz,)): def grad(self, (x, y), (gz,)):
assert _is_sparse_result(x) and _is_sparse_result(y) assert _is_sparse_result(x) and _is_sparse_result(y)
assert _is_sparse_result(gz) assert _is_sparse_result(gz)
return gz, gz return gz, gz
add_s_s = gof.op.constructor(AddSS) add_s_s = AddSS()
class AddSD(gof.op.Op): class AddSD(gof.op.Op):
''' Add a sparse and a dense matrix ''' ''' Add a sparse and a dense matrix '''
def __init__(self, x, y, **kwargs): def make_node(self, x, y):
gof.op.Op.__init__(self, **kwargs) x, y = as_sparse(x), tensor.as_tensor(y)
x, y = [assparse(x), tensor.astensor(y)] if x.type.dtype != y.type.dtype:
self.inputs = [x, y]
if x.dtype != y.dtype:
raise NotImplementedError() raise NotImplementedError()
# The magic number two here arises because L{scipy.sparse} # The magic number two here arises because L{scipy.sparse}
# objects must be matrices (have dimension 2) # objects must be matrices (have dimension 2)
assert len(y.broadcastable) == 2 assert y.type.ndim == 2
self.outputs = [tensor.Tensor(y.dtype, y.broadcastable)] return gof.Apply(self,
def impl(self, x,y): [x, y],
[tensor.Tensor(dtype = y.type.dtype,
broadcastable = y.type.broadcastable).make_result()])
def perform(self, node, (x, y), (out, )):
assert _is_sparse(x) and _is_dense(y) assert _is_sparse(x) and _is_dense(y)
return x + y out[0] = x + y
def grad(self, (x, y), (gz,)): def grad(self, (x, y), (gz,)):
assert _is_sparse_result(x) and _is_dense_result(y) assert _is_sparse_result(x) and _is_dense_result(y)
assert _is_dense_result(gz) assert _is_dense_result(gz)
return SparseFromDense(gz), gz return SparseFromDense(x.type.format)(gz), gz
add_s_d = gof.op.constructor(AddSD) add_s_d = AddSD()
def add(x,y): def add(x,y):
""" """
Add two matrices, at least one of which is sparse. Add two matrices, at least one of which is sparse.
""" """
if hasattr(x, 'getnnz'): x = assparse(x) if hasattr(x, 'getnnz'): x = as_sparse(x)
if hasattr(y, 'getnnz'): y = assparse(y) if hasattr(y, 'getnnz'): y = as_sparse(y)
x_is_sparse_result = _is_sparse_result(x) x_is_sparse_result = _is_sparse_result(x)
y_is_sparse_result = _is_sparse_result(y) y_is_sparse_result = _is_sparse_result(y)
...@@ -266,57 +311,425 @@ class Dot(gof.op.Op): ...@@ -266,57 +311,425 @@ class Dot(gof.op.Op):
@todo: Simplify code by splitting into DotSS and DotSD. @todo: Simplify code by splitting into DotSS and DotSD.
""" """
def __init__(self, x, y, grad_preserves_dense=True): def __init__(self, grad_preserves_dense=True):
self.grad_preserves_dense = grad_preserves_dense
def make_node(self, x, y):
""" """
Because of trickiness of implementing, we assume that the left argument x is SparseResult (not dense) Because of trickiness of implementing, we assume that the left argument x is SparseResult (not dense)
""" """
if x.dtype != y.dtype: if x.type.dtype != y.type.dtype:
raise NotImplementedError() raise NotImplementedError()
assert _is_sparse_result(x) assert _is_sparse_result(x)
# These are the conversions performed by scipy.sparse.dot # These are the conversions performed by scipy.sparse.dot
if x.format == "csc" or x.format == "coo": if x.type.format == "csc" or x.type.format == "coo":
myformat = "csc" myformat = "csc"
elif x.format == "csr": elif x.type.format == "csr":
myformat = "csr" myformat = "csr"
else: else:
raise NotImplementedError() raise NotImplementedError()
self.inputs = [x, y] # Need to convert? e.g. assparse inputs = [x, y] # Need to convert? e.g. assparse
self.outputs = [SparseResult(x.dtype, myformat)] outputs = [Sparse(dtype = x.type.dtype, format = myformat).make_result()]
self.grad_preserves_dense = grad_preserves_dense return gof.Apply(self, inputs, outputs)
def perform(self): def perform(self, node, (x, y), (out, )):
""" """
@todo: Verify that output is sufficiently sparse, and raise a warning if it is not @todo: Verify that output is sufficiently sparse, and raise a warning if it is not
@todo: Also determine that we are storing the output in the best storage format? @todo: Also determine that we are storing the output in the best storage format?
""" """
self.outputs[0].data = self.inputs[0].data.dot(self.inputs[1].data) out[0] = x.dot(y)
def grad(self, (x, y), (gz,)): def grad(self, (x, y), (gz,)):
assert _is_sparse_result(gz) assert _is_sparse_result(gz)
rval = [dot(gz, y.T), dot(x.T, gz)]
assert _is_sparse_result(x) assert _is_sparse_result(x)
rval = [dot(gz, y.T), dot(x.T, gz)]
if _is_dense_result(y): if _is_dense_result(y):
if self.grad_preserves_dense: if self.grad_preserves_dense:
rval[1] = dense_from_sparse(rval[1]) rval[1] = dense_from_sparse(rval[1])
return rval return rval
def __copy__(self): def __eq__(self, other):
return self.__class__(self.inputs[0], self.inputs[1], self.grad_preserves_dense) return type(self) == type(other) and self.grad_preserves_dense == other.grad_preserves_dense
def clone_with_new_inputs(self, *new_inputs): def __hash__(self):
return self.__class__(new_inputs[0], new_inputs[1], self.grad_preserves_dense) return hash(self.grad_preserves_dense)
def dot(x, y, grad_preserves_dense=True): def dot(x, y, grad_preserves_dense=True):
""" """
@todo: Maybe the triple-transposition formulation (when x is dense) @todo: Maybe the triple-transposition formulation (when x is dense)
is slow. See if there is a direct way to do this. is slow. See if there is a direct way to do this.
""" """
if hasattr(x, 'getnnz'): x = assparse(x) if hasattr(x, 'getnnz'): x = as_sparse(x)
if hasattr(y, 'getnnz'): y = assparse(y) if hasattr(y, 'getnnz'): y = as_sparse(y)
x_is_sparse_result = _is_sparse_result(x) x_is_sparse_result = _is_sparse_result(x)
y_is_sparse_result = _is_sparse_result(y) y_is_sparse_result = _is_sparse_result(y)
if not x_is_sparse_result and not y_is_sparse_result: if not x_is_sparse_result and not y_is_sparse_result:
raise TypeError() raise TypeError()
if x_is_sparse_result: if x_is_sparse_result:
return Dot(x, y, grad_preserves_dense).outputs[0] return Dot(grad_preserves_dense)(x, y)
else: else:
assert y_is_sparse_result assert y_is_sparse_result
return transpose(Dot(y.T, x.T, grad_preserves_dense).outputs[0]) return transpose(Dot(grad_preserves_dense)(y.T, x.T))
# """
# Classes for handling sparse matrices.
# To read about different sparse formats, see U{http://www-users.cs.umn.edu/~saad/software/SPARSKIT/paper.ps}.
# @todo: Automatic methods for determining best sparse format?
# """
# import copy #for __copy__
# import numpy
# from scipy import sparse
# import gof.op, gof.result
# import tensor
# """ Types of sparse matrices to use for testing """
# _mtypes = [sparse.csc_matrix, sparse.csr_matrix]
# #_mtypes = [sparse.csc_matrix, sparse.csr_matrix, sparse.dok_matrix, sparse.lil_matrix, sparse.coo_matrix]
# _mtype_to_str = {sparse.csc_matrix: "csc", sparse.csr_matrix: "csr"}
# ## Type checking
# def _is_sparse_result(x):
# """
# @rtype: boolean
# @return: True iff x is a L{SparseResult} (and not a L{tensor.Tensor})
# """
# if not isinstance(x, SparseResult) and not isinstance(x, tensor.Tensor):
# raise NotImplementedError("_is_sparse should only be called on sparse.SparseResult or tensor.Tensor, not,", x)
# return isinstance(x, SparseResult)
# def _is_dense_result(x):
# """
# @rtype: boolean
# @return: True unless x is a L{SparseResult} (and not a L{tensor.Tensor})
# """
# if not isinstance(x, SparseResult) and not isinstance(x, tensor.Tensor):
# raise NotImplementedError("_is_sparse should only be called on sparse.SparseResult or tensor.Tensor, not,", x)
# return isinstance(x, tensor.Tensor)
# def _is_sparse(x):
# """
# @rtype: boolean
# @return: True iff x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray})
# """
# if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray):
# raise NotImplementedError("_is_sparse should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x)
# return isinstance(x, sparse.spmatrix)
# def _is_dense(x):
# """
# @rtype: boolean
# @return: True unless x is a L{scipy.sparse.spmatrix} (and not a L{numpy.ndarray})
# """
# if not isinstance(x, sparse.spmatrix) and not isinstance(x, numpy.ndarray):
# raise NotImplementedError("_is_sparse should only be called on sparse.scipy.sparse.spmatrix or numpy.ndarray, not,", x)
# return isinstance(x, numpy.ndarray)
# # Wrapper type
# def assparse(sp, **kwargs):
# """
# Wrapper around SparseResult constructor.
# @param sp: A sparse matrix. assparse reads dtype and format properties
# out of this sparse matrix.
# @return: SparseResult version of sp.
# @todo Verify that sp is sufficiently sparse, and raise a warning if it is not
# """
# if isinstance(sp, SparseResult):
# rval = sp
# else:
# # @todo Verify that sp is sufficiently sparse, and raise a
# # warning if it is not
# rval = SparseResult(str(sp.dtype), sp.format, **kwargs)
# rval.data = sp
# assert _is_sparse_result(rval)
# return rval
# class SparseResult(gof.result.Result):
# """
# @type _dtype: numpy dtype string such as 'int64' or 'float64' (among others)
# @type _format: string
# @ivar _format: The sparse storage strategy.
# @note As far as I can tell, L{scipy.sparse} objects must be matrices, i.e. have dimension 2.
# """
# format_cls = {
# 'csr' : sparse.csr_matrix,
# 'csc' : sparse.csc_matrix
# }
# dtype_set = set(['int', 'int32', 'int64', 'float32', 'float64'])
# def __init__(self, dtype, format, **kwargs):
# """
# Fundamental way to create a sparse node.
# @param dtype: Type of numbers in the matrix.
# @param format: The sparse storage strategy.
# @return An empty SparseResult instance.
# """
# gof.Result.__init__(self, **kwargs)
# if dtype in SparseResult.dtype_set:
# self._dtype = dtype
# assert isinstance(format, str)
# #print format, type(format), SparseResult.format_cls.keys(), format in SparseResult.format_cls
# if format in SparseResult.format_cls:
# self._format = format
# else:
# raise NotImplementedError('unsupported format "%s" not in list' % format, SparseResult.format_cls.keys())
# def filter(self, value):
# if isinstance(value, SparseResult.format_cls[self.format])\
# and value.dtype == self.dtype:
# return value
# #print 'pass-through failed', type(value)
# sp = SparseResult.format_cls[self.format](value)
# if str(sp.dtype) != self.dtype:
# raise NotImplementedError()
# if sp.format != self.format:
# raise NotImplementedError()
# return sp
# def __copy__(self):
# if self.name is not None:
# rval = SparseResult(self._dtype, self._format, name=self.name)
# else:
# rval = SparseResult(self._dtype, self._format)
# rval.data = copy.copy(self.data)
# return rval
# dtype = property(lambda self: self._dtype)
# format = property(lambda self: self._format)
# T = property(lambda self: transpose(self), doc = "Return aliased transpose of self (read-only)")
# def __add__(left, right): return add(left, right)
# def __radd__(right, left): return add(left, right)
# #
# # Conversion
# #
# # convert a sparse matrix to an ndarray
# class DenseFromSparse(gof.op.Op):
# def __init__(self, x, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# self.inputs = [assparse(x)]
# self.outputs = [tensor.Tensor(x.dtype,[0,0])]
# def impl(self, x):
# assert _is_sparse(x)
# return numpy.asarray(x.todense())
# def grad(self, (x,), (gz,)):
# assert _is_sparse_result(x) and _is_dense_result(gz)
# return sparse_from_dense(gz, x.format),
# dense_from_sparse = gof.op.constructor(DenseFromSparse)
# class SparseFromDense(gof.op.Op):
# def __init__(self, x, format, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# if isinstance(format, gof.result.Result):
# self.inputs = [tensor.astensor(x), format]
# else:
# self.inputs = [tensor.astensor(x), gof.result.PythonResult()]
# self.inputs[1].data = format
# self.outputs = [SparseResult(x.dtype, self.inputs[1].data)]
# def impl(self, x, fmt):
# # this would actually happen anyway when we try to assign to
# # self.outputs[0].data, but that seems hackish -JB
# assert _is_dense(x)
# return SparseResult.format_cls[fmt](x)
# def grad(self, (x, fmt), (gz,)):
# assert _is_dense_result(x) and _is_sparse_result(gz)
# return dense_from_sparse(gz), None
# sparse_from_dense = gof.op.constructor(SparseFromDense)
# # Linear Algebra
# class Transpose(gof.op.Op):
# format_map = {
# 'csr' : 'csc',
# 'csc' : 'csr'}
# def __init__(self, x, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# x = assparse(x)
# self.inputs = [x]
# self.outputs = [SparseResult(x.dtype, Transpose.format_map[x.format])]
# def impl(self, x):
# assert _is_sparse(x)
# return x.transpose()
# def grad(self, (x,), (gz,)):
# assert _is_sparse_result(x) and _is_sparse_result(gz)
# return transpose(gz),
# transpose = gof.op.constructor(Transpose)
# class AddSS(gof.op.Op):
# ''' Add two sparse matrices '''
# def __init__(self, x, y, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# x, y = [assparse(x), assparse(y)]
# self.inputs = [x, y]
# if x.dtype != y.dtype:
# raise NotImplementedError()
# if x.format != y.format:
# raise NotImplementedError()
# self.outputs = [SparseResult(x.dtype, x.format)]
# def impl(self, x,y):
# assert _is_sparse(x) and _is_sparse(y)
# return x + y
# def grad(self, (x, y), (gz,)):
# assert _is_sparse_result(x) and _is_sparse_result(y)
# assert _is_sparse_result(gz)
# return gz, gz
# add_s_s = gof.op.constructor(AddSS)
# class AddSD(gof.op.Op):
# ''' Add a sparse and a dense matrix '''
# def __init__(self, x, y, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# x, y = [assparse(x), tensor.astensor(y)]
# self.inputs = [x, y]
# if x.dtype != y.dtype:
# raise NotImplementedError()
# # The magic number two here arises because L{scipy.sparse}
# # objects must be matrices (have dimension 2)
# assert len(y.broadcastable) == 2
# self.outputs = [tensor.Tensor(y.dtype, y.broadcastable)]
# def impl(self, x,y):
# assert _is_sparse(x) and _is_dense(y)
# return x + y
# def grad(self, (x, y), (gz,)):
# assert _is_sparse_result(x) and _is_dense_result(y)
# assert _is_dense_result(gz)
# return SparseFromDense(gz), gz
# add_s_d = gof.op.constructor(AddSD)
# def add(x,y):
# """
# Add two matrices, at least one of which is sparse.
# """
# if hasattr(x, 'getnnz'): x = assparse(x)
# if hasattr(y, 'getnnz'): y = assparse(y)
# x_is_sparse_result = _is_sparse_result(x)
# y_is_sparse_result = _is_sparse_result(y)
# assert x_is_sparse_result or y_is_sparse_result
# if x_is_sparse_result and y_is_sparse_result: return add_s_s(x,y)
# elif x_is_sparse_result and not y_is_sparse_result: return add_s_d(x,y)
# elif y_is_sparse_result and not x_is_sparse_result: return add_s_d(y,x)
# else: raise NotImplementedError()
# class Dot(gof.op.Op):
# """
# Attributes:
# grad_preserves_dense - a boolean flags [default: True].
# grad_preserves_dense controls whether gradients with respect to inputs
# are converted to dense matrices when the corresponding input y is
# dense (not in a L{SparseResult} wrapper). This is generally a good idea
# when L{Dot} is in the middle of a larger graph, because the types
# of gy will match that of y. This conversion might be inefficient if
# the gradients are graph outputs though, hence this mask.
# @todo: Simplify code by splitting into DotSS and DotSD.
# """
# def __init__(self, x, y, grad_preserves_dense=True):
# """
# Because of trickiness of implementing, we assume that the left argument x is SparseResult (not dense)
# """
# if x.dtype != y.dtype:
# raise NotImplementedError()
# assert _is_sparse_result(x)
# # These are the conversions performed by scipy.sparse.dot
# if x.format == "csc" or x.format == "coo":
# myformat = "csc"
# elif x.format == "csr":
# myformat = "csr"
# else:
# raise NotImplementedError()
# self.inputs = [x, y] # Need to convert? e.g. assparse
# self.outputs = [SparseResult(x.dtype, myformat)]
# self.grad_preserves_dense = grad_preserves_dense
# def perform(self):
# """
# @todo: Verify that output is sufficiently sparse, and raise a warning if it is not
# @todo: Also determine that we are storing the output in the best storage format?
# """
# self.outputs[0].data = self.inputs[0].data.dot(self.inputs[1].data)
# def grad(self, (x, y), (gz,)):
# assert _is_sparse_result(gz)
# rval = [dot(gz, y.T), dot(x.T, gz)]
# assert _is_sparse_result(x)
# if _is_dense_result(y):
# if self.grad_preserves_dense:
# rval[1] = dense_from_sparse(rval[1])
# return rval
# def __copy__(self):
# return self.__class__(self.inputs[0], self.inputs[1], self.grad_preserves_dense)
# def clone_with_new_inputs(self, *new_inputs):
# return self.__class__(new_inputs[0], new_inputs[1], self.grad_preserves_dense)
# def dot(x, y, grad_preserves_dense=True):
# """
# @todo: Maybe the triple-transposition formulation (when x is dense)
# is slow. See if there is a direct way to do this.
# """
# if hasattr(x, 'getnnz'): x = assparse(x)
# if hasattr(y, 'getnnz'): y = assparse(y)
# x_is_sparse_result = _is_sparse_result(x)
# y_is_sparse_result = _is_sparse_result(y)
# if not x_is_sparse_result and not y_is_sparse_result:
# raise TypeError()
# if x_is_sparse_result:
# return Dot(x, y, grad_preserves_dense).outputs[0]
# else:
# assert y_is_sparse_result
# return transpose(Dot(y.T, x.T, grad_preserves_dense).outputs[0])
...@@ -6,7 +6,7 @@ import numpy ...@@ -6,7 +6,7 @@ import numpy
from copy import copy from copy import copy
from gof import Result, Op, utils, Destroyer, Viewer, AbstractFunctionError, Type, Result, Constant, Apply from gof import Result, Op, utils, Destroyer, Viewer, AbstractFunctionError, Type, Result, Constant, Apply, Value
import gof import gof
import blas # for gemm, dot import blas # for gemm, dot
...@@ -27,14 +27,9 @@ def as_tensor(x, name = None): ...@@ -27,14 +27,9 @@ def as_tensor(x, name = None):
if not isinstance(x.type, Tensor): if not isinstance(x.type, Tensor):
raise TypeError("Result type field must be a Tensor.", x, x.type) raise TypeError("Result type field must be a Tensor.", x, x.type)
return x return x
if isinstance(x, Constant):
if not isinstance(x.type, Tensor):
raise TypeError("Constant type field must be a Tensor.", x, x.type)
return x
try: try:
return constant(x) return constant(x)
except TypeError: except TypeError:
raise
raise TypeError("Cannot convert %s to Tensor" % x, type(x)) raise TypeError("Cannot convert %s to Tensor" % x, type(x))
# this has a different name, because _as_tensor is the function which ops use # this has a different name, because _as_tensor is the function which ops use
# to upcast their arguments... this internal-use function is a good place to put debugging stuff, better than the global astensor. # to upcast their arguments... this internal-use function is a good place to put debugging stuff, better than the global astensor.
...@@ -48,10 +43,19 @@ def constant(x): ...@@ -48,10 +43,19 @@ def constant(x):
return TensorConstant(Tensor(dtype = x.dtype, return TensorConstant(Tensor(dtype = x.dtype,
broadcastable = [d == 1 for d in x.shape]), x) broadcastable = [d == 1 for d in x.shape]), x)
except: except:
raise raise TypeError("Could not convert %s to Tensor" % _x, type(_x))
def value(x):
if not isinstance(x, numpy.ndarray):
x = numpy.asarray(x)
try:
return TensorValue(Tensor(dtype = x.dtype,
broadcastable = [d == 1 for d in x.shape]), x)
except:
raise TypeError("Could not convert %s to Tensor" % _x, type(_x)) raise TypeError("Could not convert %s to Tensor" % _x, type(_x))
class Tensor(Type): class Tensor(Type):
""" """
L{Type} representing L{numpy.ndarray} in Theano. L{Type} representing L{numpy.ndarray} in Theano.
...@@ -342,10 +346,14 @@ class TensorResult(Result, _tensor_py_operators): ...@@ -342,10 +346,14 @@ class TensorResult(Result, _tensor_py_operators):
class TensorConstant(Constant, _tensor_py_operators): class TensorConstant(Constant, _tensor_py_operators):
pass pass
class TensorValue(Value, _tensor_py_operators):
pass
s2t.as_tensor = as_tensor s2t.as_tensor = as_tensor
s2t.Tensor = Tensor s2t.Tensor = Tensor
s2t.TensorResult = TensorResult s2t.TensorResult = TensorResult
s2t.TensorConstant = TensorConstant s2t.TensorConstant = TensorConstant
s2t.TensorValue = TensorValue
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论