8.1. Arrayクラス

ArrayクラスはPyOpenCLが独自に(つまりOpenCL規格とは独立して)定義する配列データのコンテナです。

クラスのコンストラクタは以下のように宣言されています。

class pyopencl.array.Array(
    cq, #(1)
    shape, #(2)
    dtype, #(3)
    order='C',
    allocator=None,
    data=None,
    offset=0,
    strides=None,
    events=None)

(1)

OpenCLコンテキストまたはコマンドキューオブジェクトを指定。

(2)

次元、要素数などのshapeパラメータを設定。

(3)

配列要素のデータ型を指定。

8.1.1. Arrayの属性

Arrayクラスの属性はnumpyから派生したものか、OpenCLのバックグラウンドオブジェクトとの連携に使うものに分けられます。

OpenCL関連で最重要な属性はdataです。data属性はOpenCL Bufferに変換したオブジェクトを戻すことににって、OpenCL規格に基づいたPyOpenCLランタイムライブラリとの連携が可能となります。

data
Arrayの裏で生成されているpyopencl.MemoryObjectのインスタンスです。pyopencl.Bufferとして使用可能です。

numpy系統の属性は以下のものがあります。

shape
配列の次元の要素数を定義するタプル(tuple)です。numpyの多次元配列と同じものと考えて支障ないかと思います。
ndim
shapeの次元数です。
dtype
配列内要素のnumpy.dtypeです。
size
配列内の要素数です。shapeの要素数をかけることにより算出可能です。
nbytes
配列全体のバイト数です。dtype.itemsize*sizeで算出できます。

8.1.2. Arrayのメソッド

get

getはデバイスが管理するメモリ空間からホストメモリにnumpy.ndarrayとしてコピーします。

get(queue=None, ary=None, async=False)

引数は空でも構いません。

set

setはArrayクラス内に配列を設定します。

set(ary, queue=None, async=False)

実行するとnumpy.ndarryオブジェクトの中身をデバイスメモリに移動します。

以下は実装例とその出力です。

CLSetArrayTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.arange(5).astype(np.uint32)

a_mem = clarr.Array(queue, (5,), dtype=np.uint32)
a_mem.set(a)

print(a_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLSetArrayTest.py
[0 1 2 3 4]

部分配列

Arrayクラスはnumpy配列と同じように部分配列にアクセスすることができます。

以下は実装例とその出力です。

CLArraySubSeqTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.array([10, 20, 30, 40, 50,
              60, 70, 80, 90, 100]).astype(np.float32)


platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)

print(a_mem[4:9].get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLArraySubSeqTest.py
[ 5.  6.  7.  8.  9.]

view

viewは配列をそのままのデータで戻します。ただしdtypeに異なる型を指定した場合に、各要素のバイトから配列を再構成します。

view(dtype=None)

引数はデータの再解釈をするdtypeを指定します。

以下は実装例とその出力です。

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import pyopencl.tools as cltool
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.uint32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.uint32)

a_mem = clarr.to_device(queue, a)

a_view = a_mem.view()

print(a_view)

a.resize((5, 2))

new_mem = clarr.to_device(queue, a)

print(new_mem)

new_view_16 = new_mem.view(np.uint8)

print(new_view_16)

出力. 

[ 1  2  3  4  5  6  7  8  9 10]
[[ 1  2]
 [ 3  4]
 [ 5  6]
 [ 7  8]
 [ 9 10]]
[[ 1  0  0  0  2  0  0  0]
 [ 3  0  0  0  4  0  0  0]
 [ 5  0  0  0  6  0  0  0]
 [ 7  0  0  0  8  0  0  0]
 [ 9  0  0  0 10  0  0  0]]

reshape

新たなshapeを持った配列を戻します。

reshape(*shape, **kwargs)

基本的な動作はnumpyと同様と考えて支障ないかと思います。

以下は実装例とその出力です。

CLReshapeTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.arange(10).reshape(5, 2).astype(np.uint32)

print(a)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)
a_mem = clarr.to_device(queue, a)

new_mem = a_mem.reshape(1, 10)

a_mem.finish()

print(new_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLReshapeTest.py
[[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
[[0 1 2 3 4 5 6 7 8 9]]

transpose

transposeは配列の転置ツールです。

transpose(axes=None)

配列を転置し、転置した配列を戻します。axesは整数型のリストで指定すると、これを元に転置をします。オプションですので指定する必要はありません。

以下は実装例とその出力です。

CLTransposeTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
from pyopencl.clrandom import rand as clrnd
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.arange(8).astype(np.uint32).reshape((4, 2))

print(a)

a_gpu = clarr.to_device(queue, a)

print(a_gpu.transpose().get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLTransposeTest.py
[[0 1]
 [2 3]
 [4 5]
 [6 7]]
[[0 2 4 6]
 [1 3 5 7]]

fill

配列をスカラー型の要素で埋めます。

fill(value, queue=None, wait_for=None)

valueにはスカラー型の値を指定します。値は元データと同じdtypeであるものとします。

以下が実装例と出力となります。

CLFillTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)


a_gpu = clarr.Array(queue, (10,), dtype=np.float32)
a_gpu.fill(1.0)
a = a_gpu.get()

print(a)

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLFillTest.py
[ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]

ravel

ravelは多次元配列のレベルを削減する場合に使います。

ravel()

配列を平らにしたデータを戻します。

実装例は以下のようになります。

CLRavelTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
from pyopencl.clrandom import rand as clrnd
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.arange(8).astype(np.uint32).reshape(4, 2)
print(a)

a_mem = clarr.to_device(queue, a)

print(a_mem.ravel().get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLRavelTest.py
[[0 1]
 [2 3]
 [4 5]
 [6 7]]
[0 1 2 3 4 5 6 7]

setitem

デバイスメモリ内の各要素にデータを指定するメソッドです。

setitem(subscript, value, queue=None, wait_for=None)

subscriptは配列の添字、valueは値を指定します。

CLSetItemTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.arange(5).astype(np.uint32)

a_mem = clarr.Array(queue, (5,), dtype=np.uint32)
a_mem.setitem(4, 15)

print(a_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLSetItemTest.py
[ 0  0  0  0 15]

minとmax

Arrayは最大値と最小値関数を持ちます。

  • min
  • max
  • subset_max
  • subset_min

subsetが付く関数は、添字を格納した配列で指定した範囲での最小・最大値を取得します。

実装例は以下のようになります。

CLMinMaxTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.arange(33, 99, 3).astype(np.float32)
b = np.array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).astype(np.uint32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    a_mem = clarr.to_device(queue, a)
    overall_max_a = clarr.max(a_mem)
print(overall_max_a.get())

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    max_a = clarr.subset_max(b_mem, a_mem)
print(max_a.get())

c = np.array([5, 10, 15]).astype(np.uint32)
c_mem = clarr.to_device(queue, c)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    min_a = clarr.subset_min(c_mem, a_mem)
print(min_a.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLMinMaxTest.py
96.0
63.0
48.0

NaN

numpy.isnanはArrayに対して適用可能です。

以下が実装例となります。

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, float('nan'), float('nan'), 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

print((np.isnan(a_mem.get())).any())
print((np.isnan(b_mem.get())).any())

operator

PythonのoperatorモジュールをArrayに対して適用することができます。

以下が実装例となります。

CLOperatorTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import operator as op

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.arange(10).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

eqtest = op.eq(a_mem, b_mem)

print(eqtest)

getest = op.ge(a_mem, b_mem)

print(getest)

gttest = op.gt(a_mem, b_mem)

print(gttest)

letest = op.le(a_mem, b_mem)

print(letest)

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLOperatorTest.py
[0 0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[0 0 0 0 0 0 0 0 0 0]

reverse

reverse関数は配列の順序を逆転させます。操作としては、Arrayのインスタンスからreverseと呼び出すだけです。

a_mem = a_mem.reverse()

以下が実装例となります。

CLReverseTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.arange(10).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, b)

a_mem = a_mem.reverse()

print(a_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLReverseTest.py
[ 9.  8.  7.  6.  5.  4.  3.  2.  1.  0.]

take

[a[indices[0]], …, a[indices[n]]]という形態を持つArrayを戻します。

pyopencl.array.take(a, indices, out=None, queue=None, wait_for=None)

indices自体がaの添字として機能します。

実装例は以下のようになります。

CLTakeTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.arange(100).astype(np.float32)
b = np.arange(0, 36, 2).astype(np.int32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

result = clarr.take(a_mem, b_mem)

print(result.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLTakeTest.py
[  0.   2.   4.   6.   8.  10.  12.  14.  16.  18.  20.  22.  24.  26.  28.
  30.  32.  34.]

if_positive

if_positiveはpyopencl.arrayの関数で、配列aの添字iの値が正の場合は配列bの添字iの値、負の場合は配列cの添字iの値を戻すような操作を行えます。

実装例は以下のようになります。

CLIfPositiveTest.py. 

import pyopencl.array as clarr
import pyopencl as cl
import numpy as np

ctx = cl.Context(cl.get_platforms()[0].get_devices(cl.device_type.GPU))
queue = cl.CommandQueue(ctx)

a = np.array((1, 5, 3, -2, 2, 2, -3, 2, -8, 9)).astype(np.int32)
b = np.arange(10).astype(np.int32)
c = np.arange(10,20).astype(np.int32)

print(a)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)
c_mem = clarr.to_device(queue, c)

d = clarr.if_positive(a_mem, then_=b_mem, else_=c_mem)

print(b_mem.get())
print(c_mem.get())
print(d)

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLIfPositiveTest.py
[ 1  5  3 -2  2  2 -3  2 -8  9]
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[ 0  1  2 13  4  5 16  7 18  9]

concatenate

numpy.concatenate関数に対応するのが、pyopencl.array.concatenateです。

pyopencl.array.concatenate(
    arrays,
    axis=0,
    queue=None,
    allocator=None)

arrays引数で指定した配列を結合します。

以下が実装例となります。

CLConcatenateTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

result_mem = clarr.concatenate((a_mem, b_mem))

print(result_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLConcatenateTest.py
[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  10.   9.   8.   7.   6.
   5.   4.   3.   2.   1.]

diff

差分計算関数もnumpyと同様にpyopencl.arrayは提供しています。

以下が実装例となります。

CLDiffTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.array([10, 8, 12, 15, 16, 15, 1, 2, 2, 1]).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

diff_a_mem = clarr.diff(a_mem)
diff_b_mem = clarr.diff(b_mem)

print(diff_a_mem.get())
print(diff_b_mem.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLDiffTest.py
[ 1.  1.  1.  1.  1.  1.  1.  1.  1.]
[ -2.   4.   3.   1.  -1. -14.   1.   0.  -1.]

dot

pyopencl.arrayライブラリはドット積関数についても提供しています。

以下が実装例となります。

CLDotTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import pyopencl.tools as cltool
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)
b = np.array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_mem = clarr.to_device(queue, a)
b_mem = clarr.to_device(queue, b)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    result = clarr.dot(a_mem, b_mem)

print(result.get())

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLDotTest.py
220.0

Copyright 2018-2019, by Masaki Komatsu