提交 33f46da2 authored 作者: Olivier Breuleux's avatar Olivier Breuleux

merge

...@@ -9,7 +9,10 @@ from gof import \ ...@@ -9,7 +9,10 @@ from gof import \
Type, Generic, generic, \ Type, Generic, generic, \
object2, utils object2, utils
from compile import FunctionMaker, function, OpFromGraph #, eval_outputs, fast_compute from compile import \
Mode, \
predefined_modes, predefined_linkers, predefined_optimizers, \
FunctionMaker, function, OpFromGraph #, eval_outputs, fast_compute
import tensor import tensor
import tensor_random import tensor_random
......
...@@ -146,41 +146,41 @@ import tensor as T ...@@ -146,41 +146,41 @@ import tensor as T
import random import random
import numpy as N import numpy as N
# class T_OpFromGraph(unittest.TestCase): class T_OpFromGraph(unittest.TestCase):
# def test_straightforward(self): def test_straightforward(self):
# x, y, z = T.matrices('xyz') x, y, z = T.matrices('xyz')
# e = x + y * z e = x + y * z
# op = OpFromGraph([x, y, z], [e], linker='c|py') op = OpFromGraph([x, y, z], [e], mode='FAST_RUN')
# f = op(x, y, z) - op(y, z, x) f = op(x, y, z) - op(y, z, x)
# fn = function([x, y, z], [f]) fn = function([x, y, z], f)
# xv, yv, zv = N.ones((2, 2)), N.ones((2, 2))*3, N.ones((2, 2))*5 xv, yv, zv = N.ones((2, 2)), N.ones((2, 2))*3, N.ones((2, 2))*5
# assert numpy.all(8.0 == fn(xv, yv, zv)) assert numpy.all(8.0 == fn(xv, yv, zv))
# assert numpy.all(8.0 == fn(xv, yv, zv)) assert numpy.all(8.0 == fn(xv, yv, zv))
# def test_size_changes(self): def test_size_changes(self):
# x, y, z = T.matrices('xyz') x, y, z = T.matrices('xyz')
# e = T.dot(x, y) e = T.dot(x, y)
# op = OpFromGraph([x, y], [e], linker='c|py') op = OpFromGraph([x, y], [e], mode='FAST_RUN')
# f = op(x, op(y, z)) f = op(x, op(y, z))
# fn = function([x, y, z], [f]) fn = function([x, y, z], f)
# xv, yv, zv = N.ones((2, 3)), N.ones((3, 4))*3, N.ones((4, 5))*5 xv, yv, zv = N.ones((2, 3)), N.ones((3, 4))*3, N.ones((4, 5))*5
# res = fn(xv, yv, zv) res = fn(xv, yv, zv)
# assert res.shape == (2, 5) assert res.shape == (2, 5)
# assert numpy.all(180.0 == res) assert numpy.all(180.0 == res)
# res = fn(xv, yv, zv) res = fn(xv, yv, zv)
# assert res.shape == (2, 5) assert res.shape == (2, 5)
# assert numpy.all(180.0 == res) assert numpy.all(180.0 == res)
# def test_grad(self): def test_grad(self):
# x, y, z = T.matrices('xyz') x, y, z = T.matrices('xyz')
# e = x + y * z e = x + y * z
# op = OpFromGraph([x, y, z], [e], linker='c|py', grad_depth = 2) op = OpFromGraph([x, y, z], [e], mode='FAST_RUN', grad_depth = 2)
# f = op(x, y, z) f = op(x, y, z)
# f = f - T.grad(f, y) f = f - T.grad(f, y)
# fn = function([x, y, z], [f]) fn = function([x, y, z], f)
# xv, yv, zv = N.ones((2, 2)), N.ones((2, 2))*3, N.ones((2, 2))*5 xv, yv, zv = N.ones((2, 2)), N.ones((2, 2))*3, N.ones((2, 2))*5
# assert numpy.all(11.0 == fn(xv, yv, zv)) assert numpy.all(11.0 == fn(xv, yv, zv))
class T_function(unittest.TestCase): class T_function(unittest.TestCase):
...@@ -303,24 +303,19 @@ class T_function(unittest.TestCase): ...@@ -303,24 +303,19 @@ class T_function(unittest.TestCase):
f = function([x, In(a, value=1.0,name='a'), In(s, value=0.0, update=s+a*x)], s+a*x) f = function([x, In(a, value=1.0,name='a'), In(s, value=0.0, update=s+a*x)], s+a*x)
self.failUnless(f.a == 1.0) self.failUnless(f[a] == 1.0)
self.failUnless(f.value[a] is f.a) self.failUnless(f[s] == 0.0)
self.failUnless(f.s == 0.0)
self.failUnless(f.value[s] is f.s)
self.failUnless(f(3.0) == 3.0) self.failUnless(f(3.0) == 3.0)
self.failUnless(f(3.0,a=2.0) == 9.0) #3.0 + 2*3.0 self.failUnless(f(3.0,a=2.0) == 9.0) #3.0 + 2*3.0
self.failUnless(f.a == 1.0) #state hasn't changed permanently, we just overrode it last line self.failUnless(f[a] == 1.0) #state hasn't changed permanently, we just overrode it last line
self.failUnless(f.s == 9.0) self.failUnless(f[s] == 9.0)
f.a = 5.0 f[a] = 5.0
self.failUnless(f.a == 5.0) self.failUnless(f[a] == 5.0)
self.failUnless(f.value[a] is f.a)
self.failUnless(f(3.0) == 24.0) #9 + 3*5 self.failUnless(f(3.0) == 24.0) #9 + 3*5
self.failUnless(f.s == 24.0) self.failUnless(f[s] == 24.0)
self.failUnless(f.value[s] is f.s)
def test_same_names(self): def test_same_names(self):
a,x,s = T.scalars('xxx') a,x,s = T.scalars('xxx')
...@@ -370,112 +365,112 @@ class T_function(unittest.TestCase): ...@@ -370,112 +365,112 @@ class T_function(unittest.TestCase):
g = function([x, In(a, value=1.0,name='a'), In(s, value=f.container[s], update=s-a*x, mutable=True)], s+a*x) g = function([x, In(a, value=1.0,name='a'), In(s, value=f.container[s], update=s-a*x, mutable=True)], s+a*x)
f(1, 2) f(1, 2)
self.failUnless(f.s == 2) self.failUnless(f[s] == 2)
self.failUnless(g.s == 2) self.failUnless(g[s] == 2)
g(1, 2) g(1, 2)
self.failUnless(f.s == 0) self.failUnless(f[s] == 0)
self.failUnless(g.s == 0) self.failUnless(g[s] == 0)
class T_function_examples(unittest.TestCase): # class T_function_examples(unittest.TestCase):
def test_accumulator(self): # def test_accumulator(self):
"""Test low-level interface with state.""" # """Test low-level interface with state."""
x = T.scalar('x') # x = T.scalar('x')
s = T.scalar('s') # s = T.scalar('s')
fn, states = program_states(inputs = [x], outputs = [], states = [(s, 0, s+x)]) # fn, states = program_states(inputs = [x], outputs = [], states = [(s, 0, s+x)])
sum = 0 # sum = 0
for inc in [1, 4, 5,23, -324]: # for inc in [1, 4, 5,23, -324]:
sum += inc # sum += inc
fn.run([inc], states) # fn.run([inc], states)
assert sum == states[0].value # assert sum == states[0].value
def test_misc0(self): # def test_misc0(self):
fn_inc, states_inc = function_states(\ # fn_inc, states_inc = function_states(\
inputs = [x], outputs = [], states = [(s, 0, s+x)]) # inputs = [x], outputs = [], states = [(s, 0, s+x)])
fn_inc2, states_inc2 = function_states(\ # fn_inc2, states_inc2 = function_states(\
inputs = [x], outputs = [], states = [(s, 0, s+x)]) # inputs = [x], outputs = [], states = [(s, 0, s+x)])
fn_inc_copy = copy.copy(fn_inc) #USE fn copy # fn_inc_copy = copy.copy(fn_inc) #USE fn copy
# run() is like __call__, but requires an explicit state argument # # run() is like __call__, but requires an explicit state argument
fn_inc.run([5], states_inc) #run on own state object # fn_inc.run([5], states_inc) #run on own state object
fn_inc2.run([3], states_inc) #run on compatible state object # fn_inc2.run([3], states_inc) #run on compatible state object
assert states_inc[0].value == 8 # assert states_inc[0].value == 8
states_inc_copy = copy.copy(states_inc) #USE state copy # states_inc_copy = copy.copy(states_inc) #USE state copy
fn_inc_copy.run([2], states_inc_copy) # fn_inc_copy.run([2], states_inc_copy)
assert states_inc[0].value == 10 #compatible # assert states_inc[0].value == 10 #compatible
fn_dec, states_dec = function_states(\ # fn_dec, states_dec = function_states(\
inputs = [x], outputs = [], states = [((s, s-x), states_inc[0])]) # inputs = [x], outputs = [], states = [((s, s-x), states_inc[0])])
try: # try:
fn_inc.run([5], states_dec) # wrong kind of state for given program # fn_inc.run([5], states_dec) # wrong kind of state for given program
self.fail("fn accepted an invalid state argument") # self.fail("fn accepted an invalid state argument")
except SpecificException: # except SpecificException:
raise NotImplementedError() #TODO # raise NotImplementedError() #TODO
except Exception: # except Exception:
self.fail("fn accepted an invalid state argument") # self.fail("fn accepted an invalid state argument")
def test_perceptron(self): # def test_perceptron(self):
"""Test high-level state interface.""" # """Test high-level state interface."""
mu0 = numpy.array([1.0,0.0]) # mu0 = numpy.array([1.0,0.0])
mu1 = numpy.array([0.0,0.1]) # mu1 = numpy.array([0.0,0.1])
si0 = numpy.ones_like(mu0) #unit variance # si0 = numpy.ones_like(mu0) #unit variance
si1 = numpy.ones_like(mu1) #unit variance # si1 = numpy.ones_like(mu1) #unit variance
#implicit internal state # #implicit internal state
r_state = random.random_state() # r_state = random.random_state()
label = r_state.bernoulli(0.5) # label = r_state.bernoulli(0.5)
#implicit internal state for each DiagGaussian # #implicit internal state for each DiagGaussian
x = label * DiagGaussian(mu0, si0, state=r_state) \ # x = label * DiagGaussian(mu0, si0, state=r_state) \
+ (1 - label) * random.DiagGaussian(mu1, si1, state=r_state) # + (1 - label) * random.DiagGaussian(mu1, si1, state=r_state)
w = T.tensor.dvector() # w = T.tensor.dvector()
b = T.tensor.dscalar() # b = T.tensor.dscalar()
lr = 0.01 # lr = 0.01
decision = dot(x,w) + b > 0 # decision = dot(x,w) + b > 0
new_w = w + neq(label, decision) * lr * x # new_w = w + neq(label, decision) * lr * x
new_b = b + neq(label, decision) * (label * (-lr) + (1-label)*lr) # new_b = b + neq(label, decision) * (label * (-lr) + (1-label)*lr)
init_w = numpy.array([0.0, 0.0]) # init_w = numpy.array([0.0, 0.0])
init_b = 0.0 # init_b = 0.0
io_stream = T.function([], [label, x], state={'seed':(r_state, 42)}) # io_stream = T.function([], [label, x], state={'seed':(r_state, 42)})
perceptron_learn = T.function([x, label], [decision], # perceptron_learn = T.function([x, label], [decision],
state={ # state={
'w':((w, update_w), init_w), # 'w':((w, update_w), init_w),
'b':((b, update_b), init_b), # 'b':((b, update_b), init_b),
'lr':(lr, 0.01)}) # 'lr':(lr, 0.01)})
perceptron_use = T.function([x], [decision], # perceptron_use = T.function([x], [decision],
state={ # state={
'w':(w, perceptron_learn.shared['w']), # 'w':(w, perceptron_learn.shared['w']),
'b':(b, perceptron_learn.shared['b'])}) # 'b':(b, perceptron_learn.shared['b'])})
errs = 0 # errs = 0
for i in xrange(100): # for i in xrange(100):
il, ix = io_stream() # il, ix = io_stream()
d0 = perceptron_use(ix) # d0 = perceptron_use(ix)
d1 = perceptron_learn(ix, il) # d1 = perceptron_learn(ix, il)
assert d0 == d1 # assert d0 == d1
errs += (d0 != d1) # errs += (d0 != d1)
print d0 # print d0
print 'errs =', errs # print 'errs =', errs
# class T_dict_interface(unittest.TestCase): # class T_dict_interface(unittest.TestCase):
...@@ -540,7 +535,7 @@ class T_function_examples(unittest.TestCase): ...@@ -540,7 +535,7 @@ class T_function_examples(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
if 0: if 1:
unittest.main() unittest.main()
else: else:
testcases = [] testcases = []
......
...@@ -8,6 +8,10 @@ from sparse import _is_dense, _is_sparse, _is_dense_result, _is_sparse_result ...@@ -8,6 +8,10 @@ from sparse import _is_dense, _is_sparse, _is_dense_result, _is_sparse_result
from sparse import _mtypes, _mtype_to_str from sparse import _mtypes, _mtype_to_str
import random import random
import gof
def eval_outputs(outputs):
return compile.function([], outputs)()[0]
class T_transpose(unittest.TestCase): class T_transpose(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -23,7 +27,7 @@ class T_transpose(unittest.TestCase): ...@@ -23,7 +27,7 @@ class T_transpose(unittest.TestCase):
self.failUnless(ta.type.dtype == 'float64', ta.type.dtype) self.failUnless(ta.type.dtype == 'float64', ta.type.dtype)
self.failUnless(ta.type.format == 'csr', ta.type.format) self.failUnless(ta.type.format == 'csr', ta.type.format)
vta = compile.eval_outputs([ta]) vta = 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 = as_sparse(sparse.csr_matrix(sparse.speye(5,3))) a = as_sparse(sparse.csr_matrix(sparse.speye(5,3)))
...@@ -34,7 +38,7 @@ class T_transpose(unittest.TestCase): ...@@ -34,7 +38,7 @@ class T_transpose(unittest.TestCase):
self.failUnless(ta.type.dtype == 'float64', ta.type.dtype) self.failUnless(ta.type.dtype == 'float64', ta.type.dtype)
self.failUnless(ta.type.format == 'csc', ta.type.format) self.failUnless(ta.type.format == 'csc', ta.type.format)
vta = compile.eval_outputs([ta]) vta = eval_outputs([ta])
self.failUnless(vta.shape == (3,5)) self.failUnless(vta.shape == (3,5))
class T_Add(unittest.TestCase): class T_Add(unittest.TestCase):
...@@ -60,7 +64,7 @@ class T_Add(unittest.TestCase): ...@@ -60,7 +64,7 @@ class T_Add(unittest.TestCase):
self.failUnless(apb.type.format == aR.type.format, apb.type.format) self.failUnless(apb.type.format == aR.type.format, apb.type.format)
self.failUnless(apb.type.format == bR.type.format, apb.type.format) self.failUnless(apb.type.format == bR.type.format, apb.type.format)
val = compile.eval_outputs([apb]) val = eval_outputs([apb])
self.failUnless(val.shape == (3,2)) self.failUnless(val.shape == (3,2))
self.failUnless(numpy.all(val.todense() == (a + b).todense())) self.failUnless(numpy.all(val.todense() == (a + b).todense()))
self.failUnless(numpy.all(val.todense() == numpy.array([[1., 2], [3, 4], [5, 6]]))) self.failUnless(numpy.all(val.todense() == numpy.array([[1., 2], [3, 4], [5, 6]])))
...@@ -85,7 +89,7 @@ class T_Add(unittest.TestCase): ...@@ -85,7 +89,7 @@ class T_Add(unittest.TestCase):
self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype) self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype)
self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype) self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype)
val = compile.eval_outputs([apb]) val = eval_outputs([apb])
self.failUnless(val.shape == (3, 2)) self.failUnless(val.shape == (3, 2))
self.failUnless(numpy.all(val == (a + b))) self.failUnless(numpy.all(val == (a + b)))
self.failUnless(numpy.all(val == numpy.array([[1., 2], [3, 4], [5, 6]]))) self.failUnless(numpy.all(val == numpy.array([[1., 2], [3, 4], [5, 6]])))
...@@ -110,7 +114,7 @@ class T_Add(unittest.TestCase): ...@@ -110,7 +114,7 @@ class T_Add(unittest.TestCase):
self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype) self.failUnless(apb.type.dtype == aR.type.dtype, apb.type.dtype)
self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype) self.failUnless(apb.type.dtype == bR.type.dtype, apb.type.dtype)
val = compile.eval_outputs([apb]) val = eval_outputs([apb])
self.failUnless(val.shape == (3, 2)) self.failUnless(val.shape == (3, 2))
self.failUnless(numpy.all(val == (a + b))) self.failUnless(numpy.all(val == (a + b)))
self.failUnless(numpy.all(val == numpy.array([[1., 2], [3, 4], [5, 6]]))) self.failUnless(numpy.all(val == numpy.array([[1., 2], [3, 4], [5, 6]])))
...@@ -122,14 +126,14 @@ class T_conversion(unittest.TestCase): ...@@ -122,14 +126,14 @@ class T_conversion(unittest.TestCase):
def test0(self): def test0(self):
a = tensor.as_tensor(numpy.random.rand(5)) a = tensor.as_tensor(numpy.random.rand(5))
s = csc_from_dense(a) s = csc_from_dense(a)
val = compile.eval_outputs([s]) val = 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.as_tensor(numpy.random.rand(5)) a = tensor.as_tensor(numpy.random.rand(5))
s = csr_from_dense(a) s = csr_from_dense(a)
val = compile.eval_outputs([s]) val = 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')
...@@ -138,7 +142,7 @@ class T_conversion(unittest.TestCase): ...@@ -138,7 +142,7 @@ class T_conversion(unittest.TestCase):
s = t((2,5)) s = t((2,5))
d = dense_from_sparse(s) d = dense_from_sparse(s)
s[0,0] = 1.0 s[0,0] = 1.0
val = compile.eval_outputs([d]) val = eval_outputs([d])
self.failUnless(str(val.dtype)=='float64') self.failUnless(str(val.dtype)=='float64')
self.failUnless(numpy.all(val[0] == [1,0,0,0,0])) self.failUnless(numpy.all(val[0] == [1,0,0,0,0]))
...@@ -159,7 +163,7 @@ class _testCase_dot(unittest.TestCase): ...@@ -159,7 +163,7 @@ class _testCase_dot(unittest.TestCase):
zop = dot(x,xT) zop = dot(x,xT)
self.failUnless(_is_sparse_result(zop)) self.failUnless(_is_sparse_result(zop))
z = compile.eval_outputs([zop]) z = eval_outputs([zop])
self.failUnless(_is_sparse(z)) self.failUnless(_is_sparse(z))
self.failUnless(z.shape == (500,500)) self.failUnless(z.shape == (500,500))
self.failUnless(type(z) is mtype) self.failUnless(type(z) is mtype)
...@@ -190,7 +194,7 @@ class _testCase_dot(unittest.TestCase): ...@@ -190,7 +194,7 @@ class _testCase_dot(unittest.TestCase):
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 = 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) self.failUnless(type(z) is mtype)
...@@ -227,7 +231,7 @@ class _testCase_dot(unittest.TestCase): ...@@ -227,7 +231,7 @@ class _testCase_dot(unittest.TestCase):
# zop = dot(y, x) # zop = dot(y, x)
zop = transpose(dot(y, x)) zop = transpose(dot(y, x))
self.failUnless(_is_sparse_result(zop)) self.failUnless(_is_sparse_result(zop))
z = compile.eval_outputs([zop]) z = 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) # self.failUnless(type(z) is mtype)
......
...@@ -6,7 +6,7 @@ import tensor # for hidden symbols ...@@ -6,7 +6,7 @@ import tensor # for hidden symbols
import unittest import unittest
from copy import copy from copy import copy
from compile import function, FunctionFactory, eval_outputs import compile
import gradient import gradient
import gof, gof.graph import gof, gof.graph
from gof.python25 import any from gof.python25 import any
...@@ -15,6 +15,21 @@ from gof.utils import AbstractFunctionError ...@@ -15,6 +15,21 @@ from gof.utils import AbstractFunctionError
from elemwise import DimShuffle from elemwise import DimShuffle
default_mode = compile.Mode(optimizer = None,
linker = 'c&py')
def function(inputs, outputs, mode = default_mode):
return compile.function(inputs, outputs, mode = mode, accept_inplace = True)
def eval_outputs(outputs, mode = default_mode):
results = function([], outputs, mode = mode)()
if len(results) == 1:
return results[0]
return results
def _numpy_checker(x, y): def _numpy_checker(x, y):
""" """
Checks if x.data and y.data have the same contents. Checks if x.data and y.data have the same contents.
...@@ -56,9 +71,8 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -56,9 +71,8 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
try: try:
f = function(inputrs, node.outputs, f = function(inputrs, node.outputs,
linker = 'c&py', ##lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs), mode = default_mode, ##lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs),
unpack_single = False, )
optimizer = None)
except: except:
type, exc_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" \
...@@ -115,9 +129,8 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_ ...@@ -115,9 +129,8 @@ def make_tester(name, op, expected, checks = {}, good = {}, bad_build = {}, bad_
try: try:
f = function(inputrs, node.outputs, f = function(inputrs, node.outputs,
linker = 'c&py', #lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs), mode = default_mode, #lambda env, **kwargs: gof.DualLinker(env, checker = _numpy_checker, **kwargs),
unpack_single = False, )
optimizer = None)
except: except:
type, exc_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" \
...@@ -530,14 +543,14 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to ...@@ -530,14 +543,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, linker=linker) o_fn = function(tensor_pt, o_outputs[0], mode=compile.Mode(optimizer = None, linker = linker))
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], linker=linker) cost_fn = function(tensor_pt, cost, mode=compile.Mode(optimizer = None, linker = linker))
num_grad = gradient.numeric_grad(cost_fn, pt) num_grad = gradient.numeric_grad(cost_fn, pt)
...@@ -549,7 +562,7 @@ def verify_grad(testcase, op, pt, n_tests=1, rng=numpy.random, eps=0.0000001, to ...@@ -549,7 +562,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,linker=linker) grad_fn = function(tensor_pt, symbolic_grad, mode=compile.Mode(optimizer = None, linker = linker))
analytic_grad = grad_fn(*pt) analytic_grad = grad_fn(*pt)
if not isinstance(analytic_grad, (list, tuple)): if not isinstance(analytic_grad, (list, tuple)):
...@@ -584,24 +597,24 @@ def _approx_eq(a,b,eps=1.0e-9): ...@@ -584,24 +597,24 @@ def _approx_eq(a,b,eps=1.0e-9):
return True return True
_approx_eq.debug = 0 _approx_eq.debug = 0
def check_eq(self, node_in, node_out, arg_in, arg_out): # def check_eq(self, node_in, node_out, arg_in, arg_out):
fn = Function([node_in], [node_out]) # fn = Function([node_in], node_out)
self.failUnless( numpy.all(fn(arg_in) == arg_out), (arg_in, arg_out)) # self.failUnless( numpy.all(fn(arg_in) == arg_out), (arg_in, arg_out))
def check_eq2(self, inputs, output, args_in, arg_out): # def check_eq2(self, inputs, output, args_in, arg_out):
fn = Function(inputs, [output]) # fn = Function(inputs, output)
val = fn(*args_in) # val = fn(*args_in)
self.failUnless( numpy.all(val == arg_out), (val, arg_out)) # self.failUnless( numpy.all(val == arg_out), (val, arg_out))
def check_eq2_c(self, inputs, output, args_in, arg_out): # def check_eq2_c(self, inputs, output, args_in, arg_out):
fn = Function(inputs, [output], linker_cls = gof.CLinker) # fn = Function(inputs, [output], linker_cls = gof.CLinker)
val = fn(*args_in) # val = fn(*args_in)
self.failUnless( numpy.all(val == arg_out), (val, arg_out)) # self.failUnless( numpy.all(val == arg_out), (val, arg_out))
def check_eq2_both(self, inputs, output, args_in, arg_out): # def check_eq2_both(self, inputs, output, args_in, arg_out):
fn = Function(inputs, [output], linker_cls = lambda env: gof.DualLinker(env, _numpy_checker)) # fn = Function(inputs, [output], linker_cls = lambda env: gof.DualLinker(env, _numpy_checker))
val = fn(*args_in) # val = fn(*args_in)
self.failUnless( numpy.all(val == arg_out), (val, arg_out)) # self.failUnless( numpy.all(val == arg_out), (val, arg_out))
class T_Shape(unittest.TestCase): class T_Shape(unittest.TestCase):
def test_basic0(self): def test_basic0(self):
...@@ -622,7 +635,7 @@ class T_Cast(unittest.TestCase): ...@@ -622,7 +635,7 @@ class T_Cast(unittest.TestCase):
[convert_to_int8, convert_to_int16, convert_to_int32, convert_to_int64, [convert_to_int8, convert_to_int16, convert_to_int32, convert_to_int64,
convert_to_float32, convert_to_float64]): convert_to_float32, convert_to_float64]):
y = converter(x) y = converter(x)
f = function([x], [y], strict = True, linker = 'c&py') f = function([x], y, strict = True, mode = default_mode)
a = numpy.arange(10, dtype = type1) a = numpy.arange(10, dtype = type1)
b = f(a) b = f(a)
self.failUnless(numpy.all(b == numpy.arange(10, dtype = type2))) self.failUnless(numpy.all(b == numpy.arange(10, dtype = type2)))
...@@ -690,7 +703,7 @@ class T_transpose(unittest.TestCase): ...@@ -690,7 +703,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)
...@@ -702,7 +715,7 @@ class T_transpose(unittest.TestCase): ...@@ -702,7 +715,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
...@@ -713,7 +726,7 @@ class T_transpose(unittest.TestCase): ...@@ -713,7 +726,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
...@@ -725,7 +738,7 @@ class T_transpose(unittest.TestCase): ...@@ -725,7 +738,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
...@@ -949,7 +962,7 @@ class T_Stack(unittest.TestCase): ...@@ -949,7 +962,7 @@ class T_Stack(unittest.TestCase):
class _test_comparison(unittest.TestCase): class _test_comparison(unittest.TestCase):
def test_gt(self): def test_gt(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [x > y]) fn = function([x,y], x > y)
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -957,7 +970,7 @@ class _test_comparison(unittest.TestCase): ...@@ -957,7 +970,7 @@ class _test_comparison(unittest.TestCase):
def test_lt(self): def test_lt(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [x < y]) fn = function([x,y], x < y)
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -965,7 +978,7 @@ class _test_comparison(unittest.TestCase): ...@@ -965,7 +978,7 @@ class _test_comparison(unittest.TestCase):
def test_le(self): def test_le(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [x <= y]) fn = function([x,y], x <= y)
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -973,7 +986,7 @@ class _test_comparison(unittest.TestCase): ...@@ -973,7 +986,7 @@ class _test_comparison(unittest.TestCase):
def test_ge(self): def test_ge(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [x >= y]) fn = function([x,y], x >= y)
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -981,7 +994,7 @@ class _test_comparison(unittest.TestCase): ...@@ -981,7 +994,7 @@ class _test_comparison(unittest.TestCase):
def test_eq(self): def test_eq(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [eq(x,y)]) fn = function([x,y], eq(x,y))
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -989,7 +1002,7 @@ class _test_comparison(unittest.TestCase): ...@@ -989,7 +1002,7 @@ class _test_comparison(unittest.TestCase):
def test_neq(self): def test_neq(self):
x, y = fvector(), fvector() x, y = fvector(), fvector()
fn = function([x,y], [neq(x, y)]) fn = function([x,y], neq(x, y))
l = numpy.asarray([0.,-1.,1.]) l = numpy.asarray([0.,-1.,1.])
r = numpy.asarray([0.,1.,-1.]) r = numpy.asarray([0.,1.,-1.])
v = fn(l, r) v = fn(l, r)
...@@ -998,7 +1011,7 @@ class _test_comparison(unittest.TestCase): ...@@ -998,7 +1011,7 @@ class _test_comparison(unittest.TestCase):
class _test_bitwise(unittest.TestCase): class _test_bitwise(unittest.TestCase):
def test_or(self): def test_or(self):
x, y = bvector(), bvector() x, y = bvector(), bvector()
fn = function([x,y], [x|y]) fn = function([x,y], x|y)
l = numpy.asarray([0,0,1,1], dtype = 'int8') l = numpy.asarray([0,0,1,1], dtype = 'int8')
r = numpy.asarray([0,1,0,1], dtype = 'int8') r = numpy.asarray([0,1,0,1], dtype = 'int8')
v = fn(l, r) v = fn(l, r)
...@@ -1006,10 +1019,10 @@ class _test_bitwise(unittest.TestCase): ...@@ -1006,10 +1019,10 @@ class _test_bitwise(unittest.TestCase):
def test_xor(self): def test_xor(self):
x, y = bvector(), bvector() x, y = bvector(), bvector()
fn = function([x,y], [x^y]) fn = function([x,y], x^y)
ix = x ix = x
ix ^= y ix ^= y
gn = function([x,y], [ix]) gn = function([x,y], ix)
l = numpy.asarray([0,0,1,1], dtype = 'int8') l = numpy.asarray([0,0,1,1], dtype = 'int8')
r = numpy.asarray([0,1,0,1], dtype = 'int8') r = numpy.asarray([0,1,0,1], dtype = 'int8')
v = fn(l, r) v = fn(l, r)
...@@ -1020,7 +1033,7 @@ class _test_bitwise(unittest.TestCase): ...@@ -1020,7 +1033,7 @@ class _test_bitwise(unittest.TestCase):
def test_and(self): def test_and(self):
x, y = bvector(), bvector() x, y = bvector(), bvector()
fn = function([x,y], [x&y]) fn = function([x,y], x&y)
l = numpy.asarray([0,0,1,1], dtype = 'int8') l = numpy.asarray([0,0,1,1], dtype = 'int8')
r = numpy.asarray([0,1,0,1], dtype = 'int8') r = numpy.asarray([0,1,0,1], dtype = 'int8')
v = fn(l, r) v = fn(l, r)
...@@ -1028,7 +1041,7 @@ class _test_bitwise(unittest.TestCase): ...@@ -1028,7 +1041,7 @@ class _test_bitwise(unittest.TestCase):
def test_inv(self): def test_inv(self):
x, y = bvector(), bvector() x, y = bvector(), bvector()
fn = function([x,y], [~x]) fn = function([x,y], ~x)
l = numpy.asarray([0,0,1,1], dtype = 'int8') l = numpy.asarray([0,0,1,1], dtype = 'int8')
r = numpy.asarray([0,1,0,1], dtype = 'int8') r = numpy.asarray([0,1,0,1], dtype = 'int8')
v = fn(l, r) v = fn(l, r)
...@@ -1047,7 +1060,7 @@ class T_add(unittest.TestCase): ...@@ -1047,7 +1060,7 @@ class T_add(unittest.TestCase):
("*", lambda x,y: x*y), ("*", lambda x,y: x*y),
("/", lambda x,y: x/y)) ("/", lambda x,y: x/y))
for s, fn in tests: for s, fn in tests:
f = function([a,b], [fn(a, b)], linker = 'c') f = function([a,b], fn(a, b), mode = compile.Mode(optimizer = None, linker = 'c'))
self.failUnless(numpy.all(fn(a.data, b.data) == f(a.data, b.data))) self.failUnless(numpy.all(fn(a.data, b.data) == f(a.data, b.data)))
def test_grad_scalar_l(self): def test_grad_scalar_l(self):
...@@ -1313,7 +1326,7 @@ class t_dot(unittest.TestCase): ...@@ -1313,7 +1326,7 @@ class t_dot(unittest.TestCase):
def not_aligned(self, x, y): def not_aligned(self, x, y):
z = dot(x,y) z = dot(x,y)
try: try:
tz = eval_outputs([z]) tz = eval_outputs([z], mode = compile.Mode(optimizer = None, linker = 'py'))
except ValueError, e: except ValueError, e:
self.failUnless(e[0].split()[1:4] == ['are', 'not', 'aligned'], e) self.failUnless(e[0].split()[1:4] == ['are', 'not', 'aligned'], e)
return return
...@@ -1359,7 +1372,7 @@ class t_gemm(unittest.TestCase): ...@@ -1359,7 +1372,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=l) f = function([tz,ta,tx,ty,tb], gemm(tz,ta,tx,ty,tb), mode=compile.Mode(optimizer = None, 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)
...@@ -1481,7 +1494,7 @@ class t_gemm(unittest.TestCase): ...@@ -1481,7 +1494,7 @@ class t_gemm(unittest.TestCase):
tz,ta,tx,ty,tb = [value(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=l) f = function([tz,ta,tx,ty,tb], gemm(tz,ta,tx,ty,tb), mode = compile.Mode(optimizer = None, 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)
...@@ -1730,8 +1743,8 @@ class T_op_cache(unittest.TestCase): ...@@ -1730,8 +1743,8 @@ class T_op_cache(unittest.TestCase):
v = matrix() v = matrix()
v.name = 'v' v.name = 'v'
gv = fill(v/v, 1.0)/v - (fill(v/v, 1.0) * v) / (v*v) gv = fill(v/v, 1.0)/v - (fill(v/v, 1.0) * v) / (v*v)
fn_py = function([v], [gv], linker = 'py') fn_py = function([v], gv, mode = compile.Mode(optimizer = None, linker = 'py'))
fn_c_or_py = function([v], [gv], linker = 'c|py') fn_c_or_py = function([v], gv, compile.Mode(optimizer = None, linker = 'c|py'))
a = numpy.random.rand(5,2) a = numpy.random.rand(5,2)
self.failUnless(numpy.all(fn_py(a) == fn_c_or_py(a))) self.failUnless(numpy.all(fn_py(a) == fn_c_or_py(a)))
......
...@@ -64,7 +64,9 @@ default_linker = 'c|py' ...@@ -64,7 +64,9 @@ default_linker = 'c|py'
predefined_optimizers = { predefined_optimizers = {
None : lambda env: None, None : lambda env: None,
'merge' : gof.MergeOptimizer(), 'merge' : gof.MergeOptimizer(),
'math' : gof.MergeOptMerge(tensor_opt.math_optimizer) 'math' : gof.MergeOptMerge(
gof.PureThenInplaceOptimizer(tensor_opt.math_optimizer,
tensor_opt.inplace_optimizer))
} }
default_optimizer = 'merge' default_optimizer = 'merge'
...@@ -87,6 +89,12 @@ class Mode(object): ...@@ -87,6 +89,12 @@ class Mode(object):
""" """
def __init__(self, linker = default_linker, optimizer = default_optimizer): def __init__(self, linker = default_linker, optimizer = default_optimizer):
self.__setstate__((linker, optimizer))
def __getstate__(self):
return (self.provided_linker, self.provided_optimizer)
def __setstate__(self, (linker, optimizer)):
self.provided_linker = linker self.provided_linker = linker
self.provided_optimizer = optimizer self.provided_optimizer = optimizer
if isinstance(linker, str) or linker is None: if isinstance(linker, str) or linker is None:
...@@ -104,9 +112,9 @@ class Mode(object): ...@@ -104,9 +112,9 @@ class Mode(object):
# string as the key # string as the key
predefined_modes = { predefined_modes = {
'SANITY_CHECK' : Mode('c&py', 'math'), 'SANITY_CHECK' : Mode('c&py', 'math'),
'FAST_COMPILE' : Mode('py', None), 'FAST_COMPILE' : Mode('py', 'merge'),
'FAST_RUN' : Mode('c|py', 'math'), 'FAST_RUN' : Mode('c|py', 'math'),
'EXPENSIVE_OPTIMIZATIONS' : Mode('c|py', 'math') 'EXPENSIVE_OPTIMIZATIONS' : Mode('c|py', 'math'),
} }
default_mode = 'FAST_RUN' default_mode = 'FAST_RUN'
...@@ -134,17 +142,22 @@ class SymbolicInput(object): ...@@ -134,17 +142,22 @@ class SymbolicInput(object):
True: permit the compiled function to modify the python object being passed as the input True: permit the compiled function to modify the python object being passed as the input
False: do not permit the compiled function to modify the python object being passed as the input. False: do not permit the compiled function to modify the python object being passed as the input.
strict: Bool (default: False)
True: means that the value you pass for this input must have exactly the right type
False: the value you pass for this input may be casted automatically to the proper type
autoname: Bool (default: True) autoname: Bool (default: True)
See the name option. See the name option.
""" """
def __init__(self, result, name=None, update=None, mutable=None, autoname=True): def __init__(self, result, name=None, update=None, mutable=None, strict=False, autoname=True):
self.result = result self.result = result
self.name = result.name if (autoname and name is None) else name self.name = result.name if (autoname and name is None) else name
if self.name is not None and not isinstance(self.name, str): if self.name is not None and not isinstance(self.name, str):
raise TypeError("name must be a string! (got: %s)" % self.name) raise TypeError("name must be a string! (got: %s)" % self.name)
self.update = update self.update = update
self.mutable = mutable if (mutable is not None) else (update is not None) self.mutable = mutable if (mutable is not None) else (update is not None)
self.strict = strict
def __str__(self): def __str__(self):
if self.update: if self.update:
...@@ -168,6 +181,8 @@ class SymbolicInputKit(object): ...@@ -168,6 +181,8 @@ class SymbolicInputKit(object):
""" """
def __init__(self, name): def __init__(self, name):
if not isinstance(name, str):
raise TypeError('naem must be a string (got: %s)' % name)
self.name = name self.name = name
self.sinputs = [] self.sinputs = []
self.results = [] self.results = []
...@@ -234,11 +249,15 @@ class In(SymbolicInput): ...@@ -234,11 +249,15 @@ class In(SymbolicInput):
True: permit the compiled function to modify the python object being passed as the input True: permit the compiled function to modify the python object being passed as the input
False: do not permit the compiled function to modify the python object being passed as the input. False: do not permit the compiled function to modify the python object being passed as the input.
strict: Bool (default: False)
True: means that the value you pass for this input must have exactly the right type
False: the value you pass for this input may be casted automatically to the proper type
autoname: Bool (default: True) autoname: Bool (default: True)
See the name option. See the name option.
""" """
def __init__(self, result, name=None, value=None, update=None, mutable=None, autoname=True): def __init__(self, result, name=None, value=None, update=None, mutable=None, strict=False, autoname=True):
super(In, self).__init__(result, name, update, mutable, autoname) super(In, self).__init__(result, name, update, mutable, strict, autoname)
self.value = value self.value = value
...@@ -352,7 +371,7 @@ class FunctionMaker(object): ...@@ -352,7 +371,7 @@ class FunctionMaker(object):
else: else:
raise TypeError("Unknown output type:", type(output), output) raise TypeError("Unknown output type:", type(output), output)
def __init__(self, inputs, outputs, mode = 'FAST_RUN', accept_inplace = True): def __init__(self, inputs, outputs, mode = 'FAST_RUN', accept_inplace = False):
""" """
Create a FunctionMaker for the specified inputs, outputs and mode. Create a FunctionMaker for the specified inputs, outputs and mode.
...@@ -407,6 +426,8 @@ class FunctionMaker(object): ...@@ -407,6 +426,8 @@ class FunctionMaker(object):
self.expanded_inputs = expanded_inputs self.expanded_inputs = expanded_inputs
self.outputs = outputs self.outputs = outputs
self.unpack_single = unpack_single self.unpack_single = unpack_single
self.mode = mode
self.accept_inplace = accept_inplace
def create(self, defaults = None, trustme = False): def create(self, defaults = None, trustme = False):
""" """
...@@ -427,12 +448,12 @@ class FunctionMaker(object): ...@@ -427,12 +448,12 @@ class FunctionMaker(object):
for (input, indices, subinputs), default in zip(self.indices, defaults): for (input, indices, subinputs), default in zip(self.indices, defaults):
__default = default __default = default
# If the default is a gof.Filter, this means we want to share # If the default is a gof.Container, this means we want to share
# the same storage. This is done by appending default.storage # the same storage. This is done by appending default.storage
# to input_storage # to input_storage
if isinstance(default, gof.Filter): if isinstance(default, gof.Container):
if indices is not None: if indices is not None:
raise TypeError("Cannot take a Filter instance as default for a SymbolicInputKit.") raise TypeError("Cannot take a Container instance as default for a SymbolicInputKit.")
input_storage.append(default.storage) input_storage.append(default.storage)
default = None default = None
# If the input is a SymbolicInputKit, it represents more than # If the input is a SymbolicInputKit, it represents more than
...@@ -464,7 +485,7 @@ class FunctionMaker(object): ...@@ -464,7 +485,7 @@ class FunctionMaker(object):
# back into the storage as it would defeat the point of updating it. We # back into the storage as it would defeat the point of updating it. We
# always do this policy. # always do this policy.
if default is None: if default is None:
if trustme or isinstance(__default, gof.Filter): if trustme or isinstance(__default, gof.Container):
_defaults.append((False, False, default)) _defaults.append((False, False, default))
else: else:
# This might catch some bugs early # This might catch some bugs early
...@@ -487,8 +508,28 @@ class FunctionMaker(object): ...@@ -487,8 +508,28 @@ class FunctionMaker(object):
return fn return fn
import copy_reg
import cPickle
def _pickle_FunctionMaker(fm):
return (_constructor_FunctionMaker, (fm.inputs, fm.outputs, fm.mode, fm.accept_inplace))
def _constructor_FunctionMaker(*args):
return FunctionMaker(*args)
copy_reg.pickle(FunctionMaker, _pickle_FunctionMaker)
def _pickle_slice(s):
return (slice, (s.start, s.stop, s.step))
copy_reg.pickle(slice, _pickle_slice)
from functools import partial from functools import partial
DUPLICATE = ['DUPLICATE'] # unique id object used as a placeholder for duplicate entries DUPLICATE = ['DUPLICATE'] # unique id object used as a placeholder for duplicate entries
class Function(object): class Function(object):
""" """
...@@ -498,8 +539,8 @@ class Function(object): ...@@ -498,8 +539,8 @@ class Function(object):
def __init__(self, fn, input_storage, output_storage, indices, outputs, defaults, unpack_single, maker): def __init__(self, fn, input_storage, output_storage, indices, outputs, defaults, unpack_single, maker):
""" """
fn -> a function returned by some linker's make_thunk method fn -> a function returned by some linker's make_thunk method
input_storage -> list of Filter instances used by fn to fetch the inputs input_storage -> list of Container instances used by fn to fetch the inputs
output_storage -> list of Filter instances used by fn to store the outputs in output_storage -> list of Container instances used by fn to store the outputs in
indices -> list of (SymbolicInput|SymbolicInputKit, indices, [SymbolicInput,...]), one tuple for each input indices -> list of (SymbolicInput|SymbolicInputKit, indices, [SymbolicInput,...]), one tuple for each input
defaults -> list of (required (bool), refeed (bool), value), one tuple for each input defaults -> list of (required (bool), refeed (bool), value), one tuple for each input
required -> whether this input is required or optional required -> whether this input is required or optional
...@@ -531,6 +572,8 @@ class Function(object): ...@@ -531,6 +572,8 @@ class Function(object):
for i, ((input, indices, sinputs), (required, refeed, value)) in enumerate(zip(self.indices, defaults)): for i, ((input, indices, sinputs), (required, refeed, value)) in enumerate(zip(self.indices, defaults)):
if indices is None: # this is true iff input is not a SymbolicInputKit if indices is None: # this is true iff input is not a SymbolicInputKit
c = containers[0] c = containers[0]
if input.strict:
c.strict = True
if value is not None: if value is not None:
# always initialize the storage # always initialize the storage
c.data = value c.data = value
...@@ -591,7 +634,7 @@ class Function(object): ...@@ -591,7 +634,7 @@ class Function(object):
raise TypeError("Unknown input or state: %s" % item) raise TypeError("Unknown input or state: %s" % item)
if s is DUPLICATE: if s is DUPLICATE:
raise TypeError("Ambiguous name: %s - please check the names of the inputs of your function for duplicates." % item) raise TypeError("Ambiguous name: %s - please check the names of the inputs of your function for duplicates." % item)
if isinstance(s, gof.Filter): if isinstance(s, gof.Container):
return s.value return s.value
else: else:
raise NotImplementedError raise NotImplementedError
...@@ -602,7 +645,7 @@ class Function(object): ...@@ -602,7 +645,7 @@ class Function(object):
raise TypeError("Unknown input or state: %s" % item) raise TypeError("Unknown input or state: %s" % item)
if s is DUPLICATE: if s is DUPLICATE:
raise TypeError("Ambiguous name: %s - please check the names of the inputs of your function for duplicates." % item) raise TypeError("Ambiguous name: %s - please check the names of the inputs of your function for duplicates." % item)
if isinstance(s, gof.Filter): if isinstance(s, gof.Container):
s.value = value s.value = value
s.provided += 1 s.provided += 1
else: else:
...@@ -625,6 +668,7 @@ class Function(object): ...@@ -625,6 +668,7 @@ class Function(object):
def __setitem__(self, item, value): def __setitem__(self, item, value):
self.value[item] = value self.value[item] = value
def __copy__(self): def __copy__(self):
defaults = [default for _1, _2, default in self.defaults] defaults = [default for _1, _2, default in self.defaults]
cpy = self.maker.create(defaults, trustme = True) cpy = self.maker.create(defaults, trustme = True)
...@@ -677,6 +721,26 @@ class Function(object): ...@@ -677,6 +721,26 @@ class Function(object):
doc="""TODOC""") doc="""TODOC""")
def _pickle_Function(f):
ins = list(f.input_storage)
defaults = []
for (input, indices, inputs), (required, refeed, default) in zip(f.indices, f.defaults):
if isinstance(input, SymbolicInputKit):
defaults.append(default)
ins[:len(indices)] = []
else:
defaults.append(ins[0])
del ins[0]
return (_constructor_Function, (f.maker, defaults, [x.data for x in f.input_storage]))
def _constructor_Function(maker, defaults, data):
f = maker.create(defaults, trustme = True)
for container, x in zip(f.input_storage, data):
container.data = x
return f
copy_reg.pickle(Function, _pickle_Function)
def function(inputs, outputs, mode='FAST_RUN', accept_inplace = False): def function(inputs, outputs, mode='FAST_RUN', accept_inplace = False):
""" """
...@@ -759,34 +823,8 @@ def function(inputs, outputs, mode='FAST_RUN', accept_inplace = False): ...@@ -759,34 +823,8 @@ def function(inputs, outputs, mode='FAST_RUN', accept_inplace = False):
inputs = map(wrap_in, inputs) inputs = map(wrap_in, inputs)
outputs = map(wrap_out, outputs) if isinstance(outputs, (list, tuple)) else wrap_out(outputs) outputs = map(wrap_out, outputs) if isinstance(outputs, (list, tuple)) else wrap_out(outputs)
# create a subclass of Function for the given arguments.
class F(Function):
pass
fn = FunctionMaker(inputs, outputs, mode, accept_inplace = accept_inplace).create([getattr(input, 'value', None) for input in inputs]) fn = FunctionMaker(inputs, outputs, mode, accept_inplace = accept_inplace).create([getattr(input, 'value', None) for input in inputs])
# add all input names as properties of F
def _get(name, self):
return self[name]
def _set(name, self, value):
self[name] = value
def _err(name, self):
raise TypeError("Ambiguous name: %s - please check the names of the inputs of your function for duplicates." % name)
seen = set()
for input in inputs:
name = input.name
if name:
if name in seen:
f = property(partial(_err, input.name), partial(_err, input.name))
setattr(F, input.name, f)
elif not hasattr(F, name):
f = property(partial(_get, input.name), partial(_set, input.name))
setattr(F, input.name, f)
seen.add(input.name)
else:
pass
fn.__class__ = F
return fn return fn
...@@ -825,10 +863,6 @@ class OpFromGraph(gof.Op): ...@@ -825,10 +863,6 @@ class OpFromGraph(gof.Op):
""" """
def __init__(self, inputs, outputs, grad_depth = 1, **kwargs): def __init__(self, inputs, outputs, grad_depth = 1, **kwargs):
if kwargs.get('borrow_outputs') or kwargs.get('unpack_single'):
raise ValueError('The borrow_outputs and unpack_single options cannot be True')
kwargs['unpack_single'] = False
kwargs['borrow_outputs'] = False
self.fn = function(inputs, outputs, **kwargs) self.fn = function(inputs, outputs, **kwargs)
self.inputs = inputs self.inputs = inputs
self.outputs = outputs self.outputs = outputs
......
...@@ -7,6 +7,7 @@ import scalar ...@@ -7,6 +7,7 @@ import scalar
from scalar import Scalar from scalar import Scalar
import gof import gof
from gof.python25 import all from gof.python25 import all
from copy import copy
# tensor depends on elemwise to provide definitions for several ops # tensor depends on elemwise to provide definitions for several ops
...@@ -231,6 +232,15 @@ class Elemwise(Op): ...@@ -231,6 +232,15 @@ class Elemwise(Op):
else: else:
self.ufunc = None self.ufunc = None
def __getstate__(self):
d = copy(self.__dict__)
d.pop('ufunc')
return d
def __setstate__(self, d):
self.__dict__.update(d)
self.ufunc = numpy.frompyfunc(self.scalar_op.impl, self.scalar_op.nin, self.scalar_op.nout)
def make_node(self, *inputs): def make_node(self, *inputs):
""" """
If the inputs have different number of dimensions, their shape If the inputs have different number of dimensions, their shape
......
...@@ -12,7 +12,7 @@ from graph import \ ...@@ -12,7 +12,7 @@ from graph import \
Apply, Result, Constant, Value, view_roots Apply, Result, Constant, Value, view_roots
from link import \ from link import \
Filter, Linker, LocalLinker, PerformLinker, WrapLinker, Profiler Container, Linker, LocalLinker, PerformLinker, WrapLinker, Profiler
from op import \ from op import \
Op Op
...@@ -22,7 +22,8 @@ from opt import \ ...@@ -22,7 +22,8 @@ from opt import \
MergeOptimizer, MergeOptMerge, \ MergeOptimizer, MergeOptMerge, \
LocalOptimizer, local_optimizer, LocalOptGroup, LocalOpKeyOptGroup, \ LocalOptimizer, local_optimizer, LocalOptGroup, LocalOpKeyOptGroup, \
OpSub, OpRemove, PatternSub, \ OpSub, OpRemove, PatternSub, \
NavigatorOptimizer, TopoOptimizer, OpKeyOptimizer NavigatorOptimizer, TopoOptimizer, OpKeyOptimizer, \
PureThenInplaceOptimizer
from toolbox import \ from toolbox import \
Bookkeeper, History, Validator, ReplaceValidate, NodeFinder, PrintListener Bookkeeper, History, Validator, ReplaceValidate, NodeFinder, PrintListener
......
...@@ -624,8 +624,8 @@ class CLinker(link.Linker): ...@@ -624,8 +624,8 @@ class CLinker(link.Linker):
input_storage, input_storage,
output_storage) output_storage)
return thunk, \ return thunk, \
[link.Filter(input, storage) for input, storage in zip(self.env.inputs, input_storage)], \ [link.Container(input, storage) for input, storage in zip(self.env.inputs, input_storage)], \
[link.Filter(output, storage, True) for output, storage in zip(self.env.outputs, output_storage)], \ [link.Container(output, storage, True) for output, storage in zip(self.env.outputs, output_storage)], \
error_storage error_storage
def make_thunk(self, input_storage = None, output_storage = None): def make_thunk(self, input_storage = None, output_storage = None):
...@@ -873,8 +873,8 @@ class OpWiseCLinker(link.LocalLinker): ...@@ -873,8 +873,8 @@ class OpWiseCLinker(link.LocalLinker):
f = link.streamline(env, thunks, order, no_recycling = no_recycling, profiler = profiler) f = link.streamline(env, thunks, order, no_recycling = no_recycling, profiler = profiler)
return f, [link.Filter(input, storage) for input, storage in zip(env.inputs, input_storage)], \ return f, [link.Container(input, storage) for input, storage in zip(env.inputs, input_storage)], \
[link.Filter(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \ [link.Container(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \
thunks, order thunks, order
...@@ -940,6 +940,7 @@ class DualLinker(link.Linker): ...@@ -940,6 +940,7 @@ class DualLinker(link.Linker):
no_recycling = self.no_recycling no_recycling = self.no_recycling
_f, i1, o1, thunks1, order1 = link.PerformLinker().accept(env, no_recycling = no_recycling).make_all(**kwargs) _f, i1, o1, thunks1, order1 = link.PerformLinker().accept(env, no_recycling = no_recycling).make_all(**kwargs)
kwargs.pop('input_storage', None)
_f, i2, o2, thunks2, order2 = OpWiseCLinker().accept(env, no_recycling = no_recycling).make_all(**kwargs) _f, i2, o2, thunks2, order2 = OpWiseCLinker().accept(env, no_recycling = no_recycling).make_all(**kwargs)
def f(): def f():
......
...@@ -140,7 +140,7 @@ class Result(utils.object2): ...@@ -140,7 +140,7 @@ class Result(utils.object2):
else: else:
return str(self.owner.op) + "." + str(self.index) return str(self.owner.op) + "." + str(self.index)
else: else:
return "<?>::" + str(self.type) return "<%s>" % str(self.type)
def __repr__(self): def __repr__(self):
return str(self) return str(self)
def clone(self): def clone(self):
......
import utils import utils
import graph import graph
from type import Type
import sys, traceback import sys, traceback
from copy import copy from copy import copy
...@@ -107,25 +108,30 @@ class Linker(object): ...@@ -107,25 +108,30 @@ class Linker(object):
return execute return execute
class Filter(object): class Container(object):
def __init__(self, r, storage, readonly = False, strict = False): def __init__(self, r, storage, readonly = False, strict = False, name = None):
self.r = r #self.r = r
if isinstance(r, Type):
self.type = r
else:
self.type = r.type self.type = r.type
self.name = name or r.name
self.storage = storage self.storage = storage
self.readonly = readonly self.readonly = readonly
self.strict = strict self.strict = strict
def __get(self): def __get(self):
return self.storage[0] return self.storage[0]
def __set(self, value): def __set(self, value):
try:
if self.readonly: if self.readonly:
raise Exception("Cannot set readonly storage.") raise Exception("Cannot set readonly storage: %s" % self.name)
try:
if self.strict: if self.strict:
self.storage[0] = self.type.filter(value, strict = True) self.storage[0] = self.type.filter(value, strict = True)
else: else:
self.storage[0] = self.type.filter(value) self.storage[0] = self.type.filter(value)
except: except Exception, e:
raise_with_op(self.r) e.args = e.args + (self.name,)
raise
data = property(__get, __set) data = property(__get, __set)
value = property(__get, __set) value = property(__get, __set)
def __str__(self): def __str__(self):
...@@ -256,8 +262,8 @@ class PerformLinker(LocalLinker): ...@@ -256,8 +262,8 @@ class PerformLinker(LocalLinker):
f = streamline(env, thunks, order, no_recycling = no_recycling, profiler = profiler) f = streamline(env, thunks, order, no_recycling = no_recycling, profiler = profiler)
return f, [Filter(input, storage) for input, storage in zip(env.inputs, input_storage)], \ return f, [Container(input, storage) for input, storage in zip(env.inputs, input_storage)], \
[Filter(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \ [Container(output, storage, True) for output, storage in zip(env.outputs, output_storage)], \
thunks, order thunks, order
...@@ -329,7 +335,9 @@ class WrapLinker(Linker): ...@@ -329,7 +335,9 @@ class WrapLinker(Linker):
def make_thunk(self, **kwargs): def make_thunk(self, **kwargs):
no_recycling = self.no_recycling no_recycling = self.no_recycling
make_all = [l.make_all(**kwargs) for l in self.linkers] make_all = [self.linkers[0].make_all(**kwargs)]
kwargs.pop('input_storage', None)
make_all += [l.make_all(**kwargs) for l in self.linkers[1:]]
fns, input_lists, output_lists, thunk_lists, order_lists \ fns, input_lists, output_lists, thunk_lists, order_lists \
= zip(*make_all) = zip(*make_all)
......
...@@ -12,6 +12,7 @@ import toolbox ...@@ -12,6 +12,7 @@ import toolbox
import op import op
from copy import copy from copy import copy
from collections import deque from collections import deque
import destroyhandler as dh
class Optimizer: class Optimizer:
...@@ -60,7 +61,7 @@ class FromFunctionOptimizer(Optimizer): ...@@ -60,7 +61,7 @@ class FromFunctionOptimizer(Optimizer):
def __init__(self, fn): def __init__(self, fn):
self.apply = fn self.apply = fn
def add_requirements(self, env): def add_requirements(self, env):
env.extend(gof.toolbox.ReplaceValidate) env.extend(toolbox.ReplaceValidate())
def optimizer(f): def optimizer(f):
return FromFunctionOptimizer(f) return FromFunctionOptimizer(f)
...@@ -208,7 +209,7 @@ class FromFunctionLocalOptimizer(LocalOptimizer): ...@@ -208,7 +209,7 @@ class FromFunctionLocalOptimizer(LocalOptimizer):
def __init__(self, fn): def __init__(self, fn):
self.transform = fn self.transform = fn
def add_requirements(self, env): def add_requirements(self, env):
env.extend(gof.toolbox.ReplaceValidate) env.extend(toolbox.ReplaceValidate())
def local_optimizer(f): def local_optimizer(f):
return FromFunctionLocalOptimizer(f) return FromFunctionLocalOptimizer(f)
...@@ -608,6 +609,21 @@ def check_chain(r, *chain): ...@@ -608,6 +609,21 @@ def check_chain(r, *chain):
############
### Misc ###
############
class PureThenInplaceOptimizer(Optimizer):
def __init__(self, pure, inplace):
self.pure = pure
self.inplace = inplace
def apply(self, env):
self.pure(env)
env.extend(dh.DestroyHandler())
self.inplace(env)
......
...@@ -252,16 +252,17 @@ def upcast_out(*types): ...@@ -252,16 +252,17 @@ def upcast_out(*types):
return Scalar(dtype = Scalar.upcast(*types)), return Scalar(dtype = Scalar.upcast(*types)),
def same_out(type): def same_out(type):
return type, return type,
def transfer_type(i): class transfer_type:
def __init__(self, i):
assert type(i) == int assert type(i) == int
def f(*types): self.i = i
return types[i], def __call__(self, *types):
f.__name__ = "transfer_type_%i" % i return types[self.i]
return f class specific_out:
def specific_out(*spec): def __init__(self, *spec):
def f(*types): self.spec = spec
return spec def __call__(self, *types):
return f return self.spec
def int_out(*types): def int_out(*types):
return int64, return int64,
def float_out(*types): def float_out(*types):
......
...@@ -82,10 +82,11 @@ class Tensor(Type): ...@@ -82,10 +82,11 @@ class Tensor(Type):
for L{broadcasting}, as described and implemented in Numpy. for L{broadcasting}, as described and implemented in Numpy.
""" """
def __init__(self, dtype, broadcastable): def __init__(self, dtype, broadcastable, name = None):
self.dtype = str(dtype) self.dtype = str(dtype)
self.broadcastable = tuple(broadcastable) self.broadcastable = tuple(broadcastable)
self.dtype_specs() # error checking is done there self.dtype_specs() # error checking is done there
self.name = name
def filter(self, data, strict = False): def filter(self, data, strict = False):
_data = data _data = data
...@@ -141,10 +142,21 @@ class Tensor(Type): ...@@ -141,10 +142,21 @@ class Tensor(Type):
return TensorResult(self, name = name) return TensorResult(self, name = name)
def __str__(self): def __str__(self):
return "%s(%s)" % (str(self.dtype), str(self.broadcastable)) if self.name:
return self.name
else:
b = self.broadcastable
#bcast = str(self.broadcastable)
bcast = {(): 'scalar',
(False,): 'vector',
(False, True): 'col',
(True, False): 'row',
(False, False): 'matrix'}.get(b, "%iD" % len(b) if not any(b) else str(b))
return "Tensor(%s, %s)" % (str(self.dtype), bcast)
def __repr__(self): def __repr__(self):
return "Tensor{%s, %s}" % (str(self.dtype), str(self.broadcastable)) return str(self)
#"Tensor{%s, %s}" % (str(self.dtype), str(self.broadcastable))
def c_declare(self, name, sub): def c_declare(self, name, sub):
return """ return """
......
...@@ -7,6 +7,7 @@ import tensor as T ...@@ -7,6 +7,7 @@ import tensor as T
import numpy as N import numpy as N
import operator import operator
import itertools import itertools
import sys
# Utilities # Utilities
...@@ -40,8 +41,7 @@ dot_to_gemm = gof.PatternSub((T.dot, 'a', 'b'), ...@@ -40,8 +41,7 @@ dot_to_gemm = gof.PatternSub((T.dot, 'a', 'b'),
allow_multiple_clients = False) allow_multiple_clients = False)
@gof.optimizer def _insert_inplace_optimizer(env):
def insert_inplace_optimizer(self, env):
""" """
Usage: inplace_optimizer.optimize(env) Usage: inplace_optimizer.optimize(env)
...@@ -66,14 +66,16 @@ def insert_inplace_optimizer(self, env): ...@@ -66,14 +66,16 @@ def insert_inplace_optimizer(self, env):
for candidate_input in candidate_inputs: for candidate_input in candidate_inputs:
inplace_pattern = dict(baseline, **{candidate_output: candidate_input}) inplace_pattern = dict(baseline, **{candidate_output: candidate_input})
try: try:
new = Elemwise(op.scalar_op, inplace_pattern).make_node(op.inputs) new = Elemwise(op.scalar_op, inplace_pattern).make_node(*node.inputs)
env.replace_all_validate(dict(zip(node.outputs, new.outputs))) env.replace_all_validate(zip(node.outputs, new.outputs))
except: except Exception, e:
continue continue
candidate_inputs.remove(candidate_input) candidate_inputs.remove(candidate_input)
node = new node = new
baseline = inplace_pattern baseline = inplace_pattern
break break
insert_inplace_optimizer = gof.optimizer(_insert_inplace_optimizer)
inplace_optimizer = gof.SeqOptimizer(out2in(gemm_pattern_1), inplace_optimizer = gof.SeqOptimizer(out2in(gemm_pattern_1),
out2in(dot_to_gemm), out2in(dot_to_gemm),
......
...@@ -154,186 +154,3 @@ class RandomKit(SymbolicInputKit): ...@@ -154,186 +154,3 @@ class RandomKit(SymbolicInputKit):
rk = RandomKit('rk', 0xBAD5EED) rk = RandomKit('rk', 0xBAD5EED)
# class RandomState(object):
# """The Theano version of numpy.RandomState
# This class generates a sequence of L{Op} instances via the gen() and
# gen_like() methods.
# @ivar seed: an integer which determines the initial state of the L{Op}
# instances returned by gen(), gen_like()
# @type seed: int
# """
# def __init__(self, seed):
# self.seed = seed
# def gen(self, dist, shape=(), ndim=None):
# """
# @param dist: identifier of a sampling distribution. See L{_fn_from_dist}.
# @param shape: tuple
# @return: A tensor of random numbers, with given shape.
# @rtype: L{Result} (output of L{Apply} of L{NumpyGenerator} instance)
# """
# self.seed += 1
# fn = RandomState._fn_from_dist(dist)
# if isinstance(shape, tuple):
# return NumpyGenerator(self.seed-1, len(shape),fn) (shape)
# return NumpyGenerator(self.seed - 1, ndim, fn)(shape)
# def gen_like(self, dist, x):
# """
# @param dist: identifier of a sampling distribution. See L{_fn_from_dist}.
# @param x: L{Result} of type L{Tensor}
# @return: A tensor of random numbers, with the same shape as x.
# @rtype: L{Result} (output of L{Apply} of L{NumpyGenerator} instance)
# """
# self.seed += 1
# fn = RandomState._fn_from_dist(dist)
# return NumpyGenerator(self.seed-1, x.type.ndim, fn)(tensor.shape(x))
# def uniform_like(self, template, low=0.,high=1.):
# """
# Return a multivariate uniform(low,high)
# random variable in a tensor of the same shape as template
# (template can either be a tensor or a shape tuple). Each element of the
# resulting tensor is sampled independently. low and high can
# be scalars or have the same shape as the template (or broadcastable
# to it).
# """
# return self.gen_like(('uniform',{'low':low,'high':high}),template)
# def binomial_like(self, template, n=1, p=0.5):
# """
# Return a multivariate binomial(n,p) random variable in a tensor of the same shape as template
# (template can either be a tensor or a shape tuple). Each element of the
# resulting tensor is sampled independently. low and high can
# be scalars or have the same shape as the template (or broadcastable
# to it).
# """
# return self.gen_like(('binomial',{'n':n,'p':p}),template)
# @staticmethod
# def _fn_from_dist(dist, cache={}):
# """Return a function from a distribution description
# @param dist: identifier of a sampling distribution.
# @type dist: callable or str or tuple(str, dict)
# @param cache: The optional cache argument implements a closure, which ensures that
# multiple requests for the same sampling function will get the same
# sampling function. L{NumpyGenerator}.__hash__ depends on this.
# @type cache: dict
# """
# if callable(dist):
# return dist
# if isinstance(dist, str):
# return getattr(numpy.random.RandomState, dist)
# name, kwargs = dist
# key = (name, tuple(kwargs.items()))
# if key not in cache:
# fn = getattr(numpy.random.RandomState, name)
# fn = functools.partial(fn, **kwargs)
# cache[key] = fn
# return cache[key]
# class NumpyGenerator(gof.op.Op):
# """Supply a sequence of random tensors of a given shape, from a given
# distribution.
# @param seed: initial state for instances of this L{Op}.
# @type seed: anything that numpy.random.RandomState accepts.
# @param ndim: the rank of random tensors produced by this op.
# @type ndim: non-negative integer
# @param fn: a sampling function
# @type fn: a callable that can reply to fn(numpy.RandomState(), size=<tuple>)
# """
# destroy_map = {0: [0]}
# def __init__(self, seed, ndim, fn, **kwargs):
# gof.op.Op.__init__(self, **kwargs)
# self.seed = seed
# self.ndim = ndim
# self.fn = fn
# assert numpy.random.RandomState(seed) #test the seed
# assert 'int' in str(type(ndim))
# assert callable(self.fn)
# def __eq__(self, other):
# return (type(self) is type(other))\
# and self.__class__ is NumpyGenerator \
# and self.seed == other.seed \
# and self.ndim == other.ndim \
# and self.fn == other.fn
# def __hash__(self):
# return self.seed ^ self.ndim ^ hash(self.fn)
# def make_node(self, _shape):
# #TODO: check for constant shape, and guess the broadcastable bits
# shape = tensor.convert_to_int64(_shape)
# if shape.type.ndim != 1:
# raise TypeError('shape argument was not converted to 1-d tensor', _shape)
# # we generate one random number with the distribution to determine what dtype to expect
# output_dtype = str(self.fn(numpy.random.RandomState(18), size=(1,)).dtype)
# inputs = [gof.Value(gof.type.generic, numpy.random.RandomState(self.seed)), shape]
# outputs = [tensor.Tensor(dtype=output_dtype, broadcastable = [False]*self.ndim).make_result()]
# return gof.Apply(op = self, inputs = inputs, outputs = outputs)
# def grad(self, inputs, grad_outputs):
# return [None, None]
# def perform(self, node, input_storage, output_storage):
# rng = input_storage[0]
# shape = input_storage[1]
# if self.ndim != len(shape):
# raise ValueError('shape argument %s had the wrong length (!=%i)' %
# (shape, self.ndim) )
# output_storage[0][0] = self.fn(rng, size=shape)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论