Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: ivy experimental api frombuffer #13781

Merged
merged 14 commits into from
Apr 9, 2023
Merged
160 changes: 160 additions & 0 deletions ivy/data_classes/container/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
9 changes: 9 additions & 0 deletions ivy/functional/backends/jax/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
12 changes: 12 additions & 0 deletions ivy/functional/backends/numpy/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
53 changes: 53 additions & 0 deletions ivy/functional/backends/paddle/experimental/creation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# global
import struct
from typing import Optional, Tuple
import math
import paddle
Expand All @@ -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
Expand Down Expand Up @@ -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

22 changes: 21 additions & 1 deletion ivy/functional/backends/tensorflow/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

# local
import ivy

from ivy.func_wrapper import with_unsupported_dtypes
from . import backend_version

# Array API Standard #
# -------------------#
Expand Down Expand Up @@ -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
13 changes: 13 additions & 0 deletions ivy/functional/backends/torch/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# local
import ivy
import copy

# noinspection PyProtectedMember

Expand Down Expand Up @@ -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)
62 changes: 62 additions & 0 deletions ivy/functional/ivy/experimental/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Loading