From df2fa13f6dd356db1a2515d4daadfd005df69c9d Mon Sep 17 00:00:00 2001 From: illia-bab Date: Wed, 16 Aug 2023 13:41:07 +0300 Subject: [PATCH 01/24] implement decorators for builtins frontend --- ivy/functional/frontends/builtins/__init__.py | 6 ++ .../frontends/builtins/func_wrapper.py | 82 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 ivy/functional/frontends/builtins/__init__.py create mode 100644 ivy/functional/frontends/builtins/func_wrapper.py diff --git a/ivy/functional/frontends/builtins/__init__.py b/ivy/functional/frontends/builtins/__init__.py new file mode 100644 index 0000000000000..6c99ac3a705d8 --- /dev/null +++ b/ivy/functional/frontends/builtins/__init__.py @@ -0,0 +1,6 @@ +from . import list +from .list import * +from . import func_wrapper +from .func_wrapper import * +from . import built_in_functions +from .built_in_functions import * diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py new file mode 100644 index 0000000000000..cbea09fabb68d --- /dev/null +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -0,0 +1,82 @@ +import functools +from typing import Callable, Iterable +from importlib import import_module + +import ivy + + +def _to_ivy_array(x): + if hasattr(x, "ivy_array"): + return x.ivy_array + + elif isinstance(x, (ivy.NativeArray, list)): + return ivy.array(x) + + return x + + +def _infer_return_array(x: Iterable) -> Callable: + module_path = x.__class__.__module__ + frontend_array_name = x.__class__.__name__ + + if frontend_array_name in ["int", "float", "bool", "complex"]: + frontend_path = "ivy.functional.frontends.builtins" + frontend_array_name = "list" + + elif "ivy" not in module_path: + module_str = module_path.split(".")[0] + module_str = "jax" if module_str == "jaxlib" else module_str + frontend_path = "ivy.functional.frontends." + module_str + frontend_array_name = "Array" if module_str == "jax" else frontend_array_name + + else: + frontend_path = module_path + + module = import_module(frontend_path) + frontend_array = getattr(module, frontend_array_name) + + if "numpy" in frontend_path: + return functools.partial(frontend_array, _init_overload=True) + return frontend_array + + +def inputs_to_ivy_arrays(fn: Callable) -> Callable: + @functools.wraps(fn) + def _inputs_to_ivy_arrays_builtins(*args, **kwargs): + ivy_args = ivy.nested_map(args, _to_ivy_array, shallow=False, to_ignore=list) + ivy_kwargs = ivy.nested_map( + kwargs, _to_ivy_array, shallow=False, to_ignore=list + ) + + frontend_array = _infer_return_array(args[0]) + + return fn(*ivy_args, **ivy_kwargs), frontend_array + + return _inputs_to_ivy_arrays_builtins + + +def outputs_to_frontend_arrays(fn: Callable) -> Callable: + @functools.wraps(fn) + def _outputs_to_frontend_arrays(*args, **kwargs): + ret, frontend_array = fn(*args, **kwargs) + + if isinstance(ret, ivy.Array) or ivy.is_native_array(ret): + return frontend_array(ret) + return ret + + return _outputs_to_frontend_arrays + + +def to_ivy_arrays_and_back(fn: Callable) -> Callable: + return outputs_to_frontend_arrays(inputs_to_ivy_arrays(fn)) + + +def from_zero_dim_arrays_to_scalar(fn: Callable) -> Callable: + @functools.wraps(fn) + def _from_zero_dim_arrays_to_scalar(*args, **kwargs): + ret = fn(*args, **kwargs) + if len(ret) > 1: + return ret + return ivy.to_scalar(ret) + + return _from_zero_dim_arrays_to_scalar From e80aa9166d770c03e08d90f1ff6fa21b89e92081 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Wed, 16 Aug 2023 13:41:57 +0300 Subject: [PATCH 02/24] implement frontend list class --- ivy/functional/frontends/builtins/list.py | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 ivy/functional/frontends/builtins/list.py diff --git a/ivy/functional/frontends/builtins/list.py b/ivy/functional/frontends/builtins/list.py new file mode 100644 index 0000000000000..0cd11696801c3 --- /dev/null +++ b/ivy/functional/frontends/builtins/list.py @@ -0,0 +1,68 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import _to_ivy_array +import ivy.functional.frontends.builtins as builtins_frontend + + +class list(list): + def __init__(self, iterable=None): + super().__init__() + self._ivy_array = ( + ivy.array(iterable if iterable is not None else []) + if not isinstance(iterable, ivy.Array) + else iterable + ) + + # Properties # + # ---------- # + + @property + def ivy_array(self): + return self._ivy_array + + @ivy_array.setter + def ivy_array(self, array): + self._ivy_array = ( + ivy.array(array) if not isinstance(array, ivy.Array) else array + ) + + # Dunderscore methods # + # ------------------- # + + def __repr__(self): + return str(self.ivy_array.__repr__()).replace( + "ivy.array", "ivy.frontends.builtins.List" + ) + + def __len__(self): + return len(self.ivy_array) + + def __getitem__(self, item): + item = _to_ivy_array(item) + return ivy.get_item(self.ivy_array, item) + + def __iter__(self): + iter_len = self.__len__() + if iter_len == 0: + raise TypeError("Iteration over 0-d list is not supported!") + + for i in builtins_frontend.range(iter_len): + yield self[i] + + def __str__(self): + return f"{self.ivy_array}" + + def __abs__(self): + return builtins_frontend.abs(self) + + # Methods # + # ------- # + + def pop(self, key=-1): + key = _to_ivy_array(key) + + if key < 0: + key += self.__len__() + + temp = self[key] + self.ivy_array = ivy.concat([self[:key], self[key + 1 :]]) + return temp From d6cc29dfcafa9a3c9d8a649bda607d68a2b001e5 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Wed, 16 Aug 2023 13:42:53 +0300 Subject: [PATCH 03/24] implement functions from math module --- .../frontends/builtins/math/__init__.py | 2 ++ .../math/power_and_logarithmic_functions.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/__init__.py create mode 100644 ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py new file mode 100644 index 0000000000000..ff2d4e244826c --- /dev/null +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -0,0 +1,2 @@ +from . import power_and_logarithmic_functions +from .power_and_logarithmic_functions import * diff --git a/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py new file mode 100644 index 0000000000000..d308eef0a1f41 --- /dev/null +++ b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py @@ -0,0 +1,31 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, +) + + +@from_zero_dim_arrays_to_scalar +def sqrt(x): + return ivy.sqrt(x) + + +@from_zero_dim_arrays_to_scalar +def pow(x, y): + return ivy.pow(x, y) + + +@from_zero_dim_arrays_to_scalar +def log(x, base=None): + if base: + return ivy.divide(ivy.log(x), ivy.log(base)) + return ivy.log(x) + + +@from_zero_dim_arrays_to_scalar +def log2(x): + return ivy.log2(x) + + +@from_zero_dim_arrays_to_scalar +def exp(x): + return ivy.exp(x) From 8c5eca019f8c285828a2bf27b0d98697eb91d932 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Wed, 16 Aug 2023 13:43:48 +0300 Subject: [PATCH 04/24] implement abs and range frontend functions and use within frontend list class --- .../frontends/builtins/built_in_functions.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 ivy/functional/frontends/builtins/built_in_functions.py diff --git a/ivy/functional/frontends/builtins/built_in_functions.py b/ivy/functional/frontends/builtins/built_in_functions.py new file mode 100644 index 0000000000000..398c4aa1d3cec --- /dev/null +++ b/ivy/functional/frontends/builtins/built_in_functions.py @@ -0,0 +1,18 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, + to_ivy_arrays_and_back, +) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def abs(x): + return ivy.abs(x) + + +@to_ivy_arrays_and_back +def range(start, /, stop=None, step=1): + if not stop: + return ivy.arange(0, stop=start, step=step) + return ivy.arange(start, stop=stop, step=step) From e552bff9405ef25e269681e75c3e0803ab2fcf4f Mon Sep 17 00:00:00 2001 From: illia-bab Date: Thu, 17 Aug 2023 13:54:03 +0300 Subject: [PATCH 05/24] added comments and refactored _infer_return_array to follow the current design decision --- .../frontends/builtins/func_wrapper.py | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index cbea09fabb68d..1ffb26842e7f1 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -6,48 +6,52 @@ def _to_ivy_array(x): + # if x is any frontend array including frontend list + # then extract the wrapped ivy array if hasattr(x, "ivy_array"): return x.ivy_array - elif isinstance(x, (ivy.NativeArray, list)): + # convert native arrays and lists to ivy arrays + elif isinstance(x, (ivy.NativeArray, list, tuple)): return ivy.array(x) return x def _infer_return_array(x: Iterable) -> Callable: - module_path = x.__class__.__module__ - frontend_array_name = x.__class__.__name__ - - if frontend_array_name in ["int", "float", "bool", "complex"]: - frontend_path = "ivy.functional.frontends.builtins" - frontend_array_name = "list" - - elif "ivy" not in module_path: - module_str = module_path.split(".")[0] - module_str = "jax" if module_str == "jaxlib" else module_str - frontend_path = "ivy.functional.frontends." + module_str - frontend_array_name = "Array" if module_str == "jax" else frontend_array_name - - else: - frontend_path = module_path - + # get module's name which is the first element + module_str = x.__class__.__module__.split(".")[0] + + # if function's input is a scalar, list, or tuple + # convert to current backend's frontend array + if module_str in ["builtins"]: + # get current backend str + cur_backend = ivy.current_backend_str() + # assign current backend str unless it's empty, otherwise numpy + module_str = cur_backend if len(cur_backend) != 0 else "numpy" + + # replace jaxlib with jax to construct a valid path + module_str = "jax" if module_str == "jaxlib" else module_str + frontend_path = "ivy.functional.frontends." + module_str + + # import the module and get a corresponding frontend array module = import_module(frontend_path) - frontend_array = getattr(module, frontend_array_name) + frontend_array = getattr(module, "_frontend_array") - if "numpy" in frontend_path: - return functools.partial(frontend_array, _init_overload=True) return frontend_array def inputs_to_ivy_arrays(fn: Callable) -> Callable: @functools.wraps(fn) def _inputs_to_ivy_arrays_builtins(*args, **kwargs): - ivy_args = ivy.nested_map(args, _to_ivy_array, shallow=False, to_ignore=list) + ivy_args = ivy.nested_map( + args, _to_ivy_array, shallow=False, to_ignore=(list, tuple) + ) ivy_kwargs = ivy.nested_map( - kwargs, _to_ivy_array, shallow=False, to_ignore=list + kwargs, _to_ivy_array, shallow=False, to_ignore=(list, tuple) ) + # array is the first argument given to a function frontend_array = _infer_return_array(args[0]) return fn(*ivy_args, **ivy_kwargs), frontend_array From 681cde947ad09b9d51d9fb548ca8b5ace24748c9 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Thu, 17 Aug 2023 13:55:04 +0300 Subject: [PATCH 06/24] implemented all, any and round builtin functions --- .../frontends/builtins/built_in_functions.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ivy/functional/frontends/builtins/built_in_functions.py b/ivy/functional/frontends/builtins/built_in_functions.py index 398c4aa1d3cec..cb28934748c3a 100644 --- a/ivy/functional/frontends/builtins/built_in_functions.py +++ b/ivy/functional/frontends/builtins/built_in_functions.py @@ -16,3 +16,22 @@ def range(start, /, stop=None, step=1): if not stop: return ivy.arange(0, stop=start, step=step) return ivy.arange(start, stop=stop, step=step) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def all(iterable): + return ivy.all(iterable) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def any(iterable): + return ivy.any(iterable) + + +@from_zero_dim_arrays_to_scalar +def round(number, ndigits=None): + if not ndigits: + return ivy.round(number) + return ivy.round(number, decimals=ndigits) From 3988d555accdae202f8e8f9acfe365800aa77f1a Mon Sep 17 00:00:00 2001 From: Illia Babichev <139856936+illia-bab@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:01:27 +0300 Subject: [PATCH 07/24] rm list frontend class as part of design decision --- ivy/functional/frontends/builtins/list.py | 68 ----------------------- 1 file changed, 68 deletions(-) delete mode 100644 ivy/functional/frontends/builtins/list.py diff --git a/ivy/functional/frontends/builtins/list.py b/ivy/functional/frontends/builtins/list.py deleted file mode 100644 index 0cd11696801c3..0000000000000 --- a/ivy/functional/frontends/builtins/list.py +++ /dev/null @@ -1,68 +0,0 @@ -import ivy -from ivy.functional.frontends.builtins.func_wrapper import _to_ivy_array -import ivy.functional.frontends.builtins as builtins_frontend - - -class list(list): - def __init__(self, iterable=None): - super().__init__() - self._ivy_array = ( - ivy.array(iterable if iterable is not None else []) - if not isinstance(iterable, ivy.Array) - else iterable - ) - - # Properties # - # ---------- # - - @property - def ivy_array(self): - return self._ivy_array - - @ivy_array.setter - def ivy_array(self, array): - self._ivy_array = ( - ivy.array(array) if not isinstance(array, ivy.Array) else array - ) - - # Dunderscore methods # - # ------------------- # - - def __repr__(self): - return str(self.ivy_array.__repr__()).replace( - "ivy.array", "ivy.frontends.builtins.List" - ) - - def __len__(self): - return len(self.ivy_array) - - def __getitem__(self, item): - item = _to_ivy_array(item) - return ivy.get_item(self.ivy_array, item) - - def __iter__(self): - iter_len = self.__len__() - if iter_len == 0: - raise TypeError("Iteration over 0-d list is not supported!") - - for i in builtins_frontend.range(iter_len): - yield self[i] - - def __str__(self): - return f"{self.ivy_array}" - - def __abs__(self): - return builtins_frontend.abs(self) - - # Methods # - # ------- # - - def pop(self, key=-1): - key = _to_ivy_array(key) - - if key < 0: - key += self.__len__() - - temp = self[key] - self.ivy_array = ivy.concat([self[:key], self[key + 1 :]]) - return temp From 320cd6157bcfbee89f99638fd2ab54dbe8c328f8 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Thu, 17 Aug 2023 14:05:39 +0300 Subject: [PATCH 08/24] modify builtins init file --- ivy/functional/frontends/builtins/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ivy/functional/frontends/builtins/__init__.py b/ivy/functional/frontends/builtins/__init__.py index 6c99ac3a705d8..a7eef0a51ee79 100644 --- a/ivy/functional/frontends/builtins/__init__.py +++ b/ivy/functional/frontends/builtins/__init__.py @@ -1,6 +1,5 @@ -from . import list -from .list import * from . import func_wrapper from .func_wrapper import * from . import built_in_functions from .built_in_functions import * +from . import math From fe02c237415f41539edba59ce9dd3f3f82e580ee Mon Sep 17 00:00:00 2001 From: illia-bab Date: Thu, 17 Aug 2023 16:07:29 +0300 Subject: [PATCH 09/24] minor fixes to _infer_return_array --- .../frontends/builtins/func_wrapper.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index 1ffb26842e7f1..b90a870a916b6 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -12,29 +12,35 @@ def _to_ivy_array(x): return x.ivy_array # convert native arrays and lists to ivy arrays - elif isinstance(x, (ivy.NativeArray, list, tuple)): + elif isinstance(x, ivy.NativeArray): return ivy.array(x) return x def _infer_return_array(x: Iterable) -> Callable: - # get module's name which is the first element - module_str = x.__class__.__module__.split(".")[0] + # get module path + module_path = x.__class__.__module__.split(".") # if function's input is a scalar, list, or tuple # convert to current backend's frontend array - if module_str in ["builtins"]: - # get current backend str + if "builtins" in module_path: cur_backend = ivy.current_backend_str() - # assign current backend str unless it's empty, otherwise numpy module_str = cur_backend if len(cur_backend) != 0 else "numpy" - # replace jaxlib with jax to construct a valid path - module_str = "jax" if module_str == "jaxlib" else module_str - frontend_path = "ivy.functional.frontends." + module_str + # in this case we're dealing with frontend array + # module's name is always at index 3 on the path + elif "ivy" in module_path: + module_str = module_path[3] + + # native array, e.g. np.ndarray, torch.Tensor etc. + else: + module_str = module_path[0] + # replace jaxlib with jax to construct a valid path + module_str = "jax" if module_str == "jaxlib" else module_str # import the module and get a corresponding frontend array + frontend_path = "ivy.functional.frontends." + module_str module = import_module(frontend_path) frontend_array = getattr(module, "_frontend_array") @@ -44,12 +50,8 @@ def _infer_return_array(x: Iterable) -> Callable: def inputs_to_ivy_arrays(fn: Callable) -> Callable: @functools.wraps(fn) def _inputs_to_ivy_arrays_builtins(*args, **kwargs): - ivy_args = ivy.nested_map( - args, _to_ivy_array, shallow=False, to_ignore=(list, tuple) - ) - ivy_kwargs = ivy.nested_map( - kwargs, _to_ivy_array, shallow=False, to_ignore=(list, tuple) - ) + ivy_args = ivy.nested_map(args, _to_ivy_array, shallow=False) + ivy_kwargs = ivy.nested_map(kwargs, _to_ivy_array, shallow=False) # array is the first argument given to a function frontend_array = _infer_return_array(args[0]) From c840c6b3aa8bc88bfff4a25087ea8d0686d7abc5 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 15:19:50 +0300 Subject: [PATCH 10/24] implement angular conversions --- .../frontends/builtins/math/angular_conversion.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/angular_conversion.py diff --git a/ivy/functional/frontends/builtins/math/angular_conversion.py b/ivy/functional/frontends/builtins/math/angular_conversion.py new file mode 100644 index 0000000000000..82233fd4eb9ac --- /dev/null +++ b/ivy/functional/frontends/builtins/math/angular_conversion.py @@ -0,0 +1,14 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, +) + + +@from_zero_dim_arrays_to_scalar +def radians(x): + return ivy.deg2rad(x) + + +@from_zero_dim_arrays_to_scalar +def degrees(x): + return ivy.rad2deg(x) From 7ed9f253a22d567290eeacaa41256a3d2f627fe7 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 15:20:27 +0300 Subject: [PATCH 11/24] modify init --- ivy/functional/frontends/builtins/math/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py index ff2d4e244826c..fff1e9d2ebb7f 100644 --- a/ivy/functional/frontends/builtins/math/__init__.py +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -1,2 +1,4 @@ from . import power_and_logarithmic_functions from .power_and_logarithmic_functions import * +from . import angular_conversion +from .angular_conversion import * From 85d253432173a46671abbb5972756bd67aea4447 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 15:48:33 +0300 Subject: [PATCH 12/24] implemented special functions and manually tested --- .../frontends/builtins/math/__init__.py | 2 ++ .../builtins/math/special_functions.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/special_functions.py diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py index fff1e9d2ebb7f..92f0678e16931 100644 --- a/ivy/functional/frontends/builtins/math/__init__.py +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -2,3 +2,5 @@ from .power_and_logarithmic_functions import * from . import angular_conversion from .angular_conversion import * +from . import special_functions +from .special_functions import * diff --git a/ivy/functional/frontends/builtins/math/special_functions.py b/ivy/functional/frontends/builtins/math/special_functions.py new file mode 100644 index 0000000000000..ebf3be9a913ee --- /dev/null +++ b/ivy/functional/frontends/builtins/math/special_functions.py @@ -0,0 +1,19 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, +) + + +@from_zero_dim_arrays_to_scalar +def erf(x): + return ivy.erf(x) + + +@from_zero_dim_arrays_to_scalar +def erfc(x): + return ivy.subtract(1.0, ivy.erf(x)) + + +@from_zero_dim_arrays_to_scalar +def lgamma(x): + return ivy.lgamma(x) From 857e3d8f566fa7d5098032c17905780c74b2e30c Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 16:10:22 +0300 Subject: [PATCH 13/24] implemented wrappers of hyperbolic funcs and manually tested --- .../frontends/builtins/math/__init__.py | 2 ++ .../builtins/math/hyperbolic_functions.py | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/hyperbolic_functions.py diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py index 92f0678e16931..4d31862f72fdc 100644 --- a/ivy/functional/frontends/builtins/math/__init__.py +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -4,3 +4,5 @@ from .angular_conversion import * from . import special_functions from .special_functions import * +from . import hyperbolic_functions +from .hyperbolic_functions import * diff --git a/ivy/functional/frontends/builtins/math/hyperbolic_functions.py b/ivy/functional/frontends/builtins/math/hyperbolic_functions.py new file mode 100644 index 0000000000000..b45df08cbf2b2 --- /dev/null +++ b/ivy/functional/frontends/builtins/math/hyperbolic_functions.py @@ -0,0 +1,34 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, +) + + +@from_zero_dim_arrays_to_scalar +def acosh(x): + return ivy.acosh(x) + + +@from_zero_dim_arrays_to_scalar +def asinh(x): + return ivy.asinh(x) + + +@from_zero_dim_arrays_to_scalar +def atanh(x): + return ivy.atanh(x) + + +@from_zero_dim_arrays_to_scalar +def cosh(x): + return ivy.cosh(x) + + +@from_zero_dim_arrays_to_scalar +def sinh(x): + return ivy.sinh(x) + + +@from_zero_dim_arrays_to_scalar +def tanh(x): + return ivy.tanh(x) From e5e20ca71fdbed8216b520f170ba6c4c5c7fc1b8 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 16:29:52 +0300 Subject: [PATCH 14/24] minor change to from_zero_dim_arrays_to_scalar --- ivy/functional/frontends/builtins/func_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index b90a870a916b6..b6b720b155a68 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -81,7 +81,7 @@ def from_zero_dim_arrays_to_scalar(fn: Callable) -> Callable: @functools.wraps(fn) def _from_zero_dim_arrays_to_scalar(*args, **kwargs): ret = fn(*args, **kwargs) - if len(ret) > 1: + if len(ret) > 0: return ret return ivy.to_scalar(ret) From be5874f1aadf908bc8dbdb6108d18a55eff3689c Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 17:01:41 +0300 Subject: [PATCH 15/24] implemented wrappers for trigonometric functions and manually tested --- .../frontends/builtins/math/__init__.py | 2 + .../builtins/math/trigonometric_functions.py | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/trigonometric_functions.py diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py index 4d31862f72fdc..3971dd13d3398 100644 --- a/ivy/functional/frontends/builtins/math/__init__.py +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -6,3 +6,5 @@ from .special_functions import * from . import hyperbolic_functions from .hyperbolic_functions import * +from . import trigonometric_functions +from .trigonometric_functions import * diff --git a/ivy/functional/frontends/builtins/math/trigonometric_functions.py b/ivy/functional/frontends/builtins/math/trigonometric_functions.py new file mode 100644 index 0000000000000..b8f3028b13ba8 --- /dev/null +++ b/ivy/functional/frontends/builtins/math/trigonometric_functions.py @@ -0,0 +1,52 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + from_zero_dim_arrays_to_scalar, + to_ivy_arrays_and_back, +) + + +@from_zero_dim_arrays_to_scalar +def acos(x): + return ivy.acos(x) + + +@from_zero_dim_arrays_to_scalar +def asin(x): + return ivy.asin(x) + + +@from_zero_dim_arrays_to_scalar +def atan(x): + return ivy.atan(x) + + +@from_zero_dim_arrays_to_scalar +def atan2(x, y): + return ivy.atan2(x, y) + + +@from_zero_dim_arrays_to_scalar +def cos(x): + return ivy.cos(x) + + +@from_zero_dim_arrays_to_scalar +def sin(x): + return ivy.sin(x) + + +@from_zero_dim_arrays_to_scalar +def tan(x): + return ivy.tan(x) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def dist(p, q): + return ivy.vector_norm(ivy.subtract(p, q)) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def hypot(*coordinates): + return ivy.vector_norm(coordinates) From a272b532afdebf638aad232cdd3890a86db1e81f Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 17:18:39 +0300 Subject: [PATCH 16/24] added wrappers for exp2, expm1, log1p, log10 functions and manually tested --- .../math/power_and_logarithmic_functions.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py index d308eef0a1f41..4a23c7e6ad373 100644 --- a/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py +++ b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py @@ -29,3 +29,23 @@ def log2(x): @from_zero_dim_arrays_to_scalar def exp(x): return ivy.exp(x) + + +@from_zero_dim_arrays_to_scalar +def exp2(x): + return ivy.exp2(x) + + +@from_zero_dim_arrays_to_scalar +def expm1(x): + return ivy.expm1(x) + + +@from_zero_dim_arrays_to_scalar +def log1p(x): + return ivy.log1p(x) + + +@from_zero_dim_arrays_to_scalar +def log10(x): + return ivy.log10(x) From 3cd99a8be4601886aee98b89a47d1998dc9caf25 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 17:34:15 +0300 Subject: [PATCH 17/24] compositionally implemented cbrt and manually tested --- .../builtins/math/power_and_logarithmic_functions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py index 4a23c7e6ad373..08d2a90805f2a 100644 --- a/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py +++ b/ivy/functional/frontends/builtins/math/power_and_logarithmic_functions.py @@ -49,3 +49,8 @@ def log1p(x): @from_zero_dim_arrays_to_scalar def log10(x): return ivy.log10(x) + + +@from_zero_dim_arrays_to_scalar +def cbrt(x): + return ivy.multiply(ivy.sign(x), ivy.pow(ivy.abs(x), ivy.divide(1, 3))) From b4eaff8aa5ccb7f16d63db4bea045930cf657074 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 17:42:15 +0300 Subject: [PATCH 18/24] upd jax config within decorator when jax is set as a backend --- ivy/functional/frontends/builtins/func_wrapper.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index b6b720b155a68..64cd2a82d49b1 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -64,9 +64,15 @@ def _inputs_to_ivy_arrays_builtins(*args, **kwargs): def outputs_to_frontend_arrays(fn: Callable) -> Callable: @functools.wraps(fn) def _outputs_to_frontend_arrays(*args, **kwargs): + # update config for jax backend + if ivy.current_backend_str() == "jax": + import jax + + jax.config.update("jax_enable_x64", True) + ret, frontend_array = fn(*args, **kwargs) - if isinstance(ret, ivy.Array) or ivy.is_native_array(ret): + if isinstance(ret, (ivy.Array, ivy.NativeArray)): return frontend_array(ret) return ret From e9892e073dc4f378501dc1a4ae78cf200d28ffe4 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Fri, 18 Aug 2023 18:21:43 +0300 Subject: [PATCH 19/24] added handling for tuple in from_zero_dim_array_to_scalar --- ivy/functional/frontends/builtins/func_wrapper.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index 64cd2a82d49b1..66091420da150 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -87,8 +87,15 @@ def from_zero_dim_arrays_to_scalar(fn: Callable) -> Callable: @functools.wraps(fn) def _from_zero_dim_arrays_to_scalar(*args, **kwargs): ret = fn(*args, **kwargs) - if len(ret) > 0: + + if isinstance(ret, tuple): + return ivy.nested_map( + ret, lambda x: ivy.to_scalar(x) if len(x) == 0 else x, shallow=False + ) + + elif len(ret) > 0: return ret + return ivy.to_scalar(ret) return _from_zero_dim_arrays_to_scalar From ef7545fdf3e192142b1fb00b72660709ef88a780 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Mon, 21 Aug 2023 11:46:06 +0300 Subject: [PATCH 20/24] implemented number theoretic and representation functions and manually tested --- .../frontends/builtins/math/__init__.py | 2 + ..._theoretic_and_representation_functions.py | 103 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py diff --git a/ivy/functional/frontends/builtins/math/__init__.py b/ivy/functional/frontends/builtins/math/__init__.py index 3971dd13d3398..cc97c249e7d9f 100644 --- a/ivy/functional/frontends/builtins/math/__init__.py +++ b/ivy/functional/frontends/builtins/math/__init__.py @@ -8,3 +8,5 @@ from .hyperbolic_functions import * from . import trigonometric_functions from .trigonometric_functions import * +from . import number_theoretic_and_representation_functions +from .number_theoretic_and_representation_functions import * diff --git a/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py b/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py new file mode 100644 index 0000000000000..e6bd42a759894 --- /dev/null +++ b/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py @@ -0,0 +1,103 @@ +import ivy +from ivy.functional.frontends.builtins.func_wrapper import ( + to_ivy_arrays_and_back, + from_zero_dim_arrays_to_scalar, +) + + +@from_zero_dim_arrays_to_scalar +def ceil(x): + return ivy.ceil(x).astype(int) + + +@from_zero_dim_arrays_to_scalar +def copysign(x, y): + return ivy.copysign(x, y) + + +@from_zero_dim_arrays_to_scalar +def fabs(x): + return ivy.abs(x).astype(float) + + +@from_zero_dim_arrays_to_scalar +def floor(x): + return ivy.floor(x).astype(int) + + +@to_ivy_arrays_and_back +def fmod(x, y): + return ivy.fmod(x, y).astype(float) + + +@from_zero_dim_arrays_to_scalar +def frexp(x): + return ivy.frexp(x) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def fsum(x): + return ivy.sum(x).astype(float) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def gcd(*integers): + return ivy.gcd(*integers) + + +@from_zero_dim_arrays_to_scalar +def isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0): + return ivy.isclose(a, b, rtol=rel_tol, atol=abs_tol) + + +@from_zero_dim_arrays_to_scalar +def isfinite(x): + return ivy.isfinite(x) + + +@from_zero_dim_arrays_to_scalar +def isinf(x): + return ivy.isinf(x) + + +@from_zero_dim_arrays_to_scalar +def isnan(x): + return ivy.isnan(x) + + +@from_zero_dim_arrays_to_scalar +def isqrt(x): + return ivy.floor(ivy.sqrt(x)).astype(int) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def lcm(*integers): + return ivy.lcm(*integers) + + +@from_zero_dim_arrays_to_scalar +def ldexp(x, i): + return ivy.ldexp(x, i) + + +@from_zero_dim_arrays_to_scalar +def modf(x): + return ivy.modf(x) + + +@from_zero_dim_arrays_to_scalar +def nextafter(x, y): + return ivy.nextafter(x, y) + + +@from_zero_dim_arrays_to_scalar +def remainder(x, y): + return ivy.remainder(x, y).astype(float) + + +@from_zero_dim_arrays_to_scalar +def trunc(x): + return ivy.trunc(x) From 2dc4e8bc7edc0c5093ecb9097ab572348ddc772c Mon Sep 17 00:00:00 2001 From: illia-bab Date: Mon, 21 Aug 2023 17:39:52 +0300 Subject: [PATCH 21/24] minor fix to from_zero+dim_arrays_to_scalar to deal with scalars --- ivy/functional/frontends/builtins/func_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index 66091420da150..ee2f1716d377d 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -93,7 +93,7 @@ def _from_zero_dim_arrays_to_scalar(*args, **kwargs): ret, lambda x: ivy.to_scalar(x) if len(x) == 0 else x, shallow=False ) - elif len(ret) > 0: + elif isinstance(ret, (int, float, bool, complex)) or len(ret) > 0: return ret return ivy.to_scalar(ret) From d3e8e36ce681f30e27a074a48042e08aa209a5a9 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Tue, 22 Aug 2023 15:05:54 +0300 Subject: [PATCH 22/24] modify inputs_to_ivy_arrays when no args passed and add default behaviour to lcm and gcd --- ivy/functional/frontends/builtins/func_wrapper.py | 2 +- .../math/number_theoretic_and_representation_functions.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index ee2f1716d377d..a38d0d2c7683d 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -54,7 +54,7 @@ def _inputs_to_ivy_arrays_builtins(*args, **kwargs): ivy_kwargs = ivy.nested_map(kwargs, _to_ivy_array, shallow=False) # array is the first argument given to a function - frontend_array = _infer_return_array(args[0]) + frontend_array = _infer_return_array(args[0]) if len(args) != 0 else None return fn(*ivy_args, **ivy_kwargs), frontend_array diff --git a/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py b/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py index e6bd42a759894..de85448af4e12 100644 --- a/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py +++ b/ivy/functional/frontends/builtins/math/number_theoretic_and_representation_functions.py @@ -44,6 +44,8 @@ def fsum(x): @to_ivy_arrays_and_back @from_zero_dim_arrays_to_scalar def gcd(*integers): + if len(integers) == 0: + return 0 return ivy.gcd(*integers) @@ -75,6 +77,8 @@ def isqrt(x): @to_ivy_arrays_and_back @from_zero_dim_arrays_to_scalar def lcm(*integers): + if len(integers) == 0: + return 1 return ivy.lcm(*integers) From 434d8bbee53776a173906d7c8cc2a8d01872d1c5 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Tue, 22 Aug 2023 17:20:17 +0300 Subject: [PATCH 23/24] implemented min/max frontends and manually tested --- .../frontends/builtins/built_in_functions.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/ivy/functional/frontends/builtins/built_in_functions.py b/ivy/functional/frontends/builtins/built_in_functions.py index cb28934748c3a..b9962e52b1a21 100644 --- a/ivy/functional/frontends/builtins/built_in_functions.py +++ b/ivy/functional/frontends/builtins/built_in_functions.py @@ -1,4 +1,5 @@ import ivy +import ivy.functional.frontends.builtins as builtins_frontend from ivy.functional.frontends.builtins.func_wrapper import ( from_zero_dim_arrays_to_scalar, to_ivy_arrays_and_back, @@ -35,3 +36,55 @@ def round(number, ndigits=None): if not ndigits: return ivy.round(number) return ivy.round(number, decimals=ndigits) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def min(*args, default=None, key=None): + # arguments are empty + if len(args) == 0: + # in this case default should be provided + if not default: + raise ValueError("default must be provided for empty input") + return default + + # this means we deal with iterable rather than separate arguments + elif len(args) == 1 and not ivy.isscalar(args[0]): + # pass iterable to the same func + return builtins_frontend.min(*args[0], default=default, key=key) + + # if keyfunc provided, map all args to it + if key: + mapped_args = ivy.map(key, constant=None, unique={"x": args}, mean=False) + idx = ivy.argmin(mapped_args) + # argmin always returns array, convert it to scalar + idx = ivy.to_scalar(idx) + return args[idx] + + return ivy.min(args) + + +@to_ivy_arrays_and_back +@from_zero_dim_arrays_to_scalar +def max(*args, default=None, key=None): + # arguments are empty + if len(args) == 0: + # in this case default should be provided + if not default: + raise ValueError("default must be provided for empty input") + return default + + # this means we deal with iterable rather than separate arguments + elif len(args) == 1 and not ivy.isscalar(args[0]): + # pass iterable to the same func + return builtins_frontend.max(*args[0], default=default, key=key) + + # if keyfunc provided, map all args to it + if key: + mapped_args = ivy.map(key, constant=None, unique={"x": args}, mean=False) + idx = ivy.argmax(mapped_args) + # argmin always returns array, convert it to scalar + idx = ivy.to_scalar(idx) + return args[idx] + + return ivy.max(args) From 57cfbca1f53ea6001f2fa6c71704367c438925b2 Mon Sep 17 00:00:00 2001 From: illia-bab Date: Wed, 23 Aug 2023 17:12:53 +0300 Subject: [PATCH 24/24] enable ivy array in _infer_return_array and add set/unset default dtypes in outputs_to_frontend_arrays --- ivy/functional/frontends/builtins/func_wrapper.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ivy/functional/frontends/builtins/func_wrapper.py b/ivy/functional/frontends/builtins/func_wrapper.py index a38d0d2c7683d..a0c8e12695a6a 100644 --- a/ivy/functional/frontends/builtins/func_wrapper.py +++ b/ivy/functional/frontends/builtins/func_wrapper.py @@ -1,4 +1,5 @@ import functools +import platform from typing import Callable, Iterable from importlib import import_module @@ -24,7 +25,7 @@ def _infer_return_array(x: Iterable) -> Callable: # if function's input is a scalar, list, or tuple # convert to current backend's frontend array - if "builtins" in module_path: + if "builtins" in module_path or isinstance(x, ivy.Array): cur_backend = ivy.current_backend_str() module_str = cur_backend if len(cur_backend) != 0 else "numpy" @@ -70,8 +71,18 @@ def _outputs_to_frontend_arrays(*args, **kwargs): jax.config.update("jax_enable_x64", True) + ( + ivy.set_default_int_dtype("int32") + if platform.system() == "Windows" + else ivy.set_default_int_dtype("int64") + ) + ivy.set_default_float_dtype("float64") + ret, frontend_array = fn(*args, **kwargs) + ivy.unset_default_float_dtype() + ivy.unset_default_int_dtype() + if isinstance(ret, (ivy.Array, ivy.NativeArray)): return frontend_array(ret) return ret