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

Issue:
Feature request: Serialization and deserialization using serde
An implementation similar to how [ndarray](https://github.com/rust-ndarray/ndarray) implements  [serde](https://github.com/serde-rs/serde) for serialization and deserialization would be a useful feature to see.


Discussion / hints:
I don't know how ndarray is implemented but Array from arrayfire-rust is a RAII style construct.

I think this can be implemented by calling `Array::host` method to fetch data from GPU memory to host memory - at which point this regular array can be serialized using serde as usually.

Having said that, above logic can be easily implemented at user level too since I wouldn't imagine if we would do anything different from the above.

@umar456 @syurkevi  any suggestions ?

My only concern is, is it worth it to add a whole new runtime crate dependency just for serialization when it can be very easily implemented using function `Array::host` ? Perhaps, serialization feature as separate crate makes sense :thinking: 
@9prady9 serde is the defacto standard for serialization and deserialization in Rust. It would be great if there was a feature for ArrayFire Rust that would enable serde serialization. Otherwise I'd have to implement it myself, which is a) more complicated and b) re-creates a lot of work that belongs in this repo IMO.
I came across this: https://github.com/shibii/arrayfire_serde but it is for an older version. I forked it here:
https://github.com/ethanhs/arrayfire_serde
@ethanhs What all types would require this support in your opinion ? I haven't looked at the repository you shared yet, but it does talk about sedre support for all types of ArrayFire.

I have removed the `discussion` tag for this issue. We can work on this feature, but unfortunately I can't give an exact ETA on this. You are welcome to contribute the change if you are interested working on it.
@9prady9 The crate I forked implements it for `Array`, `Dim4`, and `DType`.
@JonathanWoollett-Light @ethanhs https://github.com/arrayfire/arrayfire-rust/pull/250 is making good progress. Feel free to test it out. You would have to enable `afserde` feature to build the serde support while building the project. You can do so via the following command 
```
cargo test --all-features --features=afserde -- --nocapture --test serde
```

https://arrayfire.org/arrayfire-rust/arrayfire/index.html#structs That's the list of structs. I have listed the structs that need serde support to the best of my knowledge in the above mentioned PR description. If any other struct needs it, please do let me know.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e5d7eb7a3..5d7be837e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -48,8 +48,8 @@ jobs:
                   export AF_PATH=${GITHUB_WORKSPACE}/afbin
                   export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${AF_PATH}/lib64
                   echo "Using cargo version: $(cargo --version)"
-                  cargo build --all
-                  cargo test --no-fail-fast
+                  cargo build --all --all-features
+                  cargo test --no-fail-fast --all-features
 
     format:
         name: Format Check
diff --git a/Cargo.toml b/Cargo.toml
index 6e98e6e46..dcb76bd7c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,15 +46,19 @@ statistics = []
 vision = []
 default = ["algorithm", "arithmetic", "blas", "data", "indexing", "graphics", "image", "lapack",
 "ml", "macros", "random", "signal", "sparse", "statistics", "vision"]
+afserde = ["serde"]
 
 [dependencies]
 libc = "0.2"
 num  = "0.2"
 lazy_static = "1.0"
 half = "1.5.0"
+serde = { version = "1.0", features = ["derive"], optional = true }
 
 [dev-dependencies]
 half = "1.5.0"
+serde_json = "1.0"
+bincode = "1.3"
 
 [build-dependencies]
 serde_json = "1.0"
diff --git a/README.md b/README.md
index e13021a19..24126f7f1 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Only, Major(M) & Minor(m) version numbers need to match. *p1* and *p2* are patch
 
 ## Supported platforms
 
-Linux, Windows and OSX. Rust 1.15.1 or higher is required.
+Linux, Windows and OSX. Rust 1.31 or newer is required.
 
 ## Use from Crates.io [![][6]][7] [![][8]][9]
 
diff --git a/cuda-interop/Cargo.toml b/cuda-interop/Cargo.toml
index 19fa09369..0a47799f7 100644
--- a/cuda-interop/Cargo.toml
+++ b/cuda-interop/Cargo.toml
@@ -19,7 +19,7 @@ rustacuda = "0.1"
 rustacuda_core = "0.1"
 
 [[example]]
-name = "custom_kernel"
+name = "afcuda_custom_kernel"
 path = "examples/custom_kernel.rs"
 
 [[example]]
diff --git a/cuda-interop/examples/cuda_af_app.rs b/cuda-interop/examples/cuda_af_app.rs
index 7868402c5..b1f966cf0 100644
--- a/cuda-interop/examples/cuda_af_app.rs
+++ b/cuda-interop/examples/cuda_af_app.rs
@@ -1,6 +1,5 @@
 use arrayfire::{af_print, dim4, info, set_device, Array};
 use rustacuda::prelude::*;
-use rustacuda::*;
 
 fn main() {
     // MAKE SURE to do all rustacuda initilization before arrayfire API's
diff --git a/opencl-interop/Cargo.toml b/opencl-interop/Cargo.toml
index b416d3548..6ee2a97d5 100644
--- a/opencl-interop/Cargo.toml
+++ b/opencl-interop/Cargo.toml
@@ -18,7 +18,7 @@ cl-sys = "0.4.2"
 ocl-core = "0.11.2"
 
 [[example]]
-name = "custom_kernel"
+name = "afocl_custom_kernel"
 path = "examples/custom_kernel.rs"
 
 [[example]]
diff --git a/opencl-interop/examples/custom_kernel.rs b/opencl-interop/examples/custom_kernel.rs
index 6075aec24..33efc0f1e 100644
--- a/opencl-interop/examples/custom_kernel.rs
+++ b/opencl-interop/examples/custom_kernel.rs
@@ -22,7 +22,7 @@ fn main() {
     let af_ctx = afcl::get_context(false);
     let af_que = afcl::get_queue(false);
 
-    let devid = unsafe { ocl_core::DeviceId::from_raw(af_did) };
+    let _devid = unsafe { ocl_core::DeviceId::from_raw(af_did) };
     let contx = unsafe { ocl_core::Context::from_raw_copied_ptr(af_ctx) };
     let queue = unsafe { ocl_core::CommandQueue::from_raw_copied_ptr(af_que) };
 
diff --git a/src/core/array.rs b/src/core/array.rs
index ce70ae896..75d61c55a 100644
--- a/src/core/array.rs
+++ b/src/core/array.rs
@@ -851,6 +851,73 @@ pub fn is_eval_manual() -> bool {
     }
 }
 
+#[cfg(feature = "afserde")]
+mod afserde {
+    // Reimport required from super scope
+    use super::{Array, DType, Dim4, HasAfEnum};
+
+    use serde::de::{Deserializer, Error, Unexpected};
+    use serde::ser::Serializer;
+    use serde::{Deserialize, Serialize};
+
+    #[derive(Debug, Serialize, Deserialize)]
+    struct ArrayOnHost<T: HasAfEnum + std::fmt::Debug> {
+        dtype: DType,
+        shape: Dim4,
+        data: Vec<T>,
+    }
+
+    /// Serialize Implementation of Array
+    impl<T> Serialize for Array<T>
+    where
+        T: std::default::Default + std::clone::Clone + Serialize + HasAfEnum + std::fmt::Debug,
+    {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let mut vec = vec![T::default(); self.elements()];
+            self.host(&mut vec);
+            let arr_on_host = ArrayOnHost {
+                dtype: self.get_type(),
+                shape: self.dims().clone(),
+                data: vec,
+            };
+            arr_on_host.serialize(serializer)
+        }
+    }
+
+    /// Deserialize Implementation of Array
+    impl<'de, T> Deserialize<'de> for Array<T>
+    where
+        T: Deserialize<'de> + HasAfEnum + std::fmt::Debug,
+    {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            match ArrayOnHost::<T>::deserialize(deserializer) {
+                Ok(arr_on_host) => {
+                    let read_dtype = arr_on_host.dtype;
+                    let expected_dtype = T::get_af_dtype();
+                    if expected_dtype != read_dtype {
+                        let error_msg = format!(
+                            "data type is {:?}, deserialized type is {:?}",
+                            expected_dtype, read_dtype
+                        );
+                        return Err(Error::invalid_value(Unexpected::Enum, &error_msg.as_str()));
+                    }
+                    Ok(Array::<T>::new(
+                        &arr_on_host.data,
+                        arr_on_host.shape.clone(),
+                    ))
+                }
+                Err(err) => Err(err),
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::super::array::print;
@@ -1082,4 +1149,37 @@ mod tests {
         //    8.0000     8.0000     8.0000
         // ANCHOR_END: accum_using_channel
     }
+
+    #[cfg(feature = "afserde")]
+    mod serde_tests {
+        use super::super::Array;
+        use crate::algorithm::sum_all;
+        use crate::randu;
+
+        #[test]
+        fn array_serde_json() {
+            let input = randu!(u8; 2, 2);
+            let serd = match serde_json::to_string(&input) {
+                Ok(serialized_str) => serialized_str,
+                Err(e) => e.to_string(),
+            };
+
+            let deserd: Array<u8> = serde_json::from_str(&serd).unwrap();
+
+            assert_eq!(sum_all(&(input - deserd)), (0u32, 0u32));
+        }
+
+        #[test]
+        fn array_serde_bincode() {
+            let input = randu!(u8; 2, 2);
+            let encoded = match bincode::serialize(&input) {
+                Ok(encoded) => encoded,
+                Err(_) => vec![],
+            };
+
+            let decoded: Array<u8> = bincode::deserialize(&encoded).unwrap();
+
+            assert_eq!(sum_all(&(input - decoded)), (0u32, 0u32));
+        }
+    }
 }
diff --git a/src/core/defines.rs b/src/core/defines.rs
index 6c95f1385..d52b60b2e 100644
--- a/src/core/defines.rs
+++ b/src/core/defines.rs
@@ -2,9 +2,13 @@ use num::Complex;
 use std::fmt::Error as FmtError;
 use std::fmt::{Display, Formatter};
 
+#[cfg(feature = "afserde")]
+use serde::{Deserialize, Serialize};
+
 /// Error codes
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum AfError {
     /// The function returned successfully
     SUCCESS = 0,
@@ -52,6 +56,7 @@ pub enum AfError {
 /// Compute/Acceleration Backend
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum Backend {
     /// Default backend order: OpenCL -> CUDA -> CPU
     DEFAULT = 0,
@@ -103,6 +108,7 @@ impl Display for AfError {
 /// Types of Array data type
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum DType {
     /// 32 bit float
     F32 = 0,
@@ -135,6 +141,7 @@ pub enum DType {
 /// Dictates the interpolation method to be used by a function
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum InterpType {
     /// Nearest Neighbor interpolation method
     NEAREST = 0,
@@ -161,6 +168,7 @@ pub enum InterpType {
 /// Helps determine how to pad kernels along borders
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum BorderType {
     /// Pad using zeros
     ZERO = 0,
@@ -177,6 +185,7 @@ pub enum BorderType {
 /// Used by `regions` function to identify type of connectivity
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum Connectivity {
     /// North-East-South-West (N-E-S-W) connectivity from given pixel/point
     FOUR = 4,
@@ -187,6 +196,7 @@ pub enum Connectivity {
 /// Helps determine the size of output of convolution
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum ConvMode {
     /// Default convolution mode where output size is same as input size
     DEFAULT = 0,
@@ -197,6 +207,7 @@ pub enum ConvMode {
 /// Helps determine if convolution is in Spatial or Frequency domain
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum ConvDomain {
     /// ArrayFire chooses whether the convolution will be in spatial domain or frequency domain
     AUTO = 0,
@@ -209,6 +220,7 @@ pub enum ConvDomain {
 /// Error metric used by `matchTemplate` function
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum MatchType {
     /// Sum of Absolute Differences
     SAD = 0,
@@ -233,6 +245,7 @@ pub enum MatchType {
 /// Identify the color space of given image(Array)
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum ColorSpace {
     /// Grayscale color space
     GRAY = 0,
@@ -245,6 +258,7 @@ pub enum ColorSpace {
 /// Helps determine the type of a Matrix
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum MatProp {
     /// Default (no-op)
     NONE = 0,
@@ -276,6 +290,7 @@ pub enum MatProp {
 #[allow(non_camel_case_types)]
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum NormType {
     /// Treats input as a vector and return sum of absolute values
     VECTOR_1 = 0,
@@ -298,6 +313,7 @@ pub enum NormType {
 /// Dictates what color map is used for Image rendering
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum ColorMap {
     /// Default color map is grayscale range [0-1]
     DEFAULT = 0,
@@ -318,6 +334,7 @@ pub enum ColorMap {
 /// YCbCr Standards
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum YCCStd {
     /// ITU-R BT.601 (formerly CCIR 601) standard
     YCC_601 = 601,
@@ -330,6 +347,7 @@ pub enum YCCStd {
 /// Homography type
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum HomographyType {
     /// RANdom SAmple Consensus algorithm
     RANSAC = 0,
@@ -340,6 +358,7 @@ pub enum HomographyType {
 /// Plotting markers
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum MarkerType {
     /// No marker
     NONE = 0,
@@ -362,6 +381,7 @@ pub enum MarkerType {
 /// Image moment types
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum MomentType {
     /// Central moment of order (0 + 0)
     M00 = 1, // 1<<0
@@ -378,6 +398,7 @@ pub enum MomentType {
 /// Sparse storage format type
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum SparseFormat {
     /// Dense format
     DENSE = 0,
@@ -392,6 +413,7 @@ pub enum SparseFormat {
 /// Binary operation types for generalized scan functions
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum BinaryOp {
     /// Addition operation
     ADD = 0,
@@ -406,6 +428,7 @@ pub enum BinaryOp {
 /// Random engine types
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum RandomEngineType {
     ///Philox variant with N=4, W=32 and Rounds=10
     PHILOX_4X32_10 = 100,
@@ -424,16 +447,27 @@ pub const MERSENNE: RandomEngineType = RandomEngineType::MERSENNE_GP11213;
 /// Default RandomEngine that defaults to [PHILOX](./constant.PHILOX.html)
 pub const DEFAULT_RANDOM_ENGINE: RandomEngineType = PHILOX;
 
+#[cfg(feature = "afserde")]
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "Complex")]
+struct ComplexDef<T> {
+    re: T,
+    im: T,
+}
+
 /// Scalar value types
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum Scalar {
     /// 32 bit float
     F32(f32),
     /// 32 bit complex float
+    #[cfg_attr(feature = "afserde", serde(with = "ComplexDef"))]
     C32(Complex<f32>),
     /// 64 bit float
     F64(f64),
     /// 64 bit complex float
+    #[cfg_attr(feature = "afserde", serde(with = "ComplexDef"))]
     C64(Complex<f64>),
     /// 8 bit boolean
     B8(bool),
@@ -456,6 +490,7 @@ pub enum Scalar {
 /// Canny edge detector threshold operations types
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum CannyThresholdType {
     /// User has to define canny thresholds manually
     MANUAL = 0,
@@ -466,6 +501,7 @@ pub enum CannyThresholdType {
 /// Anisotropic diffusion flux equation types
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum DiffusionEq {
     /// Quadratic flux function
     QUADRATIC = 1,
@@ -478,6 +514,7 @@ pub enum DiffusionEq {
 /// Diffusion equation types
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum FluxFn {
     /// Quadratic flux function
     GRADIENT = 1,
@@ -490,6 +527,7 @@ pub enum FluxFn {
 /// topk function ordering
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum TopkFn {
     /// Top k min values
     MIN = 1,
@@ -502,6 +540,7 @@ pub enum TopkFn {
 /// Iterative Deconvolution Algorithm
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum IterativeDeconvAlgo {
     /// Land-Weber Algorithm
     LANDWEBER = 1,
@@ -514,6 +553,7 @@ pub enum IterativeDeconvAlgo {
 /// Inverse Deconvolution Algorithm
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum InverseDeconvAlgo {
     /// Tikhonov algorithm
     TIKHONOV = 1,
@@ -524,6 +564,7 @@ pub enum InverseDeconvAlgo {
 /// Gradient mode for convolution
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum ConvGradientType {
     /// Filter Gradient
     FILTER = 1,
@@ -538,6 +579,7 @@ pub enum ConvGradientType {
 /// Gradient mode for convolution
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum VarianceBias {
     /// Sample variance
     SAMPLE = 1,
@@ -550,9 +592,49 @@ pub enum VarianceBias {
 /// Gradient mode for convolution
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub enum CublasMathMode {
     /// To indicate use of Tensor Cores on CUDA capable GPUs
     TENSOR_OP = 1,
     /// Default i.e. tensor core operations will be avoided by the library
     DEFAULT = 0,
 }
+
+#[cfg(test)]
+mod tests {
+    #[cfg(feature = "afserde")]
+    mod serde_tests {
+        #[test]
+        fn test_enum_serde() {
+            use super::super::AfError;
+
+            let err_code = AfError::ERR_NO_MEM;
+            let serd = match serde_json::to_string(&err_code) {
+                Ok(serialized_str) => serialized_str,
+                Err(e) => e.to_string(),
+            };
+            assert_eq!(serd, "\"ERR_NO_MEM\"");
+
+            let deserd: AfError = serde_json::from_str(&serd).unwrap();
+            assert_eq!(deserd, AfError::ERR_NO_MEM);
+        }
+
+        #[test]
+        fn test_scalar_serde() {
+            use super::super::Scalar;
+            use num::Complex;
+
+            let scalar = Scalar::C32(Complex {
+                re: 1.0f32,
+                im: 1.0f32,
+            });
+            let serd = match serde_json::to_string(&scalar) {
+                Ok(serialized_str) => serialized_str,
+                Err(e) => e.to_string(),
+            };
+
+            let deserd: Scalar = serde_json::from_str(&serd).unwrap();
+            assert_eq!(deserd, scalar);
+        }
+    }
+}
diff --git a/src/core/dim4.rs b/src/core/dim4.rs
index 565b0f294..51a0120eb 100644
--- a/src/core/dim4.rs
+++ b/src/core/dim4.rs
@@ -1,8 +1,12 @@
 use std::fmt;
 use std::ops::{Index, IndexMut};
 
+#[cfg(feature = "afserde")]
+use serde::{Deserialize, Serialize};
+
 /// Dim4 is used to store [Array](./struct.Array.html) dimensions
 #[derive(Copy, Clone, PartialEq, Debug)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 pub struct Dim4 {
     dims: [u64; 4],
 }
@@ -117,3 +121,25 @@ impl Dim4 {
         &self.dims
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #[cfg(feature = "afserde")]
+    mod serde_tests {
+        use super::super::Dim4;
+        use crate::dim4;
+
+        #[test]
+        fn dim4_serde() {
+            let dims = dim4!(4, 4);
+            let serd = match serde_json::to_string(&dims) {
+                Ok(serialized_str) => serialized_str,
+                Err(e) => e.to_string(),
+            };
+            assert_eq!(serd, "{\"dims\":[4,4,1,1]}");
+
+            let deserd: Dim4 = serde_json::from_str(&serd).unwrap();
+            assert_eq!(deserd, dims);
+        }
+    }
+}
diff --git a/src/core/random.rs b/src/core/random.rs
index 5fff7747f..d948ed9e7 100644
--- a/src/core/random.rs
+++ b/src/core/random.rs
@@ -221,6 +221,50 @@ impl Drop for RandomEngine {
     }
 }
 
+#[cfg(feature = "afserde")]
+mod afserde {
+    // Reimport required from super scope
+    use super::{RandomEngine, RandomEngineType};
+
+    use serde::de::Deserializer;
+    use serde::ser::Serializer;
+    use serde::{Deserialize, Serialize};
+
+    #[derive(Debug, Serialize, Deserialize)]
+    struct RandEngine {
+        engine_type: RandomEngineType,
+        seed: u64,
+    }
+
+    /// Serialize Implementation of Array
+    impl Serialize for RandomEngine {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let r = RandEngine {
+                engine_type: self.get_type(),
+                seed: self.get_seed(),
+            };
+            r.serialize(serializer)
+        }
+    }
+
+    /// Deserialize Implementation of Array
+    #[cfg(feature = "afserde")]
+    impl<'de> Deserialize<'de> for RandomEngine {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            match RandEngine::deserialize(deserializer) {
+                Ok(r) => Ok(RandomEngine::new(r.engine_type, Some(r.seed))),
+                Err(err) => Err(err),
+            }
+        }
+    }
+}
+
 /// Get default random engine
 pub fn get_default_random_engine() -> RandomEngine {
     unsafe {
@@ -303,3 +347,27 @@ where
         temp.into()
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #[cfg(feature = "afserde")]
+    mod serde_tests {
+        use super::super::RandomEngine;
+        use crate::core::defines::RandomEngineType;
+
+        #[test]
+        #[cfg(feature = "afserde")]
+        fn random_engine_serde_bincode() {
+            let input = RandomEngine::new(RandomEngineType::THREEFRY_2X32_16, Some(2047));
+            let encoded = match bincode::serialize(&input) {
+                Ok(encoded) => encoded,
+                Err(_) => vec![],
+            };
+
+            let decoded: RandomEngine = bincode::deserialize(&encoded).unwrap();
+
+            assert_eq!(input.get_seed(), decoded.get_seed());
+            assert_eq!(input.get_type(), decoded.get_type());
+        }
+    }
+}
diff --git a/src/core/seq.rs b/src/core/seq.rs
index cba9f8516..06c6c2eb7 100644
--- a/src/core/seq.rs
+++ b/src/core/seq.rs
@@ -1,10 +1,13 @@
 use num::{One, Zero};
 
+#[cfg(feature = "afserde")]
+use serde::{Deserialize, Serialize};
 use std::default::Default;
 use std::fmt;
 
 /// Sequences are used for indexing Arrays
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[cfg_attr(feature = "afserde", derive(Serialize, Deserialize))]
 #[repr(C)]
 pub struct Seq<T> {
     begin: T,
@@ -55,3 +58,22 @@ impl<T: Copy> Seq<T> {
         self.step
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #[cfg(feature = "afserde")]
+    #[test]
+    fn seq_serde() {
+        use super::Seq;
+        use crate::seq;
+
+        let original = seq!(1:2:1);
+        let serd = match serde_json::to_string(&original) {
+            Ok(serialized_str) => serialized_str,
+            Err(e) => e.to_string(),
+        };
+
+        let deserd: Seq<i32> = serde_json::from_str(&serd).unwrap();
+        assert_eq!(deserd, original);
+    }
+}


Test patch: