Block iterator(matrix 編1) - Rubyist の Python 学習記録(13)

PythonLibrary Python NumPy 2019年 1月 16日

ブロックベースのイテレータのテスト(matrix 版)

  • generate_with_block_trans のテストに行列のテストを追記する. obj.generate_with_block_trans は index とブロックサイズを渡し,index にしたがってブロックの入れ替えを行う. 今回は,ブロックサイズを2行3列としている.
  • with such.A('MonoImage class') as it:
        @it.should('exec block scramble')
        def test():
            (中略)
            ### matrix test
            org_mat = MonoImage(np.arange(0, 35).reshape(5, 7), 8)
            correct_mat = MonoImage.create_with_array(
                    [
                        [14, 15, 16,  0,  1,  2,  6],
                        [21, 22, 23,  7,  8,  9, 13],
                        [17, 18, 19,  3,  4,  5, 20],
                        [24, 25, 26, 10, 11, 12, 27],
                        [28, 29, 30, 31, 32, 33, 34],
                        ], np.int, 8)
            ans_mat = org_mat.generate_with_block_trans(index, 2, 3)
            it.assertEqual(ans_mat, correct_mat)
            rev_mat = ans_mat.generate_with_reverse_block_trans(index, 2, 3)
            it.assertEqual(rev_mat, org_mat)

generate_with_block_trans の実装の修正

  • generate_with_block_trans に matrix の実装を追加する. ベクトルと異なり br, bc のサイズの行列を setter property に渡している.
  • class MonoImage:
        def generate_with_block_trans(self, index, br, bc):
            ans = self.generate_with_copy()
            if self.ndim == 1:
                (中略)
            else:
                nx = int(self.width / bc)
                ny = int(self.height / br)
                for (pi, bi) in zip(index.pixel_iterator(), ans.block_iterator(br, bc, br, bc, False)):
                    v = pi.value
                    vr = int(v / nx) * br
                    vc = (v % nx) * bc
                    bi.value = self._array[vr:vr+br, vc:vc+bc]
            return ans

MonoImageBlockIterator への追加・修正

  • __init__ の _f, _getter, _setter に matrix 系のメソッドを追記する
  • self._f = self.vector_iterator_func if mono_image.ndim == 1 else self.matrix_iterator_func
    self._getter = self.vector_getter_func if mono_image.ndim == 1 else self.matrix_getter_func
    self._setter = self.vector_setter_func if mono_image.ndim == 1 else self.matrix_setter_func
  • 参照している matrix_iterator_func を実装する
  • class MonoImageBlockIterator:
        def matrix_iterator_func(self):
            self._nowr = 0
            self._nowc = 0
            while (self._nowr != -1):
                remain_r = self._height - self._nowr
                remain_c = self._width - self._nowc
                self._lenr = self._br if remain_r > self._br else remain_r
                self._lenc = self._bc if remain_c > self._bc else remain_c
                if (self._lenr == self._br and self._lenc == self._bc) or self._is_small_ok:
                    yield self
                self._nowc += self._sc
                if self._nowc >= self._width:
                    self._nowc = 0
                    self._nowr += self._sr
                    if self._nowr >= self._height:
                        self._nowr = -1
  • 同様に matrix_getter_func も実装する.
  • def matrix_getter_func(self):
        return self._mono_image._array[self._nowr:self._nowr+self._lenr, self._nowc:self._nowc+self._lenc]
  • 同様に matrix_setter_func も実装する.
  • def matrix_setter_func(self, nda):
        self._mono_image._array[self._nowr:self._nowr+self._lenr, self._nowc:self._nowc+self._lenc] = nda
  • これでテストが全て通過した.

端数がない場合のテスト

  • ベクトルの場合と同様,念のため端数が存在しない場合にもうまく動作することを確認するために,長さを 8 に変更した場合のテストも追加しておく. np.delete で org_mat と correct_mat の最後の列要素および行要素を削る. これによって,両行列とも大きさが 4 行 6 列 (ブロックサイズ 2 行 3 列 の倍数)となり,端数がなくなる.
  • ### matrix test (without edge)
    np.delete(org_mat._array, -1, -1)
    np.delete(correct_mat._array, -1, -1)
    ans_mat = org_mat.generate_with_block_trans(index, 2, 3)
    it.assertEqual(ans_mat, correct_mat)
    rev_mat = ans_mat.generate_with_reverse_block_trans(index, 2, 3)
    it.assertEqual(rev_mat, org_mat)
  • テストを追加しても,テストはそのまま成功した.これで行列のブロックスクランブルのテストは終わりにする.

index の数が一致しない時の例外のテスト

  • さらにベクトルの時と同様に index の数とブロック数が一致しないときには例外を出したい. さらに行列の大きさを 5 行 3 列 に減らすと,index の長さ 4 と一致しなくなる. このときに例外が発生するかどうかをテストする.
  • ### raise exception when index size is wrong
    org_mat.delete_self(-1, 0).delete_self(-1, 1)
    with it.assertRaises(IndexError):
        org_mat.generate_with_block_trans(index, 2, 3)
  • すでに例外処理を追加しているので,このテストはそのまま動作する.

長くなったので今日はここまで.