1- from redis import StrictRedis
2- from redis ._compat import (long , nativestr )
31from enum import Enum
4- import six
5-
6- class Type (Enum ):
7- FLOAT = 1
8- DOUBLE = 2
9- INT8 = 3
10- INT16 = 4
11- INT32 = 5
12- INT64 = 6
13- UINT8 = 7
14- UINT16 = 8
15-
2+ from redis import StrictRedis
3+ from ._util import to_string
4+
5+ try :
6+ import numpy as np
7+ except ImportError :
8+ np = None
9+
10+ try :
11+ from typing import Union , Any , AnyStr , ByteString , Collection
12+ except ImportError :
13+ pass
14+
15+
16+ class Device (Enum ):
17+ cpu = 'cpu'
18+ gpu = 'gpu'
19+
20+
21+ class Backend (Enum ):
22+ tf = 'tf'
23+ torch = 'torch'
24+ onnx = 'ort'
25+
26+
27+ class DType (Enum ):
28+ float = 'float'
29+ double = 'double'
30+ int8 = 'int8'
31+ int16 = 'int16'
32+ int32 = 'int32'
33+ int64 = 'int64'
34+ uint8 = 'uint8'
35+ uint16 = 'uint16'
36+ uint32 = 'uint32'
37+ uint64 = 'uint64'
38+
39+ # aliases
40+ float32 = 'float'
41+ float64 = 'double'
1642
17- class Client (StrictRedis ):
1843
19- def __init__ (self , * args , ** kwargs ):
44+ class Tensor (object ):
45+ ARGNAME = 'VALUES'
46+
47+ def __init__ (self ,
48+ dtype , # type: DType
49+ shape , # type: Collection[int]
50+ value ):
51+ """
52+ Declare a tensor suitable for passing to tensorset
53+ :param dtype: The type the values should be stored as.
54+ This can be one of Tensor.FLOAT, tensor.DOUBLE, etc.
55+ :param shape: An array describing the shape of the tensor. For an
56+ image 250x250 with three channels, this would be [250, 250, 3]
57+ :param value: The value for the tensor. Can be an array.
58+ The contents must coordinate with the shape, meaning that the
59+ overall length needs to be the product of all figures in the
60+ shape. There is no verification to ensure that each dimension
61+ is correct. Your application must ensure that the ordering
62+ is always consistent.
2063 """
21- Create a new Client optional host and port
64+ self .type = dtype
65+ self .shape = shape
66+ self .value = value
67+ self ._size = 1
68+ if not isinstance (value , (list , tuple )):
69+ self .value = [value ]
70+
71+ @property
72+ def size (self ):
73+ return self ._size
74+
75+ def __repr__ (self ):
76+ return '<{c.__class__.__name__}(shape={s} type={t}) at 0x{id:x}>' .format (
77+ c = self ,
78+ s = self .shape ,
79+ t = self .type ,
80+ id = id (self ))
81+
82+
83+ class ScalarTensor (Tensor ):
84+ def __init__ (self , dtype , * values ):
85+ # type: (ScalarTensor, DType, Any) -> None
86+ """
87+ Declare a tensor with a bunch of scalar values. This can be used
88+ to 'batch-load' several tensors.
89+
90+ :param dtype: The datatype to store the tensor as
91+ :param values: List of values
92+ """
93+ super (ScalarTensor , self ).__init__ (dtype , [1 ], values )
94+ self ._size = len (values )
95+
96+
97+ class BlobTensor (Tensor ):
98+ ARGNAME = 'BLOB'
2299
23- If conn is not None, we employ an already existing redis connection
100+ def __init__ (self ,
101+ dtype ,
102+ shape , # type: Collection[int]
103+ * blobs # type: Union[BlobTensor, ByteString]
104+ ):
24105 """
25- StrictRedis .__init__ (self , * args , ** kwargs )
26-
27- # Set the module commands' callbacks
28- MODULE_CALLBACKS = {
29- 'AI.TENSORSET' : lambda r : r and nativestr (r ) == 'OK' ,
106+ Create a tensor from a binary blob
107+ :param dtype: The datatype, one of Tensor.FLOAT, Tensor.DOUBLE, etc.
108+ :param shape: An array
109+ :param blobs: One or more blobs to assign to the tensor.
110+ """
111+ if len (blobs ) > 1 :
112+ blobarr = bytearray ()
113+ for b in blobs :
114+ if isinstance (b , BlobTensor ):
115+ b = b .value [0 ]
116+ blobarr += b
117+ size = len (blobs )
118+ blobs = bytes (blobarr )
119+ else :
120+ blobs = bytes (blobs [0 ])
121+ size = 1
122+
123+ super (BlobTensor , self ).__init__ (dtype , shape , blobs )
124+ self ._size = size
125+
126+ @classmethod
127+ def from_numpy (cls , * nparrs ):
128+ # type: (type, np.array) -> BlobTensor
129+ blobs = []
130+ for arr in nparrs :
131+ blobs .append (arr .data )
132+ dt = DType .__members__ [str (nparrs [0 ].dtype )]
133+ return cls (dt , nparrs [0 ].shape , * blobs )
134+
135+ @property
136+ def blob (self ):
137+ return self .value [0 ]
138+
139+ def to_numpy (self ):
140+ # type: () -> np.array
141+ a = np .frombuffer (self .value [0 ], dtype = self ._to_numpy_type (self .type ))
142+ return a .reshape (self .shape )
143+
144+ @staticmethod
145+ def _to_numpy_type (t ):
146+ t = t .lower ()
147+ mm = {
148+ 'float' : 'float32' ,
149+ 'double' : 'float64'
150+ }
151+ if t in mm :
152+ return mm [t ]
153+ return t
154+
155+
156+ class Client (StrictRedis ):
157+ def modelset (self ,
158+ name , # type: AnyStr
159+ backend , # type: Backend
160+ device , # type: Device
161+ inputs , # type: Collection[AnyStr]
162+ outputs , # type: Collection[AnyStr]
163+ data # type: ByteString
164+ ):
165+ args = ['AI.MODELSET' , name , backend .value , device .value , 'INPUTS' ]
166+ args += inputs
167+ args += ['OUTPUTS' ] + outputs
168+ args += [data ]
169+ return self .execute_command (* args )
170+
171+ def modelget (self , name ):
172+ rv = self .execute_command ('AI.MODELGET' , name )
173+ return {
174+ 'backend' : Backend (rv [0 ]),
175+ 'device' : Device (rv [1 ]),
176+ 'data' : rv [2 ]
30177 }
31- for k , v in six .iteritems (MODULE_CALLBACKS ):
32- self .set_response_callback (k , v )
33178
179+ def modelrun (self , name , inputs , outputs ):
180+ args = ['AI.MODELRUN' , name ]
181+ args += ['INPUTS' ] + inputs + ['OUTPUTS' ] + outputs
182+ return self .execute_command (* args )
34183
35- def tensorset (self , key , type , dimensions , tensor ):
36- args = ['AI.TENSORSET' , key , type .name ] + dimensions + ['VALUES' ] + tensor
37-
184+ def tensorset (self , key , tensor ):
185+ # type: (Client, AnyStr, Union[Tensor, np.ndarray]) -> Any
186+ """
187+ Set the values of the tensor on the server using the provided Tensor object
188+ :param key: The name of the tensor
189+ :param tensor: a `Tensor` object
190+ """
191+ if np and isinstance (tensor , np .ndarray ):
192+ tensor = BlobTensor .from_numpy (tensor )
193+ args = ['AI.TENSORSET' , key , tensor .type .value , tensor .size ]
194+ args += tensor .shape
195+ args += [tensor .ARGNAME ]
196+ args += tensor .value
38197 return self .execute_command (* args )
39198
40-
199+ def tensorget (self , key , astype = Tensor , meta_only = False ):
200+ """
201+ Retrieve the value of a tensor from the server
202+ :param key: the name of the tensor
203+ :param astype: the resultant tensor type
204+ :param meta_only: if true, then the value is not retrieved,
205+ only the shape and the type
206+ :return: an instance of astype
207+ """
208+ argname = 'META' if meta_only else astype .ARGNAME
209+ res = self .execute_command ('AI.TENSORGET' , key , argname )
210+ dtype , shape = to_string (res [0 ]), res [1 ]
211+ if meta_only :
212+ return astype (dtype , shape , [])
213+ else :
214+ return astype (dtype , shape , res [2 ])
215+
216+ def scriptset (self , name , device , script ):
217+ return self .execute_command ('AI.SCRIPTSET' , name , device .value , script )
218+
219+ def scriptget (self , name ):
220+ r = self .execute_command ('AI.SCRIPTGET' , name )
221+ return {
222+ 'device' : to_string (r [0 ]),
223+ 'script' : to_string (r [1 ])
224+ }
225+
226+ def scriptrun (self , name , function , inputs , outputs ):
227+ args = ['AI.SCRIPTRUN' , name , function , 'INPUTS' ]
228+ args += inputs
229+ args += ['OUTPUTS' ]
230+ args += outputs
231+ return self .execute_command (* args )
0 commit comments