diff --git a/ivy/data_classes/container/experimental/creation.py b/ivy/data_classes/container/experimental/creation.py index cfc70e6e7febb..b4fef4b6cf3b0 100644 --- a/ivy/data_classes/container/experimental/creation.py +++ b/ivy/data_classes/container/experimental/creation.py @@ -818,3 +818,163 @@ def eye_like( dtype=dtype, device=device, ) + + @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?', + ... b = b'\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.]), + b: ivy.array([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?', + ... b = b'\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.]), + b: ivy.array([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/experimental/creation.py b/ivy/functional/backends/jax/experimental/creation.py index 57d0c8e66f0be..22ac05154a338 100644 --- a/ivy/functional/backends/jax/experimental/creation.py +++ b/ivy/functional/backends/jax/experimental/creation.py @@ -85,3 +85,12 @@ def tril_indices( jnp.tril_indices(n=n_rows, k=k, m=n_cols), device=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/experimental/creation.py b/ivy/functional/backends/numpy/experimental/creation.py index 30e5407ae54ec..54d5087f3cb3b 100644 --- a/ivy/functional/backends/numpy/experimental/creation.py +++ b/ivy/functional/backends/numpy/experimental/creation.py @@ -91,3 +91,15 @@ def kaiser_window( kaiser_window.support_native_out = False + + +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/paddle/experimental/creation.py b/ivy/functional/backends/paddle/experimental/creation.py index 940cf2dff13e8..9d1d3d4168296 100644 --- a/ivy/functional/backends/paddle/experimental/creation.py +++ b/ivy/functional/backends/paddle/experimental/creation.py @@ -1,4 +1,5 @@ # global +import struct from typing import Optional, Tuple import math import paddle @@ -8,6 +9,8 @@ # local import ivy +from ivy.func_wrapper import with_unsupported_dtypes +from . import backend_version # noinspection PyProtectedMember # Helpers for calculating Window Functions @@ -205,3 +208,53 @@ def tril_indices( paddle.tril_indices(n_rows, col=n_cols, offset=k, dtype="int64"), device ) ) + + +@with_unsupported_dtypes( + { + "2.4.2 and below": ( + "bfloat16", + "complex64", + "complex128", + "uint16", + "uint32", + "uint64", + ) + }, + backend_version, +) +def frombuffer( + buffer: bytes, + dtype: Optional[paddle.dtype] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> paddle.Tensor: + dtype_bytes = int(ivy.Dtype(dtype).dtype_bits / 8) + if (str(dtype) == 'bool'): + dtype_bytes = 1 + dtype_str = str(dtype) + struct_format = { + 'bool': '?', + 'int8': 'b', + 'int16': 'h', + 'int32': 'i', + 'int64': 'q', + 'uint8': 'B', + 'float16': 'e', + 'float32': 'f', + 'float64': 'd' + } + ret = [] + for i in range(0, len(buffer), dtype_bytes): + x = struct.unpack(struct_format[dtype_str], buffer[i:i + dtype_bytes]) + ret = ret + list(x) + if offset > 0: + offset = int(offset / dtype_bytes) + if count > -1: + ret = ret[offset:offset + count] + else: + ret = ret[offset:] + ret = paddle.to_tensor(ret, dtype=dtype) + + return ret + diff --git a/ivy/functional/backends/tensorflow/experimental/creation.py b/ivy/functional/backends/tensorflow/experimental/creation.py index 1195f94774a94..ed708314c80cb 100644 --- a/ivy/functional/backends/tensorflow/experimental/creation.py +++ b/ivy/functional/backends/tensorflow/experimental/creation.py @@ -6,7 +6,8 @@ # local import ivy - +from ivy.func_wrapper import with_unsupported_dtypes +from . import backend_version # Array API Standard # # -------------------# @@ -120,3 +121,22 @@ def tril_indices( return tuple(tf.convert_to_tensor(ret, dtype=tf.int64)) return tuple(tf.convert_to_tensor(ret, dtype=tf.int64)) + + +@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/experimental/creation.py b/ivy/functional/backends/torch/experimental/creation.py index 705d03fb3a4ba..8ed1eb1649a74 100644 --- a/ivy/functional/backends/torch/experimental/creation.py +++ b/ivy/functional/backends/torch/experimental/creation.py @@ -6,6 +6,7 @@ # local import ivy +import copy # noinspection PyProtectedMember @@ -134,3 +135,15 @@ def tril_indices( row=n_rows, col=n_cols, offset=k, dtype=torch.int64, device=device ) ) + + +def frombuffer( + buffer: bytes, + dtype: Optional[torch.dtype] = float, + count: Optional[int] = -1, + offset: Optional[int] = 0, +) -> torch.Tensor: + buffer_copy = copy.deepcopy(buffer) + dtype = ivy.as_native_dtype(dtype) + + return torch.frombuffer(buffer_copy, dtype=dtype, count=count, offset=offset) diff --git a/ivy/functional/ivy/experimental/creation.py b/ivy/functional/ivy/experimental/creation.py index a5043ff998227..f5775c889b8fb 100644 --- a/ivy/functional/ivy/experimental/creation.py +++ b/ivy/functional/ivy/experimental/creation.py @@ -579,3 +579,65 @@ def eye_like( 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@\x00B\x00D\x00E' + >>> y = ivy.frombuffer(x, dtype='float16', count=4, offset=2) + >>> print(y) + (ivy.array([2., 3., 4., 5.])) + """ + return current_backend().frombuffer( + buffer, + dtype=dtype, + count=count, + offset=offset, + ) diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_creation.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_creation.py index d7f8c4126dd0d..9e532fed39f85 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_creation.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_creation.py @@ -3,6 +3,7 @@ # local import ivy_tests.test_ivy.helpers as helpers from ivy_tests.test_ivy.helpers import handle_test +import numpy as np @handle_test( @@ -312,3 +313,54 @@ def test_eye_like( device=on_device, ground_truth_backend=ground_truth_backend, ) + + +@st.composite +def _get_dtype_buffer_count_offset(draw): + dtype, value = draw( + helpers.dtype_and_values( + available_dtypes=helpers.get_dtypes("valid"), + ) + ) + value = np.array(value) + length = value.size + value = value.tobytes() + + offset = draw(helpers.ints(min_value=0, max_value=length - 1)) + count = draw(helpers.ints(min_value=-2 ** 30, max_value=length - offset)) + if count == 0: + count = -1 + offset = offset * np.dtype(dtype[0]).itemsize + + return dtype, value, count, offset + + +@handle_test( + fn_tree="functional.ivy.experimental.frombuffer", + dtype_buffer_count_offset=_get_dtype_buffer_count_offset(), + test_instance_method=st.just(False), + test_with_out=st.just(False), + test_gradients=st.just(False), +) +def test_frombuffer( + dtype_buffer_count_offset, + test_flags, + backend_fw, + fn_name, + on_device, + ground_truth_backend, +): + input_dtype, buffer, count, offset = dtype_buffer_count_offset + helpers.test_function( + input_dtypes=input_dtype, + test_flags=test_flags, + on_device=on_device, + fw=backend_fw, + fn_name=fn_name, + buffer=buffer, + dtype=input_dtype[0], + count=count, + offset=offset, + ground_truth_backend=ground_truth_backend, + ) +