Repository: arrayfire/arrayfire-rust (language: rust)

Issue:
[BUG] Binary ops with a f16 array does not compile
Description
===========
<!--
* Details regarding the bug
* Did you build ArrayFire yourself or did you use the official installers
* Which backend is experiencing this issue? (CPU, CUDA, OpenCL)
* Do you have a workaround?
* Can the bug be reproduced reliably on your system?
* A clear and concise description of what you expected to happen.
* Run your executable with AF_TRACE=all and AF_PRINT_ERRORS=1 environment
  variables set.
* Screenshot or terminal output of the results from above run
-->

Binary operations (for eg: add) where one of the arrays is f16 does not compile, raising the following compiler error:

```
error[E0277]: the trait bound `f16: ImplicitPromote<f32>` is not satisfied
```

This is because the `ImplicitPromote<*>` traits have not been implemented for f16. There also doesn't seem to be a `ImplicitPromote<f16>` trait. Implementing these traits for f16 should fix the issue.

Reproducible Code and/or Steps
------------------------------
<!--
* Steps or code snippet that can reproduce the bug
* A full example will allow us to debug and fix the bug faster
-->

The following code:

```rust
use arrayfire::{add, print, Array, Dim4};
use half::f16;

fn main() {
    let values: [f32; 3] = [1.0, 2.0, 3.0];
    let dims = Dim4::new(&[3, 1, 1, 1]);

    let half_values = values.iter().map(|&x| f16::from_f32(x)).collect::<Vec<_>>();

    let vals = Array::new(&values, dims);
    let hvals = Array::new(&half_values, dims);

    print(&add(&hvals, &vals, false));
    print(&add(&vals, &hvals, false));
}
```

Gives these errors:

```
error[E0271]: type mismatch resolving `<Array<f32> as Convertable>::OutType == f16`
  --> src/main.rs:13:12
   |
13 |     print(&add(&hvals, &vals, false));
   |            ^^^ expected `f32`, found struct `f16`

error[E0277]: the trait bound `f32: ImplicitPromote<f16>` is not satisfied
   --> src/main.rs:13:24
    |
13  |     print(&add(&hvals, &vals, false));
    |            ---         ^^^^^ the trait `ImplicitPromote<f16>` is not implemented for `f32`
    |            |
    |            required by a bound introduced by this call
    |
    = help: the following implementations were found:
              <f32 as ImplicitPromote<bool>>
              <f32 as ImplicitPromote<f64>>
              <f32 as ImplicitPromote<i16>>
              <f32 as ImplicitPromote<i32>>
            and 95 others
note: required by a bound in `arrayfire::add`
   --> /home/ben/.cargo/registry/src/github.com-1ecc6299db9ec823/arrayfire-3.8.0/src/core/arith.rs:474:1
    |
474 | overloaded_binary_func!("Addition of two Arrays", add, add_helper, af_add);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `arrayfire::add`
    = note: this error originates in the macro `overloaded_binary_func` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0271]: type mismatch resolving `<Array<f32> as Convertable>::OutType == f16`
  --> src/main.rs:13:12
   |
13 |     print(&add(&hvals, &vals, false));
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f32`, found struct `f16`

error[E0277]: the trait bound `f32: ImplicitPromote<f16>` is not satisfied
   --> src/main.rs:14:16
    |
14  |     print(&add(&vals, &hvals, false));
    |            --- ^^^^^ the trait `ImplicitPromote<f16>` is not implemented for `f32`
    |            |
    |            required by a bound introduced by this call
    |
    = help: the following implementations were found:
              <f32 as ImplicitPromote<bool>>
              <f32 as ImplicitPromote<f64>>
              <f32 as ImplicitPromote<i16>>
              <f32 as ImplicitPromote<i32>>
            and 95 others
note: required by a bound in `arrayfire::add`
   --> /home/ben/.cargo/registry/src/github.com-1ecc6299db9ec823/arrayfire-3.8.0/src/core/arith.rs:474:1
    |
474 | overloaded_binary_func!("Addition of two Arrays", add, add_helper, af_add);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `arrayfire::add`
    = note: this error originates in the macro `overloaded_binary_func` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `f16: ImplicitPromote<f32>` is not satisfied
   --> src/main.rs:14:23
    |
14  |     print(&add(&vals, &hvals, false));
    |            ---        ^^^^^^ the trait `ImplicitPromote<f32>` is not implemented for `f16`
    |            |
    |            required by a bound introduced by this call
    |
note: required by a bound in `arrayfire::add`
   --> /home/ben/.cargo/registry/src/github.com-1ecc6299db9ec823/arrayfire-3.8.0/src/core/arith.rs:474:1
    |
474 | overloaded_binary_func!("Addition of two Arrays", add, add_helper, af_add);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `arrayfire::add`
    = note: this error originates in the macro `overloaded_binary_func` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `f16: ImplicitPromote<f32>` is not satisfied
  --> src/main.rs:13:12
   |
13 |     print(&add(&hvals, &vals, false));
   |            ^^^ the trait `ImplicitPromote<f32>` is not implemented for `f16`

error[E0277]: the trait bound `f32: ImplicitPromote<f16>` is not satisfied
  --> src/main.rs:14:12
   |
14 |     print(&add(&vals, &hvals, false));
   |            ^^^ the trait `ImplicitPromote<f16>` is not implemented for `f32`
   |
   = help: the following implementations were found:
             <f32 as ImplicitPromote<bool>>
             <f32 as ImplicitPromote<f64>>
             <f32 as ImplicitPromote<i16>>
             <f32 as ImplicitPromote<i32>>
           and 95 others

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
error: could not compile `afbug` due to 7 previous errors
```

System Information
------------------
<!--
Please provide the following information:
1. ArrayFire version
2. Devices installed on the system
3. (optional) Output from the af::info() function if applicable.
4. Output from the following scripts:

Run one of the following commands based on your OS

Linux:
```sh
lsb_release -a
if command -v nvidia-smi >/dev/null; then
  nvidia-smi --query-gpu="name,memory.total,driver_version" --format=csv -i 0
else
  echo "nvidia-smi not found"
fi
if command -v /opt/rocm/bin/rocm-smi >/dev/null; then
  /opt/rocm/bin/rocm-smi --showproductname
else
  echo "rocm-smi not found."
fi
if command -v clinfo > /dev/null; then
  clinfo
else
  echo "clinfo not found."
fi
```

Windows:
Download clinfo from https://github.com/Oblomov/clinfo

If you have NVIDIA GPUs. Run nvidia-smi usually located in
C:\Program Files\NVIDIA Corporation\NVSMI

Provide driver version for your GPU. (This is vendor specific)
-->

LSB Version:	:core-4.1-amd64:core-4.1-noarch
Distributor ID:	Fedora
Description:	Fedora release 36 (Thirty Six)
Release:	36
Codename:	ThirtySix
nvidia-smi not found
rocm-smi not found.
clinfo not found.

Checklist
---------

- [X] Using the latest available ArrayFire release
- [ ] GPU drivers are up to date



Discussion / hints:

Fix patch:
diff --git a/src/core/arith.rs b/src/core/arith.rs
index d9a39629..d0f1dda8 100644
--- a/src/core/arith.rs
+++ b/src/core/arith.rs
@@ -4,6 +4,8 @@ use super::defines::AfError;
 use super::dim4::Dim4;
 use super::error::HANDLE_ERROR;
 use super::util::{af_array, HasAfEnum, ImplicitPromote, IntegralType};
+
+use half::f16;
 use num::Zero;
 
 use libc::c_int;
@@ -758,6 +760,7 @@ arith_scalar_spec!(Complex<f64>);
 arith_scalar_spec!(Complex<f32>);
 arith_scalar_spec!(f64);
 arith_scalar_spec!(f32);
+arith_scalar_spec!(f16);
 arith_scalar_spec!(u64);
 arith_scalar_spec!(i64);
 arith_scalar_spec!(u32);
diff --git a/src/core/data.rs b/src/core/data.rs
index 62630f58..75308807 100644
--- a/src/core/data.rs
+++ b/src/core/data.rs
@@ -4,6 +4,7 @@ use super::dim4::Dim4;
 use super::error::HANDLE_ERROR;
 use super::util::{af_array, c32, c64, dim_t, u64_t, HasAfEnum};
 
+use half::f16;
 use libc::{c_double, c_int, c_uint};
 use std::option::Option;
 use std::vec::Vec;
@@ -231,6 +232,25 @@ impl ConstGenerator for bool {
     }
 }
 
+impl ConstGenerator for f16 {
+    type OutType = f16;
+
+    fn generate(&self, dims: Dim4) -> Array<Self::OutType> {
+        unsafe {
+            let mut temp: af_array = std::ptr::null_mut();
+            let err_val = af_constant(
+                &mut temp as *mut af_array,
+                f16::to_f64(*self),
+                dims.ndims() as c_uint,
+                dims.get().as_ptr() as *const dim_t,
+                12,
+            );
+            HANDLE_ERROR(AfError::from(err_val));
+            temp.into()
+        }
+    }
+}
+
 macro_rules! cnst {
     ($rust_type:ty, $ffi_type:expr) => {
         impl ConstGenerator for $rust_type {
diff --git a/src/core/num.rs b/src/core/num.rs
index 27f5ab26..73eca474 100644
--- a/src/core/num.rs
+++ b/src/core/num.rs
@@ -1,3 +1,4 @@
+
 pub trait Zero {
     fn zero() -> Self;
 }
@@ -28,6 +29,7 @@ zero_impl!(i64, 0);
 zero_impl!(isize, 0);
 zero_impl!(f32, 0.0);
 zero_impl!(f64, 0.0);
+zero_impl!(half::f16, half::f16::from_f32(0.0));
 
 macro_rules! one_impl {
     ( $t:ident, $o:expr ) => {
@@ -51,3 +53,4 @@ one_impl!(i64, 1);
 one_impl!(isize, 1);
 one_impl!(f32, 1.0);
 one_impl!(f64, 1.0);
+one_impl!(half::f16, half::f16::from_f32(1.0));
diff --git a/src/core/util.rs b/src/core/util.rs
index d5c10f4f..5a626d52 100644
--- a/src/core/util.rs
+++ b/src/core/util.rs
@@ -337,7 +337,7 @@ impl HasAfEnum for f16 {
     type AbsOutType = Self;
     type ArgOutType = Self;
     type UnaryOutType = Self;
-    type ComplexOutType = Complex<f16>;
+    type ComplexOutType = Complex<f32>;
     type MeanOutType = Self;
     type AggregateOutType = f32;
     type ProductOutType = f32;
@@ -468,6 +468,7 @@ macro_rules! implicit {
 implicit!(c64, c32 => c64);
 implicit!(c64, f64       => c64);
 implicit!(c64, f32       => c64);
+implicit!(c64, f16       => c64);
 implicit!(c64, i64       => c64);
 implicit!(c64, u64       => c64);
 implicit!(c64, i32       => c64);
@@ -481,6 +482,7 @@ implicit!(c64, u8        => c64);
 implicit!(c32, c64 => c64);
 implicit!(c32, f64       => c64);
 implicit!(c32, f32       => c32);
+implicit!(c32, f16       => c32);
 implicit!(c32, i64       => c32);
 implicit!(c32, u64       => c32);
 implicit!(c32, i32       => c32);
@@ -494,6 +496,7 @@ implicit!(c32, u8        => c32);
 implicit!(f64, c64 => c64);
 implicit!(f64, c32 => c64);
 implicit!(f64, f32       =>       f64);
+implicit!(f64, f16       =>       f64);
 implicit!(f64, i64       =>       f64);
 implicit!(f64, u64       =>       f64);
 implicit!(f64, i32       =>       f64);
@@ -507,6 +510,7 @@ implicit!(f64, u8        =>       f64);
 implicit!(f32, c64 => c64);
 implicit!(f32, c32 => c32);
 implicit!(f32, f64       =>       f64);
+implicit!(f32, f16       =>       f32);
 implicit!(f32, i64       =>       f32);
 implicit!(f32, u64       =>       f32);
 implicit!(f32, i32       =>       f32);
@@ -516,11 +520,26 @@ implicit!(f32, u16       =>       f32);
 implicit!(f32, bool      =>       f32);
 implicit!(f32, u8        =>       f32);
 
+//LHS is 16-bit floating point
+implicit!(f16, c64 => c64);
+implicit!(f16, c32 => c32);
+implicit!(f16, f64       =>       f64);
+implicit!(f16, f32       =>       f32);
+implicit!(f16, i64       =>       f16);
+implicit!(f16, u64       =>       f16);
+implicit!(f16, i32       =>       f16);
+implicit!(f16, u32       =>       f16);
+implicit!(f16, i16       =>       f16);
+implicit!(f16, u16       =>       f16);
+implicit!(f16, bool      =>       f16);
+implicit!(f16, u8        =>       f16);
+
 //LHS is 64-bit signed integer
 implicit!(i64, c64 => c64);
 implicit!(i64, c32 => c32);
 implicit!(i64, f64       =>       f64);
 implicit!(i64, f32       =>       f32);
+implicit!(i64, f16       =>       f16);
 implicit!(i64, u64       =>       u64);
 implicit!(i64, i32       =>       i64);
 implicit!(i64, u32       =>       i64);
@@ -534,6 +553,7 @@ implicit!(u64, c64 => c64);
 implicit!(u64, c32 => c32);
 implicit!(u64, f64       =>       f64);
 implicit!(u64, f32       =>       f32);
+implicit!(u64, f16       =>       f16);
 implicit!(u64, i64       =>       u64);
 implicit!(u64, i32       =>       u64);
 implicit!(u64, u32       =>       u64);
@@ -547,6 +567,7 @@ implicit!(i32, c64 => c64);
 implicit!(i32, c32 => c32);
 implicit!(i32, f64       =>       f64);
 implicit!(i32, f32       =>       f32);
+implicit!(i32, f16       =>       f16);
 implicit!(i32, i64       =>       i64);
 implicit!(i32, u64       =>       u64);
 implicit!(i32, u32       =>       u32);
@@ -560,6 +581,7 @@ implicit!(u32, c64 => c64);
 implicit!(u32, c32 => c32);
 implicit!(u32, f64       =>       f64);
 implicit!(u32, f32       =>       f32);
+implicit!(u32, f16       =>       f16);
 implicit!(u32, i64       =>       i64);
 implicit!(u32, u64       =>       u64);
 implicit!(u32, i32       =>       u32);
@@ -573,6 +595,7 @@ implicit!(i16, c64 => c64);
 implicit!(i16, c32 => c32);
 implicit!(i16, f64       =>       f64);
 implicit!(i16, f32       =>       f32);
+implicit!(i16, f16       =>       f16);
 implicit!(i16, i64       =>       i64);
 implicit!(i16, u64       =>       u64);
 implicit!(i16, i32       =>       i32);
@@ -586,6 +609,7 @@ implicit!(u16, c64 => c64);
 implicit!(u16, c32 => c32);
 implicit!(u16, f64       =>       f64);
 implicit!(u16, f32       =>       f32);
+implicit!(u16, f16       =>       f16);
 implicit!(u16, i64       =>       i64);
 implicit!(u16, u64       =>       u64);
 implicit!(u16, i32       =>       i32);
@@ -599,6 +623,7 @@ implicit!(u8, c64 => c64);
 implicit!(u8, c32 => c32);
 implicit!(u8, f64       =>       f64);
 implicit!(u8, f32       =>       f32);
+implicit!(u8, f16       =>       f16);
 implicit!(u8, i64       =>       i64);
 implicit!(u8, u64       =>       u64);
 implicit!(u8, i32       =>       i32);
@@ -612,6 +637,7 @@ implicit!(bool, c64 => c64);
 implicit!(bool, c32 => c32);
 implicit!(bool, f64       =>       f64);
 implicit!(bool, f32       =>       f32);
+implicit!(bool, f16       =>       f16);
 implicit!(bool, i64       =>       i64);
 implicit!(bool, u64       =>       u64);
 implicit!(bool, i32       =>       i32);
@@ -652,12 +678,18 @@ impl FloatingPoint for f32 {
         true
     }
 }
+impl FloatingPoint for f16 {
+    fn is_real() -> bool {
+        true
+    }
+}
 
 ///Trait qualifier to accept real data(numbers)
 pub trait RealFloating: HasAfEnum {}
 
 impl RealFloating for f64 {}
 impl RealFloating for f32 {}
+impl RealFloating for f16 {}
 
 ///Trait qualifier to accept complex data(numbers)
 pub trait ComplexFloating: HasAfEnum {}
@@ -670,6 +702,7 @@ pub trait RealNumber: HasAfEnum {}
 
 impl RealNumber for f64 {}
 impl RealNumber for f32 {}
+impl RealNumber for f16 {}
 impl RealNumber for i32 {}
 impl RealNumber for u32 {}
 impl RealNumber for i16 {}
@@ -830,6 +863,8 @@ impl Fromf64 for u32  { fn fromf64(value: f64) -> Self { value as Self }}
 #[rustfmt::skip]
 impl Fromf64 for i32  { fn fromf64(value: f64) -> Self { value as Self }}
 #[rustfmt::skip]
+impl Fromf64 for f16  { fn fromf64(value: f64) -> Self { f16::from_f64(value) }}
+#[rustfmt::skip]
 impl Fromf64 for u16  { fn fromf64(value: f64) -> Self { value as Self }}
 #[rustfmt::skip]
 impl Fromf64 for i16  { fn fromf64(value: f64) -> Self { value as Self }}
@@ -847,6 +882,7 @@ impl IndexableType for u64 {}
 impl IndexableType for f32 {}
 impl IndexableType for i32 {}
 impl IndexableType for u32 {}
+impl IndexableType for f16 {}
 impl IndexableType for i16 {}
 impl IndexableType for u16 {}
 impl IndexableType for u8 {}


Test patch: