開発環境
- macOS Sierra - Apple (OS)
- Emacs (Text Editor)
- Python 3.6 (プログラミング言語)
行列プログラマー(Philip N. Klein (著)、 松田 晃一 (翻訳)、 弓林 司 (翻訳)、 脇本 佑紀 (翻訳)、 中田 洋 (翻訳)、 齋藤 大吾 (翻訳)、オライリージャパン)の2章(ベクトル)、2.14(問題)、Vec クラス用のプロシージャを書く、問題 2.14.10 を取り組んでみる。
問題 2.14.10
コード(Emacs)
# Copyright 2013 Philip N. Klein def getitem(v, k): """ Return the value of entry k in v. Be sure getitem(v,k) returns 0 if k is not represented in v.f. >>> v = Vec({'a','b','c', 'd'},{'a':2,'c':1,'d':3}) >>> v['d'] 3 >>> v['b'] 0 """ return v.f.get(k, 0) def setitem(v, k, val): """ Set the element of v with label d to be val. setitem(v,d,val) should set the value for key d even if d is not previously represented in v.f, and even if val is 0. >>> v = Vec({'a', 'b', 'c'}, {'b':0}) >>> v['b'] = 5 >>> v['b'] 5 >>> v['a'] = 1 >>> v['a'] 1 >>> v['a'] = 0 >>> v['a'] 0 """ assert k in v.D v.f[k] = val def equal(u, v): """ Return true iff u is equal to v. Because of sparse representation, it is not enough to compare dictionaries Consider using brackets notation u[...] and v[...] in your procedure to access entries of the input vectors. This avoids some sparsity bugs. >>> Vec({'a', 'b', 'c'}, {'a':0}) == Vec({'a', 'b', 'c'}, {'b':0}) True >>> Vec({'a', 'b', 'c'}, {'a': 0}) == Vec({'a', 'b', 'c'}, {}) True >>> Vec({'a', 'b', 'c'}, {}) == Vec({'a', 'b', 'c'}, {'a': 0}) True Be sure that equal(u, v) checks equalities for all keys from u.f and v.f even if some keys in u.f do not exist in v.f (or vice versa) >>> Vec({'x','y','z'},{'y':1,'x':2}) == Vec({'x','y','z'},{'y':1,'z':0}) False >>> Vec({'a','b','c'}, {'a':0,'c':1}) == Vec({'a','b','c'}, {'a':0,'c':1,'b':4}) False >>> Vec({'a','b','c'}, {'a':0,'c':1,'b':4}) == Vec({'a','b','c'}, {'a':0,'c':1}) False The keys matter: >>> Vec({'a','b'},{'a':1}) == Vec({'a','b'},{'b':1}) False The values matter: >>> Vec({'a','b'},{'a':1}) == Vec({'a','b'},{'a':2}) False """ assert u.D == v.D return all([getitem(u, k) == getitem(v, k) for k in u.D]) def add(u, v): """ Returns the sum of the two vectors. Consider using brackets notation u[...] and v[...] in your procedure to access entries of the input vectors. This avoids some sparsity bugs. Do not seek to create more sparsity than exists in the two input vectors. Doing so will unnecessarily complicate your code and will hurt performance. Make sure to add together values for all keys from u.f and v.f even if some keys in u.f do not exist in v.f (or vice versa) >>> a = Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2}) >>> b = Vec({'a','e','i','o','u'}, {'o':4,'u':7}) >>> c = Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2,'o':4,'u':7}) >>> a + b == c True >>> a == Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2}) True >>> b == Vec({'a','e','i','o','u'}, {'o':4,'u':7}) True >>> d = Vec({'x','y','z'}, {'x':2,'y':1}) >>> e = Vec({'x','y','z'}, {'z':4,'y':-1}) >>> f = Vec({'x','y','z'}, {'x':2,'y':0,'z':4}) >>> d + e == f True >>> d == Vec({'x','y','z'}, {'x':2,'y':1}) True >>> e == Vec({'x','y','z'}, {'z':4,'y':-1}) True >>> b + Vec({'a','e','i','o','u'}, {}) == b True """ assert u.D == v.D D = u.D | v.D return Vec(D, {d: getitem(u, d) + getitem(v, d) for d in D if (getitem(u, d) + getitem(v, d)) != 0}) def dot(u, v): """ Returns the dot product of the two vectors. Consider using brackets notation u[...] and v[...] in your procedure to access entries of the input vectors. This avoids some sparsity bugs. >>> u1 = Vec({'a','b'}, {'a':1, 'b':2}) >>> u2 = Vec({'a','b'}, {'b':2, 'a':1}) >>> u1*u2 5 >>> u1 == Vec({'a','b'}, {'a':1, 'b':2}) True >>> u2 == Vec({'a','b'}, {'b':2, 'a':1}) True >>> v1 = Vec({'p','q','r','s'}, {'p':2,'s':3,'q':-1,'r':0}) >>> v2 = Vec({'p','q','r','s'}, {'p':-2,'r':5}) >>> v1*v2 -4 >>> w1 = Vec({'a','b','c'}, {'a':2,'b':3,'c':4}) >>> w2 = Vec({'a','b','c'}, {'a':12,'b':8,'c':6}) >>> w1*w2 72 The pairwise products should not be collected in a set before summing because a set eliminates duplicates >>> v1 = Vec({1, 2}, {1 : 3, 2 : 6}) >>> v2 = Vec({1, 2}, {1 : 2, 2 : 1}) >>> v1 * v2 12 """ assert u.D == v.D return sum([getitem(u, d) * getitem(v, d) for d in u.D | v.D]) def scalar_mul(v, alpha): """ Returns the scalar-vector product alpha times v. Consider using brackets notation v[...] in your procedure to access entries of the input vector. This avoids some sparsity bugs. >>> zero = Vec({'x','y','z','w'}, {}) >>> u = Vec({'x','y','z','w'},{'x':1,'y':2,'z':3,'w':4}) >>> 0*u == zero True >>> 1*u == u True >>> 0.5*u == Vec({'x','y','z','w'},{'x':0.5,'y':1,'z':1.5,'w':2}) True >>> u == Vec({'x','y','z','w'},{'x':1,'y':2,'z':3,'w':4}) True """ return Vec(v.D, {d: x * alpha for d, x in v.f.items()}) def neg(v): """ Returns the negation of a vector. Consider using brackets notation v[...] in your procedure to access entries of the input vector. This avoids some sparsity bugs. >>> u = Vec({1,3,5,7},{1:1,3:2,5:3,7:4}) >>> -u Vec({1, 3, 5, 7},{1: -1, 3: -2, 5: -3, 7: -4}) >>> u == Vec({1,3,5,7},{1:1,3:2,5:3,7:4}) True >>> -Vec({'a','b','c'}, {'a':1}) == Vec({'a','b','c'}, {'a':-1}) True """ return Vec(v.D, {d: -1 * getitem(v, d) for d in v.D}) ########################################################################## class Vec: """ A vector has two fields: D - the domain (a set) f - a dictionary mapping (some) domain elements to field elements elements of D not appearing in f are implicitly mapped to zero """ def __init__(self, labels, function): assert isinstance(labels, set) assert isinstance(function, dict) self.D = labels self.f = function __getitem__ = getitem __setitem__ = setitem __neg__ = neg __rmul__ = scalar_mul # if left arg of * is primitive, assume it's a scalar def __mul__(self, other): # If other is a vector, returns the dot product of self and other if isinstance(other, Vec): return dot(self, other) else: # Will cause other.__rmul__(self) to be invoked return NotImplemented def __truediv__(self, other): # Scalar division return (1 / other) * self __add__ = add def __radd__(self, other): "Hack to allow sum(...) to work with vectors" if other == 0: return self def __sub__(a, b): "Returns a vector which is the difference of a and b." return a + (-b) __eq__ = equal def is_almost_zero(self): s = 0 for x in self.f.values(): if isinstance(x, int) or isinstance(x, float): s += x * x elif isinstance(x, complex): y = abs(x) s += y * y else: return False return s < 1e-20 def __str__(v): "pretty-printing" D_list = sorted(v.D, key=repr) numdec = 3 wd = dict([(k, (1 + max(len(str(k)), len('{0:.{1}G}'.format(v[k], numdec))))) if isinstance( v[k], int) or isinstance(v[k], float) else (k, (1 + max(len(str(k)), len(str(v[k]))))) for k in D_list]) s1 = ''.join(['{0:>{1}}'.format(str(k), wd[k]) for k in D_list]) s2 = ''.join(['{0:>{1}.{2}G}'.format(v[k], wd[k], numdec) if isinstance( v[k], int) or isinstance(v[k], float) else '{0:>{1}}'.format(v[k], wd[k]) for k in D_list]) return "\n" + s1 + "\n" + '-' * sum(wd.values()) + "\n" + s2 def __hash__(self): "Here we pretend Vecs are immutable so we can form sets of them" h = hash(frozenset(self.D)) for k, v in sorted(self.f.items(), key=lambda x: repr(x[0])): if v != 0: h = hash((h, hash(v))) return h def __repr__(self): return "Vec(" + str(self.D) + "," + str(self.f) + ")" def copy(self): "Don't make a new copy of the domain D" return Vec(self.D, self.f.copy()) def __iter__(self): raise TypeError('%r object is not iterable' % self.__class__.__name__)
入出力結果(Terminal, IPython)
$ python3 -m doctest vec.py -v Trying: a = Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2}) Expecting nothing ok Trying: b = Vec({'a','e','i','o','u'}, {'o':4,'u':7}) Expecting nothing ok Trying: c = Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2,'o':4,'u':7}) Expecting nothing ok Trying: a + b == c Expecting: True ok Trying: a == Vec({'a','e','i','o','u'}, {'a':0,'e':1,'i':2}) Expecting: True ok Trying: b == Vec({'a','e','i','o','u'}, {'o':4,'u':7}) Expecting: True ok Trying: d = Vec({'x','y','z'}, {'x':2,'y':1}) Expecting nothing ok Trying: e = Vec({'x','y','z'}, {'z':4,'y':-1}) Expecting nothing ok Trying: f = Vec({'x','y','z'}, {'x':2,'y':0,'z':4}) Expecting nothing ok Trying: d + e == f Expecting: True ok Trying: d == Vec({'x','y','z'}, {'x':2,'y':1}) Expecting: True ok Trying: e == Vec({'x','y','z'}, {'z':4,'y':-1}) Expecting: True ok Trying: b + Vec({'a','e','i','o','u'}, {}) == b Expecting: True ok Trying: u1 = Vec({'a','b'}, {'a':1, 'b':2}) Expecting nothing ok Trying: u2 = Vec({'a','b'}, {'b':2, 'a':1}) Expecting nothing ok Trying: u1*u2 Expecting: 5 ok Trying: u1 == Vec({'a','b'}, {'a':1, 'b':2}) Expecting: True ok Trying: u2 == Vec({'a','b'}, {'b':2, 'a':1}) Expecting: True ok Trying: v1 = Vec({'p','q','r','s'}, {'p':2,'s':3,'q':-1,'r':0}) Expecting nothing ok Trying: v2 = Vec({'p','q','r','s'}, {'p':-2,'r':5}) Expecting nothing ok Trying: v1*v2 Expecting: -4 ok Trying: w1 = Vec({'a','b','c'}, {'a':2,'b':3,'c':4}) Expecting nothing ok Trying: w2 = Vec({'a','b','c'}, {'a':12,'b':8,'c':6}) Expecting nothing ok Trying: w1*w2 Expecting: 72 ok Trying: v1 = Vec({1, 2}, {1 : 3, 2 : 6}) Expecting nothing ok Trying: v2 = Vec({1, 2}, {1 : 2, 2 : 1}) Expecting nothing ok Trying: v1 * v2 Expecting: 12 ok Trying: Vec({'a', 'b', 'c'}, {'a':0}) == Vec({'a', 'b', 'c'}, {'b':0}) Expecting: True ok Trying: Vec({'a', 'b', 'c'}, {'a': 0}) == Vec({'a', 'b', 'c'}, {}) Expecting: True ok Trying: Vec({'a', 'b', 'c'}, {}) == Vec({'a', 'b', 'c'}, {'a': 0}) Expecting: True ok Trying: Vec({'x','y','z'},{'y':1,'x':2}) == Vec({'x','y','z'},{'y':1,'z':0}) Expecting: False ok Trying: Vec({'a','b','c'}, {'a':0,'c':1}) == Vec({'a','b','c'}, {'a':0,'c':1,'b':4}) Expecting: False ok Trying: Vec({'a','b','c'}, {'a':0,'c':1,'b':4}) == Vec({'a','b','c'}, {'a':0,'c':1}) Expecting: False ok Trying: Vec({'a','b'},{'a':1}) == Vec({'a','b'},{'b':1}) Expecting: False ok Trying: Vec({'a','b'},{'a':1}) == Vec({'a','b'},{'a':2}) Expecting: False ok Trying: v = Vec({'a','b','c', 'd'},{'a':2,'c':1,'d':3}) Expecting nothing ok Trying: v['d'] Expecting: 3 ok Trying: v['b'] Expecting: 0 ok Trying: u = Vec({1,3,5,7},{1:1,3:2,5:3,7:4}) Expecting nothing ok Trying: -u Expecting: Vec({1, 3, 5, 7},{1: -1, 3: -2, 5: -3, 7: -4}) ok Trying: u == Vec({1,3,5,7},{1:1,3:2,5:3,7:4}) Expecting: True ok Trying: -Vec({'a','b','c'}, {'a':1}) == Vec({'a','b','c'}, {'a':-1}) Expecting: True ok Trying: zero = Vec({'x','y','z','w'}, {}) Expecting nothing ok Trying: u = Vec({'x','y','z','w'},{'x':1,'y':2,'z':3,'w':4}) Expecting nothing ok Trying: 0*u == zero Expecting: True ok Trying: 1*u == u Expecting: True ok Trying: 0.5*u == Vec({'x','y','z','w'},{'x':0.5,'y':1,'z':1.5,'w':2}) Expecting: True ok Trying: u == Vec({'x','y','z','w'},{'x':1,'y':2,'z':3,'w':4}) Expecting: True ok Trying: v = Vec({'a', 'b', 'c'}, {'b':0}) Expecting nothing ok Trying: v['b'] = 5 Expecting nothing ok Trying: v['b'] Expecting: 5 ok Trying: v['a'] = 1 Expecting nothing ok Trying: v['a'] Expecting: 1 ok Trying: v['a'] = 0 Expecting nothing ok Trying: v['a'] Expecting: 0 ok 13 items had no tests: vec vec.Vec vec.Vec.__hash__ vec.Vec.__init__ vec.Vec.__iter__ vec.Vec.__mul__ vec.Vec.__radd__ vec.Vec.__repr__ vec.Vec.__str__ vec.Vec.__sub__ vec.Vec.__truediv__ vec.Vec.copy vec.Vec.is_almost_zero 7 items passed all tests: 13 tests in vec.add 14 tests in vec.dot 8 tests in vec.equal 3 tests in vec.getitem 4 tests in vec.neg 6 tests in vec.scalar_mul 7 tests in vec.setitem 55 tests in 20 items. 55 passed and 0 failed. Test passed. $
0 コメント:
コメントを投稿