Repository: ivy-llc/ivy
Pull request: fix: ivy.linalg.matrix_norm() for paddle backend
<!--
This template will help you to have a meaningful PR, please follow it and do not leave it blank.
-->

# PR Description
- Type-casting to the `dtype` argument through the `@infer_dtype` decorator and `ivy.astype(x, dtype).as_native()`.
- Added `dtype` argument to array and container methods.
- Added type-casting to `float32` for the `out` argument passed down to torch backend- `out` argument has to be of the same type as the return of `torch.linalg.matrix_norm()` (`float32`).
- Test coverage for all valid dtypes.

**Current issues**:
- Gradient tests for paddle have problems with `max()` and `min()` functions (I will write just for `max()`) due to different implementations between backends (look up `paddle.max()` vs `paddle.amax()` or `torch.max()` vs `torch.amax()`). Tensorflow, numpy, torch and jax pass all the tests alike. 

<!--
If there is no related issue, please add a short description about your PR.
-->

## Related Issue
Continuation of the issue https://github.com/unifyai/ivy/issues/28314 and PR https://github.com/unifyai/ivy/pull/28323

<!--
Please use this format to link other issues with their numbers: Close #123
https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->

Closes #28314 

## Checklist

- [ ] Did you add a function?
- [ ] Did you add the tests?
- [x] Did you run your tests and are your tests passing?
- [x] Did pre-commit not fail on any check?
- [x] Did you follow the steps we provided?

<!--
Please mark your PR as a draft if you realise after the fact that your tests are not passing or
that your pre-commit check has some failures.

Here are some relevant resources regarding tests and pre-commit:

https://unify.ai/docs/ivy/overview/deep_dive/ivy_tests.html
https://unify.ai/docs/ivy/overview/deep_dive/formatting.html#pre-commit

-->

### Socials

<!--
If you have Twitter, please provide it here otherwise just ignore this.
-->


Problem statement:
Fix Frontend Failing Test: torch - linalg.torch.linalg.matrix_norm
At the first glance, the possible reasons for errors:
- lack of `@with_supported_dtypes`/type-casting in some backends or unnecessary type-casting in the tests,
- lack of set tolerances and/or no bounds on values, possibly leading to the generation of values too big for the dtype,
- errors in the backend implementations (?) or some backends having limited implementations (not working at all for some dtypes).

Hints from issue discussion:
- [ ] #28323

Commit message: Delete pytestdebug.log

Patch to review:
diff --git a/ivy/data_classes/array/linear_algebra.py b/ivy/data_classes/array/linear_algebra.py
index 025932fa11f0c..0387ec59b3456 100644
--- a/ivy/data_classes/array/linear_algebra.py
+++ b/ivy/data_classes/array/linear_algebra.py
@@ -453,6 +453,7 @@ def matrix_norm(
         ord: Union[int, float, Literal[inf, -inf, "fro", "nuc"]] = "fro",
         axis: Tuple[int, int] = (-2, -1),
         keepdims: bool = False,
+        dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = None,
         out: Optional[ivy.Array] = None,
     ) -> ivy.Array:
         """ivy.Array instance method variant of ivy.matrix_norm. This method
@@ -472,6 +473,10 @@ def matrix_norm(
             If this is set to True, the axes which are normed over are left in
             the result as dimensions with size one. With this option the result will
             broadcast correctly against the original x. Default is False.
+        dtype
+            If specified, the input tensor is cast to dtype before
+            performingthe operation, and the returned tensor's type
+            will be dtype. Default: None
         out
             optional output array, for writing the result to. It must have a shape
             that the inputs broadcast to.
@@ -495,7 +500,7 @@ def matrix_norm(
                 [[11.4 ]]])
         """
         return ivy.matrix_norm(
-            self._data, ord=ord, axis=axis, keepdims=keepdims, out=out
+            self._data, ord=ord, axis=axis, keepdims=keepdims, dtype=dtype, out=out
         )
 
     def matrix_power(
diff --git a/ivy/data_classes/container/linear_algebra.py b/ivy/data_classes/container/linear_algebra.py
index 464b7be1fff7e..648ec4c601e2e 100644
--- a/ivy/data_classes/container/linear_algebra.py
+++ b/ivy/data_classes/container/linear_algebra.py
@@ -1385,6 +1385,7 @@ def _static_matrix_norm(
         ord: Union[int, float, Literal[inf, -inf, "fro", "nuc"], ivy.Container] = "fro",
         axis: Tuple[int, int, ivy.Container] = (-2, -1),
         keepdims: Union[bool, ivy.Container] = False,
+        dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype, ivy.Container]] = None,
         key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None,
         to_apply: Union[bool, ivy.Container] = True,
         prune_unapplied: Union[bool, ivy.Container] = False,
@@ -1408,6 +1409,10 @@ def _static_matrix_norm(
             If this is set to True, the axes which are normed over are left in the
             result as dimensions with size one. With this option the result will
             broadcast correctly against the original x. Default is ``False``.
+        dtype
+            If specified, the input tensor is cast to dtype before performing
+            the operation, and the returned tensor's type will be dtype.
+            Default: None
         key_chains
             The key-chains to apply or not apply the method to. Default is ``None``.
         to_apply
@@ -1458,6 +1463,7 @@ def _static_matrix_norm(
             ord=ord,
             axis=axis,
             keepdims=keepdims,
+            dtype=dtype,
             key_chains=key_chains,
             to_apply=to_apply,
             prune_unapplied=prune_unapplied,
@@ -1472,6 +1478,7 @@ def matrix_norm(
         ord: Union[int, float, Literal[inf, -inf, "fro", "nuc"], ivy.Container] = "fro",
         axis: Tuple[int, int, ivy.Container] = (-2, -1),
         keepdims: Union[bool, ivy.Container] = False,
+        dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype, ivy.Container]] = None,
         key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None,
         to_apply: Union[bool, ivy.Container] = True,
         prune_unapplied: Union[bool, ivy.Container] = False,
@@ -1495,6 +1502,10 @@ def matrix_norm(
             If this is set to True, the axes which are normed over are left in the
             result as dimensions with size one. With this option the result will
             broadcast correctly against the original x. Default is ``False``.
+        dtype
+            If specified, the input tensor is cast to dtype before performing
+            the operation, and the returned tensor's type will be dtype.
+            Default: None
         key_chains
             The key-chains to apply or not apply the method to. Default is ``None``.
         to_apply
@@ -1545,6 +1556,7 @@ def matrix_norm(
             ord=ord,
             axis=axis,
             keepdims=keepdims,
+            dtype=dtype,
             key_chains=key_chains,
             to_apply=to_apply,
             prune_unapplied=prune_unapplied,
diff --git a/ivy/functional/backends/jax/linear_algebra.py b/ivy/functional/backends/jax/linear_algebra.py
index 886d159961ef1..d482f91ba482f 100644
--- a/ivy/functional/backends/jax/linear_algebra.py
+++ b/ivy/functional/backends/jax/linear_algebra.py
@@ -195,14 +195,17 @@ def matrix_norm(
     out: Optional[JaxArray] = None,
 ) -> JaxArray:
     if dtype is not None:
-        x = ivy.astype(x, dtype)
+        x = ivy.astype(x, dtype).to_native()
     if hasattr(axis, "__iter__"):
         if not isinstance(axis, tuple):
             axis = tuple(axis)
     else:
         if not isinstance(axis, tuple):
             axis = (axis,)
-    return jnp.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    ret = jnp.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    if ivy.exists(out):
+        ivy.inplace_update(out, ret)
+    return ret
 
 
 @with_unsupported_dtypes({"0.4.24 and below": ("complex",)}, backend_version)
diff --git a/ivy/functional/backends/numpy/linear_algebra.py b/ivy/functional/backends/numpy/linear_algebra.py
index 98b5096a7c33c..1fe6f54152640 100644
--- a/ivy/functional/backends/numpy/linear_algebra.py
+++ b/ivy/functional/backends/numpy/linear_algebra.py
@@ -152,10 +152,13 @@ def matrix_norm(
     out: Optional[np.ndarray] = None,
 ) -> np.ndarray:
     if dtype is not None:
-        x = ivy.astype(x, dtype)
+        x = ivy.astype(x, dtype).to_native()
     if not isinstance(axis, tuple):
         axis = tuple(axis)
-    return np.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    ret = np.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    if ivy.exists(out):
+        ivy.inplace_update(out, ret)
+    return ret
 
 
 def matrix_power(
diff --git a/ivy/functional/backends/paddle/linear_algebra.py b/ivy/functional/backends/paddle/linear_algebra.py
index ddf447a62e946..ce651cdc08e4c 100644
--- a/ivy/functional/backends/paddle/linear_algebra.py
+++ b/ivy/functional/backends/paddle/linear_algebra.py
@@ -267,7 +267,7 @@ def matrix_norm(
     out: Optional[paddle.Tensor] = None,
 ) -> paddle.Tensor:
     if dtype is not None:
-        x = ivy.astype(x, dtype=dtype)
+        x = ivy.astype(x, dtype).to_native()
     axis_ = list(axis)  # paddle.moveaxis doesn't support tuple axes
     if ord == "nuc":
         x = paddle.moveaxis(x, axis_, [-2, -1])
@@ -279,40 +279,40 @@ def matrix_norm(
             axis=-1,
         )
     elif ord == 1:
-        ret = paddle_backend.max(
-            paddle.sum(paddle_backend.abs(x), axis=axis[0], keepdim=True),
+        ret = paddle.amax(
+            paddle_backend.sum(paddle.abs(x), axis=axis[0], keepdims=True),
             axis=axis,
-            keepdims=keepdims,
+            keepdim=keepdims,
         )
     elif ord == -1:
-        ret = paddle_backend.min(
-            paddle.sum(paddle_backend.abs(x), axis=axis[0], keepdim=True),
+        ret = paddle.amin(
+            paddle_backend.sum(paddle.abs(x), axis=axis[0], keepdims=True),
             axis=axis,
-            keepdims=keepdims,
+            keepdim=keepdims,
         )
     elif ord == 2:
         x = paddle.moveaxis(x, axis_, [-2, -1])
-        ret = paddle_backend.max(
+        ret = paddle.max(
             paddle_backend.svd(x)[1],
             axis=-1,
         )
     elif ord == -2:
         x = paddle.moveaxis(x, axis_, [-2, -1])
-        ret = paddle_backend.min(
+        ret = paddle.min(
             paddle_backend.svd(x)[1],
             axis=-1,
         )
     elif ord == float("inf"):
-        ret = paddle_backend.max(
-            paddle.sum(paddle.abs(x), axis=axis[1], keepdim=True),
+        ret = paddle.amax(
+            paddle_backend.sum(paddle.abs(x), axis=axis[1], keepdims=True),
             axis=axis,
-            keepdims=keepdims,
+            keepdim=keepdims,
         )
     elif ord == float("-inf"):
-        ret = paddle_backend.min(
-            paddle.sum(paddle.abs(x), axis=axis[1], keepdim=True),
+        ret = paddle.amin(
+            paddle_backend.sum(paddle.abs(x), axis=axis[1], keepdims=True),
             axis=axis,
-            keepdims=keepdims,
+            keepdim=keepdims,
         )
     else:
         ret = paddle.linalg.norm(x, p=ord, axis=axis, keepdim=keepdims)
@@ -324,6 +324,8 @@ def matrix_norm(
             # although expand_dims support tuple axes, we have to loop
             # over the axes because it faces problems when the input is a scalar
             ret = paddle_backend.expand_dims(ret, axis=dim % x.ndim)
+    if ivy.exists(out):
+        ivy.inplace_update(out, ret)
     return ret
 
 
diff --git a/ivy/functional/backends/paddle/statistical.py b/ivy/functional/backends/paddle/statistical.py
index 4a0376cd2cf17..ff799b336c5fb 100644
--- a/ivy/functional/backends/paddle/statistical.py
+++ b/ivy/functional/backends/paddle/statistical.py
@@ -81,12 +81,12 @@ def max(
     ret_dtype = x.dtype
     if paddle.is_complex(x):
         const = paddle.to_tensor(1j, dtype=x.dtype)
-        real_max = paddle.max(x.real(), axis=axis, keepdim=keepdims)
+        real_max = paddle.amax(x.real(), axis=axis, keepdim=keepdims)
         imag = paddle.where(
             x.real() == real_max, x.imag(), paddle.full_like(x.imag(), -1e10)
         )
         # we consider the number with the biggest real and imag part
-        img_max = paddle.max(imag, axis=axis, keepdim=keepdims)
+        img_max = paddle.amax(imag, axis=axis, keepdim=keepdims)
         img_max = paddle.cast(img_max, x.dtype)
         return paddle.add(
             paddle.cast(real_max, x.dtype), paddle.multiply(img_max, const)
diff --git a/ivy/functional/backends/tensorflow/linear_algebra.py b/ivy/functional/backends/tensorflow/linear_algebra.py
index 230d91a7be0f3..8d70461660fe9 100644
--- a/ivy/functional/backends/tensorflow/linear_algebra.py
+++ b/ivy/functional/backends/tensorflow/linear_algebra.py
@@ -292,7 +292,7 @@ def matrix_norm(
     out: Optional[Union[tf.Tensor, tf.Variable]] = None,
 ) -> Union[tf.Tensor, tf.Variable]:
     if dtype is not None:
-        x = ivy.astype(x, dtype)
+        x = ivy.astype(x, dtype).to_native()
     if ord == "nuc":
         x = tf.experimental.numpy.moveaxis(x, axis, (-2, -1))
         ret = tf.reduce_sum(
@@ -323,6 +323,8 @@ def matrix_norm(
     if keepdims and ord in [-2, "nuc"]:
         for dim in axis:
             ret = tf.expand_dims(ret, dim % tf.rank(x))
+    if ivy.exists(out):
+        ivy.inplace_update(out, ret)
     return ret
 
 
diff --git a/ivy/functional/backends/torch/linear_algebra.py b/ivy/functional/backends/torch/linear_algebra.py
index a22ed63c897b1..d9ed15f0b799b 100644
--- a/ivy/functional/backends/torch/linear_algebra.py
+++ b/ivy/functional/backends/torch/linear_algebra.py
@@ -193,7 +193,9 @@ def matmul(
 matmul.support_native_out = True
 
 
-@with_unsupported_dtypes({"2.2 and below": ("float16", "complex")}, backend_version)
+@with_supported_dtypes(
+    {"2.2 and below": ("float32", "float64", "complex32", "complex64")}, backend_version
+)
 def matrix_norm(
     x: torch.Tensor,
     /,
diff --git a/ivy/functional/frontends/torch/linalg.py b/ivy/functional/frontends/torch/linalg.py
index d0db7936cc929..3df44b0ae3fa7 100644
--- a/ivy/functional/frontends/torch/linalg.py
+++ b/ivy/functional/frontends/torch/linalg.py
@@ -173,9 +173,9 @@ def matrix_exp(A):
     {"2.2 and below": ("float32", "float64", "complex32", "complex64")}, "torch"
 )
 def matrix_norm(input, ord="fro", dim=(-2, -1), keepdim=False, *, dtype=None, out=None):
-    if dtype is not None:
-        input = ivy.astype(input, dtype)
-    return ivy.matrix_norm(input, ord=ord, axis=dim, keepdims=keepdim, out=out)
+    return ivy.matrix_norm(
+        input, ord=ord, axis=dim, keepdims=keepdim, dtype=dtype, out=out
+    )
 
 
 @to_ivy_arrays_and_back
diff --git a/ivy/functional/ivy/linear_algebra.py b/ivy/functional/ivy/linear_algebra.py
index 434c25997a68b..11b4c46fc986d 100644
--- a/ivy/functional/ivy/linear_algebra.py
+++ b/ivy/functional/ivy/linear_algebra.py
@@ -13,6 +13,7 @@
     handle_array_like_without_promotion,
     handle_device,
     handle_backend_invalid,
+    infer_dtype,
 )
 from ivy.utils.exceptions import handle_exceptions
 
@@ -1129,14 +1130,15 @@ def matmul(
     )
 
 
-@handle_exceptions
-@handle_backend_invalid
-@handle_nestable
-@handle_array_like_without_promotion
-@handle_out_argument
-@to_native_arrays_and_back
-@handle_array_function
 @handle_device
+@infer_dtype
+@handle_array_function
+@to_native_arrays_and_back
+@handle_out_argument
+@handle_array_like_without_promotion
+@handle_nestable
+@handle_backend_invalid
+@handle_exceptions
 def matrix_norm(
     x: Union[ivy.Array, ivy.NativeArray],
     /,
diff --git a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py
index 4b75c6fdb4482..f7af9305a5ba8 100644
--- a/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py
+++ b/ivy_tests/test_ivy/test_frontends/test_torch/test_linalg.py
@@ -899,7 +899,7 @@ def test_torch_matrix_exp(
 @handle_frontend_test(
     fn_tree="torch.linalg.matrix_norm",
     dtype_values_axis=helpers.dtype_values_axis(
-        available_dtypes=helpers.get_dtypes("float_and_complex"),
+        available_dtypes=helpers.get_dtypes("float"),
         min_num_dims=2,
         min_axes_size=2,
         max_axes_size=2,
@@ -911,7 +911,7 @@ def test_torch_matrix_exp(
     ),
     ord=st.sampled_from(["fro", "nuc", np.inf, -np.inf, 1, -1, 2, -2]),
     keepdim=st.booleans(),
-    dtypes=helpers.get_dtypes("float_and_complex", none=True, full=False),
+    dtypes=helpers.get_dtypes("float", none=True, full=False),
 )
 def test_torch_matrix_norm(
     *,
@@ -926,10 +926,8 @@ def test_torch_matrix_norm(
     on_device,
 ):
     input_dtype, x, axis = dtype_values_axis
-    if dtypes[0] is not None and "complex128" in input_dtype[0]:
-        dtypes[0] = input_dtype[0]
     if dtypes[0] is not None:
-        dtypes[0] = input_dtype[0][:-2] + max([input_dtype[0][-2:], dtypes[0][-2:]])
+        dtypes[0] = input_dtype[0][0:-2] + max([input_dtype[0][-2:], dtypes[0][-2:]])
 
     helpers.test_frontend_function(
         input_dtypes=input_dtype,
diff --git a/ivy_tests/test_ivy/test_functional/test_core/test_linalg.py b/ivy_tests/test_ivy/test_functional/test_core/test_linalg.py
index b6ca29eacb3ab..4d3830bd775ec 100644
--- a/ivy_tests/test_ivy/test_functional/test_core/test_linalg.py
+++ b/ivy_tests/test_ivy/test_functional/test_core/test_linalg.py
@@ -643,27 +643,39 @@ def test_matmul(*, x, y, test_flags, backend_fw, fn_name, on_device):
 
 # matrix_norm
 @handle_test(
+    ground_truth_backend="torch",
     fn_tree="functional.ivy.matrix_norm",
-    # ground_truth_backend="numpy",
     dtype_value_axis=helpers.dtype_values_axis(
         available_dtypes=helpers.get_dtypes("float"),
         min_num_dims=2,
         valid_axis=True,
         min_axes_size=2,
         max_axes_size=2,
+        max_value=1e4,
+        min_value=-1e4,
+        abs_smallest_val=1e-4,
         force_tuple_axis=True,
         allow_neg_axes=False,
+        safety_factor_scale="log",
+        large_abs_safety_factor=1.5,
+        small_abs_safety_factor=1.5,
     ),
     kd=st.booleans(),
     ord=st.sampled_from((-2, -1, 1, 2, -float("inf"), float("inf"), "fro", "nuc")),
+    dtypes=helpers.get_dtypes("float", none=True, full=False),
 )
 def test_matrix_norm(
-    *, dtype_value_axis, kd, ord, test_flags, backend_fw, fn_name, on_device
+    *, dtype_value_axis, kd, ord, dtypes, test_flags, backend_fw, fn_name, on_device
 ):
-    dtype, x, axis = dtype_value_axis
+    input_dtype, x, axis = dtype_value_axis
+    if dtypes[0] is not None:
+        dtypes[0] = input_dtype[0][0:-2] + max([input_dtype[0][-2:], dtypes[0][-2:]])
+
+    test_flags.test_gradients = True
+
     assume(matrix_is_stable(x[0], cond_limit=10))
     helpers.test_function(
-        input_dtypes=dtype,
+        input_dtypes=input_dtype,
         test_flags=test_flags,
         backend_to_test=backend_fw,
         fn_name=fn_name,
@@ -673,6 +685,7 @@ def test_matrix_norm(
         x=x[0],
         axis=axis,
         keepdims=kd,
+        dtype=dtypes[0],
         ord=ord,
     )
 


Code review comments:
diff_hunk: @@ -643,27 +643,39 @@ def test_matmul(*, x, y, test_flags, backend_fw, fn_name, on_device):
 
 # matrix_norm
 @handle_test(
+    ground_truth_backend="torch",
     fn_tree="functional.ivy.matrix_norm",
-    # ground_truth_backend="numpy",
     dtype_value_axis=helpers.dtype_values_axis(
         available_dtypes=helpers.get_dtypes("float"),
         min_num_dims=2,
         valid_axis=True,
         min_axes_size=2,
         max_axes_size=2,
+        max_value=1e4,
+        min_value=-1e4,
+        abs_smallest_val=1e-4,
         force_tuple_axis=True,
         allow_neg_axes=False,
+        safety_factor_scale="log",
+        large_abs_safety_factor=1.5,
+        small_abs_safety_factor=1.5,
     ),
     kd=st.booleans(),
     ord=st.sampled_from((-2, -1, 1, 2, -float("inf"), float("inf"), "fro", "nuc")),
+    dtypes=helpers.get_dtypes("float", none=True, full=False),
 )
 def test_matrix_norm(
-    *, dtype_value_axis, kd, ord, test_flags, backend_fw, fn_name, on_device
+    *, dtype_value_axis, kd, ord, dtypes, test_flags, backend_fw, fn_name, on_device
 ):
-    dtype, x, axis = dtype_value_axis
+    input_dtype, x, axis = dtype_value_axis
+    if dtypes[0] is not None:
+        dtypes[0] = input_dtype[0][0:-2] + max([input_dtype[0][-2:], dtypes[0][-2:]])
+
+    test_flags.test_gradients = True
path: ivy_tests/test_ivy/test_functional/test_core/test_linalg.py
text: @user1:
let's not hardcode `test_gradients` to `True` for each test and instead pass the one that was generated by the strategy

@author:
Ah! Thanks, I forgot about that- I had that flag on while trying to figure out the reason behind failed gradient tests (it was the difference between `max` (`min`) and `amax` (`amin`) in paddle backend)- fixed!

diff_hunk: @@ -643,27 +643,39 @@ def test_matmul(*, x, y, test_flags, backend_fw, fn_name, on_device):
 
 # matrix_norm
 @handle_test(
+    ground_truth_backend="torch",
     fn_tree="functional.ivy.matrix_norm",
-    # ground_truth_backend="numpy",
     dtype_value_axis=helpers.dtype_values_axis(
         available_dtypes=helpers.get_dtypes("float"),
         min_num_dims=2,
         valid_axis=True,
         min_axes_size=2,
         max_axes_size=2,
+        max_value=1e4,
+        min_value=-1e4,
+        abs_smallest_val=1e-4,
         force_tuple_axis=True,
         allow_neg_axes=False,
+        safety_factor_scale="log",
path: ivy_tests/test_ivy/test_functional/test_core/test_linalg.py
text: @user1:
as `min_value`, `max_value`, etc. are specified, the `safety_factor` arguments isn't required

@author:
Fixed 👍

diff_hunk: @@ -152,10 +152,13 @@ def matrix_norm(
     out: Optional[np.ndarray] = None,
 ) -> np.ndarray:
     if dtype is not None:
-        x = ivy.astype(x, dtype)
+        x = ivy.astype(x, dtype).to_native()
     if not isinstance(axis, tuple):
         axis = tuple(axis)
-    return np.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    ret = np.linalg.norm(x, ord=ord, axis=axis, keepdims=keepdims)
+    if ivy.exists(out):
path: ivy/functional/backends/numpy/linear_algebra.py
text: @user1:
I think this should get handled automatically through the `handle_out_argument` decorator, could you please check if that's the case? Same applies to the other backends

@author:
You're 100% correct, I wasn't completely sure about that. Fixed! There are some small changes in torch backend related to the `out` argument as well- with said changes, now all the supported dtypes work correctly (not just float).

diff_hunk: @@ -1129,14 +1130,15 @@ def matmul(
     )
 
 
-@handle_exceptions
-@handle_backend_invalid
-@handle_nestable
-@handle_array_like_without_promotion
-@handle_out_argument
-@to_native_arrays_and_back
-@handle_array_function
 @handle_device
+@infer_dtype
path: ivy/functional/ivy/linear_algebra.py
text: @user1:
The order of the relevant decorators applied to the function should be the reverse of the order [here](https://github.com/unifyai/ivy/blob/36a36bf1da65fa6f6b1c4077b2a64d5e0904ac8a/ivy/func_wrapper.py#L17)

@author:
I took the order from here: https://unify.ai/docs/ivy/overview/deep_dive/function_wrapping.html so I'm guessing that deep-dive might need an update. Either way- fixed!