* mode.py now defines default_mode, which is read from THEANO_DEFAULT_MODE env.…

* mode.py now defines default_mode, which is read from THEANO_DEFAULT_MODE env. var (or defaults to FAST_RUN if absent). * added registration of DEBUG_MODE string for DebugMode (old OptCheck) * all unittests now run with default mode. To change mode, run unittests like so: THEANO_DEFAULT_MODE=[FAST_COMPILE|FAST_RUN|DEBUG_MODE] nosetests Note: unittests specific to one mode (ie. gemm and blas) were kept in FAST_RUN only TODO: once DEBUG_MODE is made such that it defaults to FAST_RUN after the first successfull execution of the graph, put DEBUG_MODE as the default
上级 5438f37b
...@@ -57,6 +57,7 @@ import tensor ...@@ -57,6 +57,7 @@ import tensor
import scalar import scalar
import sparse import sparse
import gradient import gradient
import gof
## import scalar_opt ## import scalar_opt
......
...@@ -14,5 +14,7 @@ from builders import * ...@@ -14,5 +14,7 @@ from builders import *
import module import module
from module import * from module import *
import debugmode
from profilemode import ProfileMode from profilemode import ProfileMode
...@@ -11,13 +11,9 @@ from ..gof import Env, graph, utils, link ...@@ -11,13 +11,9 @@ from ..gof import Env, graph, utils, link
from ..gof.link import WrapLinkerMany, raise_with_op from ..gof.link import WrapLinkerMany, raise_with_op
from ..gof.cutils import run_cthunk from ..gof.cutils import run_cthunk
from ..gof.cc import OpWiseCLinker, CLinker from ..gof.cc import OpWiseCLinker, CLinker
from ..compile.mode import Mode
import numpy import numpy
from ..compile.function_module import (FunctionMaker,
from ..compile.function_module import (convert_function_input,
FunctionMaker,
predefined_modes,
Function, Function,
infer_reuse_pattern, infer_reuse_pattern,
SymbolicInput, SymbolicInput,
...@@ -622,6 +618,9 @@ class DebugModeFunctionMaker(FunctionMaker): #inheritance buys a few helper func ...@@ -622,6 +618,9 @@ class DebugModeFunctionMaker(FunctionMaker): #inheritance buys a few helper func
fn = self.function_builder(_fn, _i, _o, self.indices, self.outputs, defaults, self.unpack_single, self) fn = self.function_builder(_fn, _i, _o, self.indices, self.outputs, defaults, self.unpack_single, self)
return fn return fn
from ..compile.mode import Mode, register_mode
class DebugMode(Mode): class DebugMode(Mode):
"""Evaluation Mode that detects optimization errors. """Evaluation Mode that detects optimization errors.
...@@ -648,6 +647,4 @@ class DebugMode(Mode): ...@@ -648,6 +647,4 @@ class DebugMode(Mode):
linker=DebugModeLinker) linker=DebugModeLinker)
self.stability_patience = stability_patience self.stability_patience = stability_patience
self.check_c_code = check_c_code self.check_c_code = check_c_code
register_mode('DEBUG_MODE',DebugMode(optimizer='fast_run'))
...@@ -15,10 +15,6 @@ from copy import copy ...@@ -15,10 +15,6 @@ from copy import copy
from mode import * from mode import *
from io import * from io import *
# used by function and module as the default compilation mode
mode_default = 'FAST_COMPILE'
def infer_reuse_pattern(env, outputs_to_disown): def infer_reuse_pattern(env, outputs_to_disown):
""" """
Given an env and a list of results, returns the list of all Given an env and a list of results, returns the list of all
...@@ -454,7 +450,7 @@ class FunctionMaker(object): ...@@ -454,7 +450,7 @@ class FunctionMaker(object):
raise TypeError("Unknown output type: %s (%s)", type(output), output) raise TypeError("Unknown output type: %s (%s)", type(output), output)
def __init__(self, inputs, outputs, def __init__(self, inputs, outputs,
mode = mode_default, accept_inplace = False, function_builder = Function): mode = default_mode, accept_inplace = False, function_builder = Function):
""" """
:type inputs: a list of SymbolicInput instances :type inputs: a list of SymbolicInput instances
...@@ -644,7 +640,7 @@ def register_checker(checker): ...@@ -644,7 +640,7 @@ def register_checker(checker):
def function(inputs, outputs, mode=mode_default, accept_inplace = False): def function(inputs, outputs, mode=default_mode, accept_inplace = False):
""" """
Return a function calculating the outputs from the inputs. Return a function calculating the outputs from the inputs.
......
import numpy import numpy
import os
import scipy.sparse as sp import scipy.sparse as sp
from .. import gof from .. import gof
...@@ -144,16 +144,21 @@ class Mode(object): ...@@ -144,16 +144,21 @@ class Mode(object):
# If a string is passed as the mode argument in function or # If a string is passed as the mode argument in function or
# FunctionMaker, the Mode will be taken from this dictionary using the # FunctionMaker, the Mode will be taken from this dictionary using the
# string as the key # string as the key
FAST_COMPILE = Mode('py', 'fast_compile') FAST_COMPILE = Mode('py', 'fast_compile')
FAST_RUN = Mode('c|py', 'fast_run') FAST_RUN = Mode('c|py', 'fast_run')
SANITY_CHECK = [Mode('c|py', None), SANITY_CHECK = [Mode('c|py', None),
Mode('c|py', 'fast_run')] Mode('c|py', 'fast_run')]
predefined_modes = {'FAST_COMPILE': FAST_COMPILE, predefined_modes = {'FAST_COMPILE': FAST_COMPILE,
'FAST_RUN': FAST_RUN, 'FAST_RUN': FAST_RUN,
'SANITY_CHECK': SANITY_CHECK} 'SANITY_CHECK': SANITY_CHECK}
default_mode = 'FAST_COMPILE'
##
# The default mode used by functions and modules is read from the environment
# variable THEANO_DEFAULT_MODE. Unit tests will run using this value. If the env. var.
# is not set, it will default to 'FAST_RUN'
##
default_mode = os.getenv('THEANO_DEFAULT_MODE','FAST_RUN')
def register_mode(name, mode): def register_mode(name, mode):
"""Add a `Mode` which can be referred to by `name` in `function`.""" """Add a `Mode` which can be referred to by `name` in `function`."""
......
...@@ -7,6 +7,7 @@ from functools import partial ...@@ -7,6 +7,7 @@ from functools import partial
from copy import copy from copy import copy
import io import io
import function_module as F import function_module as F
from mode import default_mode
def join(*args): def join(*args):
...@@ -125,7 +126,7 @@ class Component(object): ...@@ -125,7 +126,7 @@ class Component(object):
""" """
raise NotImplementedError raise NotImplementedError
def make_no_init(self, mode=F.mode_default): def make_no_init(self, mode=default_mode):
""" """
Allocates the necessary containers using allocate() and uses Allocates the necessary containers using allocate() and uses
build() with the provided mode to make an instance which will build() with the provided mode to make an instance which will
...@@ -145,7 +146,7 @@ class Component(object): ...@@ -145,7 +146,7 @@ class Component(object):
arguments and the keyword arguments. If 'mode' is in the arguments and the keyword arguments. If 'mode' is in the
keyword arguments it will be passed to build(). keyword arguments it will be passed to build().
""" """
mode = kwargs.pop('mode', F.mode_default) mode = kwargs.pop('mode', default_mode)
rval = self.make_no_init(mode) rval = self.make_no_init(mode)
if hasattr(rval, 'initialize'): if hasattr(rval, 'initialize'):
rval.initialize(*args, **kwargs) rval.initialize(*args, **kwargs)
...@@ -958,7 +959,7 @@ class Module(ComponentDict): ...@@ -958,7 +959,7 @@ class Module(ComponentDict):
""" """
self.make_mi(args,kwargs) self.make_mi(args,kwargs)
mode = kwargs.pop('mode', F.mode_default) mode = kwargs.pop('mode', default_mode)
rval = self.make_no_init(mode) rval = self.make_no_init(mode)
if hasattr(rval, 'initialize'): if hasattr(rval, 'initialize'):
rval.initialize(*args, **kwargs) rval.initialize(*args, **kwargs)
...@@ -1011,10 +1012,3 @@ class KitComponent(Component): ...@@ -1011,10 +1012,3 @@ class KitComponent(Component):
def build(self, mode, memo): def build(self, mode, memo):
return [memo[i.result].value for i in self.kit.sinputs] return [memo[i.result].value for i in self.kit.sinputs]
...@@ -37,6 +37,13 @@ class Print(Op): ...@@ -37,6 +37,13 @@ class Print(Op):
def grad(self,input,output_gradients): def grad(self,input,output_gradients):
return output_gradients return output_gradients
def __eq__(self, other):
return type(self)==type(other) and self.message==other.message and self.attrs==other.attrs
def __hash__(self):
return hash(self.message) ^ hash(self.attrs)
class PrinterState(gof.utils.scratchpad): class PrinterState(gof.utils.scratchpad):
def __init__(self, props = {}, **more_props): def __init__(self, props = {}, **more_props):
......
...@@ -335,22 +335,21 @@ class test_structureddot(unittest.TestCase): ...@@ -335,22 +335,21 @@ class test_structureddot(unittest.TestCase):
return structured_dot(csc, images.T) return structured_dot(csc, images.T)
out = buildgraphCSC(kerns,images) out = buildgraphCSC(kerns,images)
for mode in 'FAST_COMPILE','FAST_RUN': f = theano.function([kerns,images], out)
f = theano.function([kerns,images], out, mode=mode) kernvals = spmat.data[:spmat.size]
kernvals = spmat.data[:spmat.size] imvals = 1.0 * numpy.arange(bsize*spmat.shape[1]).reshape(bsize,spmat.shape[1])
imvals = 1.0 * numpy.arange(bsize*spmat.shape[1]).reshape(bsize,spmat.shape[1]) outvals = f(kernvals,imvals)
outvals = f(kernvals,imvals) print type(spmat.dot(imvals.T))
print type(spmat.dot(imvals.T)) print spmat.dot(imvals.T)
print spmat.dot(imvals.T) print dir(spmat.dot(imvals.T))
print dir(spmat.dot(imvals.T))
# scipy 0.7.0 should already make the output dense
# scipy 0.7.0 should already make the output dense # assert numpy.all(outvals == spmat.dot(imvals.T).todense())
# assert numpy.all(outvals == spmat.dot(imvals.T).todense()) c = spmat.dot(imvals.T)
c = spmat.dot(imvals.T) assert _is_dense(c)
assert _is_dense(c) assert numpy.all(outvals == c)
assert numpy.all(outvals == c)
tensor.verify_grad(None, buildgraphCSC, [kernvals,imvals])
tensor.verify_grad(None, buildgraphCSC, [kernvals,imvals], mode=mode)
spmat = spmat.tocsr() spmat = spmat.tocsr()
def buildgraphCSR(kerns,images): def buildgraphCSR(kerns,images):
...@@ -358,19 +357,18 @@ class test_structureddot(unittest.TestCase): ...@@ -358,19 +357,18 @@ class test_structureddot(unittest.TestCase):
return structured_dot(csr, images.T) return structured_dot(csr, images.T)
out = buildgraphCSR(kerns,images) out = buildgraphCSR(kerns,images)
for mode in 'FAST_COMPILE','FAST_RUN': f = theano.function([kerns,images], out)
f = theano.function([kerns,images], out, mode=mode) kernvals = spmat.data[:spmat.size]
kernvals = spmat.data[:spmat.size] imvals = 1.0 * numpy.arange(bsize*spmat.shape[1]).reshape(bsize,spmat.shape[1])
imvals = 1.0 * numpy.arange(bsize*spmat.shape[1]).reshape(bsize,spmat.shape[1]) outvals = f(kernvals,imvals)
outvals = f(kernvals,imvals)
# scipy 0.7.0 should already make the output dense # scipy 0.7.0 should already make the output dense
# assert numpy.all(outvals == spmat.dot(imvals.T).todense()) # assert numpy.all(outvals == spmat.dot(imvals.T).todense())
c = spmat.dot(imvals.T) c = spmat.dot(imvals.T)
assert _is_dense(c) assert _is_dense(c)
assert numpy.all(outvals == c) assert numpy.all(outvals == c)
tensor.verify_grad(None, buildgraphCSR, [kernvals,imvals], mode=mode) tensor.verify_grad(None, buildgraphCSR, [kernvals,imvals])
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -11,7 +11,7 @@ _as_scalar = GemmLocalOptimizer._as_scalar ...@@ -11,7 +11,7 @@ _as_scalar = GemmLocalOptimizer._as_scalar
_is_real_matrix = GemmLocalOptimizer._is_real_matrix _is_real_matrix = GemmLocalOptimizer._is_real_matrix
from theano import In, Out from theano import In, Out
from .test_basic import (_approx_eq, as_tensor, function, from .test_basic import (_approx_eq, as_tensor, inplace_func,
compile, value, constant, inplace, eval_outputs) compile, value, constant, inplace, eval_outputs)
class t_gemm(TestCase): class t_gemm(TestCase):
...@@ -36,7 +36,7 @@ class t_gemm(TestCase): ...@@ -36,7 +36,7 @@ class t_gemm(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), mode=compile.Mode(optimizer = None, linker = l)) f = inplace_func([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)
...@@ -158,7 +158,7 @@ class t_gemm(TestCase): ...@@ -158,7 +158,7 @@ class t_gemm(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), mode = compile.Mode(optimizer = None, linker=l)) f = inplace_func([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, z_after - z)) self.failUnless(_approx_eq(z_after, z), (z_orig, z_after, z, z_after - z))
...@@ -256,11 +256,11 @@ class Warning(Exception): ...@@ -256,11 +256,11 @@ class Warning(Exception):
def just_gemm(i, o, ishapes = [(4,3), (3,5), (4,5), (), ()]): def just_gemm(i, o, ishapes = [(4,3), (3,5), (4,5), (), ()]):
try: try:
f = function([In(ii, mutable=True) for ii in i],o, mode='FAST_RUN') f = inplace_func([In(ii, mutable=True) for ii in i],o, mode='FAST_RUN')
for node in f.maker.env.nodes: for node in f.maker.env.nodes:
if node.op == T.dot: raise Warning('dot not changed to gemm in graph') if node.op == T.dot: raise Warning('dot not changed to gemm in graph')
if node.op == _dot22: raise Warning('_dot22 not changed to gemm in graph') if node.op == _dot22: raise Warning('_dot22 not changed to gemm in graph')
g = function(i, o, mode=compile.Mode(linker='py', optimizer=None)) g = inplace_func(i, o, mode=compile.Mode(linker='py', optimizer=None))
for node in g.maker.env.nodes: for node in g.maker.env.nodes:
if node.op == gemm: raise Exception('gemm in original graph') if node.op == gemm: raise Exception('gemm in original graph')
...@@ -320,11 +320,11 @@ def test_gemm_opt_double_gemm(): ...@@ -320,11 +320,11 @@ def test_gemm_opt_double_gemm():
i = [X,Y,Z,a,b, R, S, c] i = [X,Y,Z,a,b, R, S, c]
o = [a * T.dot(X,Y) + gemm(Z, b, S.T, R.T, 1.0)] o = [a * T.dot(X,Y) + gemm(Z, b, S.T, R.T, 1.0)]
try: try:
f = function([In(ii, mutable=True) for ii in i],o, mode='FAST_RUN') f = inplace_func([In(ii, mutable=True) for ii in i],o, mode='FAST_RUN')
for node in f.maker.env.nodes: for node in f.maker.env.nodes:
if node.op == T.dot: raise Failure('dot in graph') if node.op == T.dot: raise Failure('dot in graph')
if node.op == _dot22: raise Failure('_dot22 in graph') if node.op == _dot22: raise Failure('_dot22 in graph')
g = function(i, o, mode=compile.Mode(linker='py', optimizer=None)) g = inplace_func(i, o, mode=compile.Mode(linker='py', optimizer=None))
#for node in g.maker.env.nodes: #for node in g.maker.env.nodes:
# if node.op == gemm: raise Failure('gemm in graph') # if node.op == gemm: raise Failure('gemm in graph')
...@@ -379,11 +379,11 @@ def test_gemm_opt_vector_stuff(): ...@@ -379,11 +379,11 @@ def test_gemm_opt_vector_stuff():
X,Y,Z,a,b = T.dmatrix(), T.dmatrix(), T.dmatrix(), T.dscalar(), T.dscalar() X,Y,Z,a,b = T.dmatrix(), T.dmatrix(), T.dmatrix(), T.dscalar(), T.dscalar()
u,v = T.dvector(), T.dvector() u,v = T.dvector(), T.dvector()
f = function([a, u, v], a + T.dot(u,v), mode='FAST_RUN') f = inplace_func([a, u, v], a + T.dot(u,v), mode='FAST_RUN')
if gemm in [n.op for n in f.maker.env.nodes]: if gemm in [n.op for n in f.maker.env.nodes]:
raise Failure('gemm in graph') raise Failure('gemm in graph')
f = function([a, u, X,Y], a * u + T.dot(X,Y), mode='FAST_RUN') f = inplace_func([a, u, X,Y], a * u + T.dot(X,Y), mode='FAST_RUN')
if (gemm in [n.op for n in f.maker.env.nodes]): if (gemm in [n.op for n in f.maker.env.nodes]):
raise Failure('gemm in graph') raise Failure('gemm in graph')
...@@ -392,7 +392,7 @@ def test_inplace0(): ...@@ -392,7 +392,7 @@ def test_inplace0():
X,Y,Z,a,b = T.dmatrix(), T.dmatrix(), T.dmatrix(), T.dscalar(), T.dscalar() X,Y,Z,a,b = T.dmatrix(), T.dmatrix(), T.dmatrix(), T.dscalar(), T.dscalar()
R, S, c = T.dmatrix(), T.dmatrix(), T.dscalar() R, S, c = T.dmatrix(), T.dmatrix(), T.dscalar()
f = function([X,Y,Z,a,b, R, S, c], f = inplace_func([X,Y,Z,a,b, R, S, c],
[Z * (Z *c + a * T.dot(X,Y) + b * T.dot(R,S).T)], mode='FAST_RUN') [Z * (Z *c + a * T.dot(X,Y) + b * T.dot(R,S).T)], mode='FAST_RUN')
if (gemm in [n.op for n in f.maker.env.nodes]): if (gemm in [n.op for n in f.maker.env.nodes]):
raise Failure('gemm in graph') raise Failure('gemm in graph')
...@@ -400,7 +400,7 @@ def test_inplace0(): ...@@ -400,7 +400,7 @@ def test_inplace0():
def test_inplace1(): def test_inplace1():
X,Y,Z,a,b = XYZab() X,Y,Z,a,b = XYZab()
# with > 2 terms in the overall addition # with > 2 terms in the overall addition
f = function([X,Y,Z,a,b], f = inplace_func([X,Y,Z,a,b],
[Z + Z + T.dot(X,Y)], mode='FAST_RUN') [Z + Z + T.dot(X,Y)], mode='FAST_RUN')
if (gemm in [n.op for n in f.maker.env.nodes]): if (gemm in [n.op for n in f.maker.env.nodes]):
raise Failure('gemm in graph') raise Failure('gemm in graph')
......
...@@ -73,7 +73,7 @@ def test_merge_opt_runtime(): ...@@ -73,7 +73,7 @@ def test_merge_opt_runtime():
else: else:
r = x r = x
t = time.time() t = time.time()
f = theano.function([x], r,mode='FAST_COMPILE') f = theano.function([x], r)
dt = time.time() - t dt = time.time() - t
assert dt < 5.0 #it should never take longer than 5 seconds to compile this graph assert dt < 5.0 #it should never take longer than 5 seconds to compile this graph
...@@ -5,6 +5,7 @@ from theano import tensor as T ...@@ -5,6 +5,7 @@ from theano import tensor as T
from theano.tensor import nnet as NN from theano.tensor import nnet as NN
import numpy as N import numpy as N
from theano.compile import module from theano.compile import module
from theano.compile.mode import default_mode
from theano import tensor as T, sparse as S from theano import tensor as T, sparse as S
import numpy as N import numpy as N
import sys import sys
...@@ -482,15 +483,17 @@ def create_realistic(window_size=3,#7, ...@@ -482,15 +483,17 @@ def create_realistic(window_size=3,#7,
model = architecture.make(input_size=input_dimension, input_representation_size=token_representation_size, hidden_representation_size=concatenated_representation_size, output_size=output_vocabsize, lr=lr, seed=seed, noise_level=noise_level, qfilter_relscale=qfilter_relscale, mode=compile_mode) model = architecture.make(input_size=input_dimension, input_representation_size=token_representation_size, hidden_representation_size=concatenated_representation_size, output_size=output_vocabsize, lr=lr, seed=seed, noise_level=noise_level, qfilter_relscale=qfilter_relscale, mode=compile_mode)
return model return model
def test_naacl_model(optimizer='fast_run', iters_per_unsup=10, iters_per_sup=10, def test_naacl_model(iters_per_unsup=10, iters_per_sup=10,
realistic=False): optimizer=None, realistic=False):
print "BUILDING MODEL" print "BUILDING MODEL"
import time import time
t = time.time() t = time.time()
mode = theano.Mode(linker='c|py', optimizer=optimizer) if optimizer else default_mode
if realistic: if realistic:
m = create_realistic(compile_mode = theano.Mode(linker='c|py', optimizer=optimizer)) m = create_realistic(compile_mode=mode)
else: else:
m = create(compile_mode = theano.Mode(linker='c|py', optimizer=optimizer)) m = create(compile_mode=mode)
print 'BUILD took', time.time() - t print 'BUILD took', time.time() - t
prog_str = [] prog_str = []
......
...@@ -136,7 +136,7 @@ class test_greedy_distribute(unittest.TestCase): ...@@ -136,7 +136,7 @@ class test_greedy_distribute(unittest.TestCase):
, eps + y/s , eps + y/s
, s) , s)
f = function([s, eps, x,y], r**2, mode=DebugMode()) f = function([s, eps, x,y], r**2)
r0 = f(4,1.e-6, [1.5,2], [2.3,3.1]) r0 = f(4,1.e-6, [1.5,2], [2.3,3.1])
r1 = f(4,1.e-6, [1.5,2], [2.3,3.1]) r1 = f(4,1.e-6, [1.5,2], [2.3,3.1])
......
...@@ -13,7 +13,7 @@ class T_test_module(unittest.TestCase): ...@@ -13,7 +13,7 @@ class T_test_module(unittest.TestCase):
def test_state_propagation(self): def test_state_propagation(self):
x = tensor.vector() x = tensor.vector()
rk = RandomKit('rk', 1000) rk = RandomKit('rk', 1000)
f = compile.function([x, (rk, [gof.Container(r = gof.generic, storage = [123], name='bla')])], rk.binomial(tensor.shape(x)), mode='FAST_COMPILE') f = compile.function([x, (rk, [gof.Container(r = gof.generic, storage = [123], name='bla')])], rk.binomial(tensor.shape(x)))
f['rk'] = 9873456 f['rk'] = 9873456
rvals = [f([1,2,3,4,6, 7, 8]) for i in xrange(5)] rvals = [f([1,2,3,4,6, 7, 8]) for i in xrange(5)]
...@@ -45,7 +45,7 @@ class T_test_module(unittest.TestCase): ...@@ -45,7 +45,7 @@ class T_test_module(unittest.TestCase):
self.f = compile.Method([self.b.x], self.b.r) self.f = compile.Method([self.b.x], self.b.r)
b = E() b = E()
m = b.make(mode='FAST_COMPILE') m = b.make()
m.seed(1000) m.seed(1000)
#print m.f(N.ones(5)) #print m.f(N.ones(5))
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论