From e0be35b10bffc13ecae6f7da0eb5ac8b038f0cc5 Mon Sep 17 00:00:00 2001 From: Tuan Date: Wed, 5 Apr 2023 16:34:05 +0700 Subject: [PATCH] add: frombuffer to ivy functional api --- ivy/container/creation.py | 160 ++++++++++++++++++ ivy/functional/backends/jax/creation.py | 9 + ivy/functional/backends/numpy/creation.py | 12 ++ .../backends/tensorflow/creation.py | 19 +++ ivy/functional/backends/torch/creation.py | 11 ++ ivy/functional/ivy/creation.py | 59 +++++++ 6 files changed, 270 insertions(+) diff --git a/ivy/container/creation.py b/ivy/container/creation.py index e8f1d2a32f665..d09d44e55d076 100644 --- a/ivy/container/creation.py +++ b/ivy/container/creation.py @@ -1320,3 +1320,163 @@ def one_hot( map_sequences=map_sequences, out=out, ) + + @staticmethod + def static_frombuffer( + buffer: ivy.Container, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, + key_chains: Optional[Union[List[str], Dict[str, str]]] = None, + to_apply: bool = True, + prune_unapplied: bool = False, + map_sequences: bool = False, + ) -> ivy.Container: + """ + ivy.Container static method variant of ivy.frombuffer. This method simply + wraps the function, and so the docstring for ivy.frombuffer also applies + to this method with minimal changes. + + Parameters + ---------- + buffer + An object that exposes the buffer interface. + dtype + Data-type of the returned array; default: float. + count + Number of items to read. -1 means all data in the buffer. + offset + Start reading the buffer from this offset (in bytes); default: 0. + key_chains + The key-chains to apply or not apply the method to. Default is ``None``. + to_apply + If True, the method will be applied to key_chains, otherwise key_chains will + be skipped. Default is ``True``. + prune_unapplied + Whether to prune key_chains for which the function was not applied. Default + is False. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is ``False``. + + Returns + ------- + out + 1-dimensional array. + + Examples + -------- + With :class:`ivy.Container` inputs: + + >>> x = ivy.Container( + ... a = b'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@', + ... b = b'\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@' + ... ) + >>> y = ivy.Container.static_frombuffer(x) + >>> print(y) + { + a: ivy.array([1., 2.]), + b: ivy.array([2., 1., 2.]) + } + + >>> x = ivy.Container( + ... a = b'\x01\x02\x03\x04', + ... b = b'\x05\x04\x03\x03\x02' + ... ) + >>> y = ivy.Container.static_frombuffer(x, dtype=ivy.int8, count=3, offset=1) + >>> print(y) + { + a: ivy.array([2, 3, 4]), + b: ivy.array([4, 3, 3]) + } + """ + return ContainerBase.cont_multi_map_in_function( + "frombuffer", + buffer, + dtype=dtype, + count=count, + offset=offset, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + ) + + def frombuffer( + self: ivy.Container, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, + key_chains: Optional[Union[List[str], Dict[str, str]]] = None, + to_apply: bool = True, + prune_unapplied: bool = False, + map_sequences: bool = False, + ) -> ivy.Container: + """ + ivy.Container instance method variant of ivy.frombuffer. This method + simply wraps the function, and so the docstring for ivy.frombuffer + also applies to this method with minimal changes. + + Parameters + ---------- + self + An object that exposes the buffer interface. + dtype + Data-type of the returned array; default: float. + count + Number of items to read. -1 means all data in the buffer. + offset + Start reading the buffer from this offset (in bytes); default: 0. + key_chains + The key-chains to apply or not apply the method to. Default is ``None``. + to_apply + If True, the method will be applied to key_chains, otherwise key_chains will + be skipped. Default is ``True``. + prune_unapplied + Whether to prune key_chains for which the function was not applied. Default + is False. + map_sequences + Whether to also map method to sequences (lists, tuples). + Default is ``False``. + + Returns + ------- + out + 1-dimensional array. + + Examples + -------- + With :class:`ivy.Container` inputs: + + >>> x = ivy.Container( + ... a = b'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@', + ... b = b'\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@' + ... ) + >>> y = ivy.frombuffer(x) + >>> print(y) + { + a: ivy.array([1., 2.]), + b: ivy.array([2., 1., 2.]) + } + + >>> x = ivy.Container( + ... a = b'\x01\x02\x03\x04', + ... b = b'\x05\x04\x03\x03\x02' + ... ) + >>> y = ivy.frombuffer(x, dtype=ivy.int8, count=3, offset=1) + >>> print(y) + { + a: ivy.array([2, 3, 4]), + b: ivy.array([4, 3, 3]) + } + """ + return self.static_frombuffer( + self, + dtype=dtype, + count=count, + offset=offset, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + ) diff --git a/ivy/functional/backends/jax/creation.py b/ivy/functional/backends/jax/creation.py index 8c89ba7e12e99..2189319e37f45 100644 --- a/ivy/functional/backends/jax/creation.py +++ b/ivy/functional/backends/jax/creation.py @@ -340,3 +340,12 @@ def one_hot( res = jnp.moveaxis(res, -1, axis) return _to_device(res, device) + + +def frombuffer( + buffer: bytes, + dtype: Optional[jnp.dtype] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> JaxArray: + return jnp.frombuffer(buffer, dtype=dtype, count=count, offset=offset) diff --git a/ivy/functional/backends/numpy/creation.py b/ivy/functional/backends/numpy/creation.py index 2eb18064264d2..0b7066d484b07 100644 --- a/ivy/functional/backends/numpy/creation.py +++ b/ivy/functional/backends/numpy/creation.py @@ -273,3 +273,15 @@ def one_hot( res = np.moveaxis(res, -1, axis) return res + + +def frombuffer( + buffer: bytes, + dtype: Optional[np.dtype] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> np.ndarray: + if isinstance(dtype, list): + dtype = np.dtype(dtype[0]) + + return np.frombuffer(buffer, dtype=dtype, count=count, offset=offset) diff --git a/ivy/functional/backends/tensorflow/creation.py b/ivy/functional/backends/tensorflow/creation.py index 18c1f8bc172e3..897795d3ec7fb 100644 --- a/ivy/functional/backends/tensorflow/creation.py +++ b/ivy/functional/backends/tensorflow/creation.py @@ -421,3 +421,22 @@ def one_hot( return tf.one_hot( indices, depth, on_value=on_value, off_value=off_value, axis=axis, dtype=dtype ) + + +@with_unsupported_dtypes({"1.11.0 and below": ("uint32", "uint64")}, backend_version) +def frombuffer( + buffer: bytes, + dtype: Optional[tf.DType] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> Union[tf.Tensor, tf.Variable]: + ret = tf.io.decode_raw(buffer, dtype) + dtype = tf.dtypes.as_dtype(dtype) + if offset > 0: + offset = int(offset / dtype.size) + if count > -1: + ret = ret[offset:offset + count] + else: + ret = ret[offset:] + + return ret diff --git a/ivy/functional/backends/torch/creation.py b/ivy/functional/backends/torch/creation.py index 3f8c0369bec2a..069459cc7869b 100644 --- a/ivy/functional/backends/torch/creation.py +++ b/ivy/functional/backends/torch/creation.py @@ -611,3 +611,14 @@ def one_hot( res = torch.moveaxis(res, -1, axis) return res.to(device, dtype) + + +def frombuffer( + buffer: bytes, + dtype: Optional[torch.dtype] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> torch.Tensor: + dtype = ivy.as_native_dtype(dtype) + + return torch.frombuffer(buffer, dtype=dtype, count=count, offset=offset) diff --git a/ivy/functional/ivy/creation.py b/ivy/functional/ivy/creation.py index 49835895f5b0d..cf3c21b25a566 100644 --- a/ivy/functional/ivy/creation.py +++ b/ivy/functional/ivy/creation.py @@ -1920,3 +1920,62 @@ def logspace( device=device, out=out, ) + + +@outputs_to_ivy_arrays +@handle_nestable +def frombuffer( + buffer: bytes, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> ivy.Array: + """ + Interpret a buffer as a 1-dimensional array. + + .. note:: + Note that either of the following must be true: + 1. count is a positive non-zero number, and the total number of bytes in the buffer is equal or greater than offset plus count times the size (in bytes) of dtype. + 2. count is negative, and the length (number of bytes) of the buffer subtracted by the offset is a multiple of the size (in bytes) of dtype. + + Parameters + ---------- + buffer + An object that exposes the buffer interface. + dtype + Data-type of the returned array; default: float. + count + Number of items to read. -1 means all data in the buffer. + offset + Start reading the buffer from this offset (in bytes); default: 0. + + Returns + ------- + out + 1-dimensional array. + + Examples + -------- + With :class:`bytes` inputs: + + >>> x = b'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@' + >>> y = ivy.frombuffer(x) + >>> print(y) + (ivy.array([1., 2.])) + + >>> x = b'\x01\x02\x03\x04' + >>> y = ivy.frombuffer(x, dtype='int8', count=-2, offset=1) + >>> print(y) + (ivy.array([2, 3, 4])) + + >>> x = b'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x00\x00\xa0@\x00\x00\xc0@' + >>> y = ivy.frombuffer(x, dtype='float32', count=4, offset=4) + >>> print(y) + (ivy.array([2., 3., 4., 5.])) + """ + return current_backend().frombuffer( + buffer, + dtype=dtype, + count=count, + offset=offset, + )