Repository: rakytap/sequential-quantum-gate-decomposer (language: python)

Issue:
Jn export unitary



Discussion / hints:

Fix patch:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f18852e7..b2cabd92 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -108,7 +108,22 @@ find_package(PythonExtensions REQUIRED)
 
 set(CMAKE_VERBOSE_MAKEFILE ON)
 
+#################################################################
+# find MPI libraries if MPI is enables by environment variable PIQUASSOBOOST_MPI
+
+if(DEFINED ENV{QGD_MPI})
+  find_package(MPI REQUIRED)
+
+  # openmpi which has a different c++ bindings
+  #add_definitions(-DOMPI_SKIP_MPICXX)
+
+  # setting basic compile flags
+  list(APPEND CXX_FLAGS_DEBUG "-D__MPI__")
+  list(APPEND CXX_FLAGS_RELEASE "-D__MPI__")
 
+  list(APPEND EXTRA_INCLUDES "${MPI_C_INCLUDE_PATH}") 
+
+endif()
 
 #################################################################
 # find CBLAS libraries. Hints are given by numpy library directory via variable BLAS_DIR
@@ -325,12 +340,13 @@ else()
 endif()
 
 
-add_library( qgd SHARED
+list(APPEND qgd_files 
     ${PROJECT_SOURCE_DIR}/common/common.cpp
     ${PROJECT_SOURCE_DIR}/common/dot.cpp
     ${PROJECT_SOURCE_DIR}/common/matrix.cpp
     ${PROJECT_SOURCE_DIR}/common/matrix_real.cpp
     ${PROJECT_SOURCE_DIR}/common/logging.cpp
+    ${PROJECT_SOURCE_DIR}/common/Adam.cpp
     ${PROJECT_SOURCE_DIR}/gates/CNOT.cpp
     ${PROJECT_SOURCE_DIR}/gates/SYC.cpp
     ${PROJECT_SOURCE_DIR}/gates/CZ.cpp
@@ -362,8 +378,32 @@ add_library( qgd SHARED
 )
 
 
+if(DEFINED ENV{QGD_MPI})
+
+    list(APPEND qgd_files 
+    ${PROJECT_SOURCE_DIR}/common/mpi_base.cpp
+    )
+
+endif()
+
+
+if (DEFINED ENV{QGD_DFE})
+  list(APPEND qgd_files 
+      ${PROJECT_SOURCE_DIR}/common/common_DFE.cpp
+  )
+
+  list(APPEND CXX_FLAGS_DEBUG "-D__DFE__=1")
+  list(APPEND CXX_FLAGS_RELEASE "-D__DFE__=1")
+
+  find_library(DFE_LIB qgdDFE
+               PATHS $ENV{DFE_LIB_DIR}
+               REQUIRED)
+
+endif()
 
 
+add_library(qgd SHARED
+    ${qgd_files})
 
 # adding compile options
 target_compile_options(qgd PUBLIC
@@ -372,6 +412,8 @@ target_compile_options(qgd PUBLIC
     "$<$<CONFIG:Release>:${CXX_FLAGS_RELEASE}>"
 )
 
+
+
 # adding linking options
 target_link_libraries( qgd PRIVATE
     ${BLAS_LIBRARIES}
@@ -381,7 +423,10 @@ target_link_libraries( qgd PRIVATE
     ${TBBMALLOC_PROXY_LIB}
     ${TBB_LIB}
     ${GSL_LIB}
-)
+    ${DFE_LIB}
+    ${MPI_C_LIBRARIES}
+    )
+
 
 target_include_directories(qgd PRIVATE
                             .
@@ -391,6 +436,8 @@ target_include_directories(qgd PRIVATE
                             ./random_unitary/include
                             ${EXTRA_INCLUDES})
 
+
+
 set_target_properties(qgd PROPERTIES
     VERSION ${PROJECT_VERSION}
     PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/common/include/common.h
@@ -431,6 +478,8 @@ set_target_properties(qgd PROPERTIES
     PUBLIC_HEADER ${PROJECT_SOURCE_DIR}/random_unitary/include/Random_Orthogonal.h
 )
 
+
+
 configure_file(${PROJECT_SOURCE_DIR}/common/include/Config.h.in
                ${PROJECT_SOURCE_DIR}/common/include/Config.h)
 
diff --git a/common/Adam.cpp b/common/Adam.cpp
new file mode 100644
index 00000000..f0ef6102
--- /dev/null
+++ b/common/Adam.cpp
@@ -0,0 +1,174 @@
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file Adam.cpp
+    \brief A class for Adam optimization according to https://towardsdatascience.com/how-to-implement-an-adam-optimizer-from-scratch-76e7b217f1cc
+*/
+
+#include "Adam.h"
+#include "tbb/tbb.h"
+
+	
+
+/** Nullary constructor of the class
+@return An instance of the class
+*/
+Adam::Adam() {
+
+    beta1 = 0.68;
+    beta2 = 0.8;
+    epsilon = 1e-4;
+    eta = 0.001;
+
+
+
+    reset();
+	
+#if CBLAS==1
+    num_threads = mkl_get_max_threads();
+#elif CBLAS==2
+    num_threads = openblas_get_num_threads();
+#endif
+
+}
+
+
+/** Contructor of the class
+@brief Constructor of the class.
+@param ???????????????????????????????
+@param ???????????????????????????????
+@param ???????????????????????????????
+@return An instance of the class
+*/
+Adam::Adam( double beta1_in, double beta2_in, double epsilon_in, double eta_in ) {
+
+   
+    beta1 = beta1_in;
+    beta2 = beta2_in;
+    epsilon = epsilon_in;
+    eta = eta_in;
+
+
+    reset();
+
+
+#if CBLAS==1
+    num_threads = mkl_get_max_threads();
+#elif CBLAS==2
+    num_threads = openblas_get_num_threads();
+#endif
+
+}
+
+/**
+@brief Destructor of the class
+*/
+
+Adam::~Adam() {
+}
+
+
+
+/**
+@brief ?????????????
+*/
+void Adam::reset() {
+
+
+    mom = Matrix_real(0,0);
+    var = Matrix_real(0,0);
+
+    iter_t = 0;
+    beta1_t = 1.0;
+    beta2_t = 1.0;
+
+
+}
+
+
+/**
+@brief ?????????????
+*/
+void Adam::initialize_moment_and_variance(int parameter_num) {
+
+    mom = Matrix_real(parameter_num,1);
+    var = Matrix_real(parameter_num,1);  
+
+    memset( mom.get_data(), 0.0, mom.size()*sizeof(double) );
+    memset( var.get_data(), 0.0, var.size()*sizeof(double) );
+}
+
+
+/**
+@brief Call to set the number of gate blocks to be optimized in one shot
+@param optimization_block_in The number of gate blocks to be optimized in one shot
+*/
+void Adam::update( Matrix_real& parameters, Matrix_real& grad ) {
+
+    int parameter_num = parameters.size();
+    if ( parameter_num != grad.size() ) {
+        std::string error("Adam::update: number of parameters shoulod be equal to the number of elements in gradient vector");
+        throw error;
+    }
+
+    if ( mom.size() == 0 ) {
+        initialize_moment_and_variance( parameter_num );
+    }
+
+    if ( parameter_num != mom.size() ) {
+        std::string error("Adam::update: number of parameters shoulod be equal to the number of elements in momentum vector");
+        throw error;
+    }
+
+
+    double* mom_data = mom.get_data();
+    double* var_data = var.get_data();
+    double* grad_data = grad.get_data();
+    double* param_data = parameters.get_data();
+
+tbb::task_arena ta(4);
+ta.execute( [&](){
+    tbb::parallel_for( 0, parameter_num, 1, [&](int idx) {
+    //for (int idx=0; idx<parameter_num; idx++) {
+        mom_data[idx] = beta1 * mom_data[idx] + (1-beta1) * grad_data[idx];
+        var_data[idx] = beta2 * var_data[idx] + (1-beta2) * grad_data[idx] * grad_data[idx];
+
+        // bias correction step
+        beta1_t = beta1_t * beta1;
+        double mom_bias_corr = mom_data[idx]/(1-beta1_t);
+
+        beta2_t = beta2_t * beta2;
+        double var_bias_corr = var_data[idx]/(1-beta2_t);
+
+        // update parameters
+        param_data[idx] = param_data[idx] - eta * mom_bias_corr/(sqrt(var_bias_corr) + epsilon);
+        /*
+        if ( std::abs(eta * mom_bias_corr/(sqrt(var_bias_corr) + epsilon)) > 1e-3 ) {
+            std::cout << std::abs(eta * mom_bias_corr/(sqrt(var_bias_corr) + epsilon)) << std::endl;
+        }
+        */
+        
+    //}
+    });
+
+});
+
+    iter_t++;
+}
+
diff --git a/common/common.cpp b/common/common.cpp
index d32f4a0c..03b0251b 100644
--- a/common/common.cpp
+++ b/common/common.cpp
@@ -306,3 +306,43 @@ void mult( QGD_Complex16 a, Matrix& b ) {
 
 
 
+/**
+@brief ???????????????????
+@param a A complex numberr.
+*/
+double arg( const QGD_Complex16& a ) {
+
+
+    double angle;
+
+    if ( a.real > 0 && a.imag > 0 ) {
+        angle = std::atan(a.imag/a.real);
+        return angle;
+    }
+    else if ( a.real > 0 && a.imag <= 0 ) {
+        angle = std::atan(a.imag/a.real);
+        return angle;
+    }
+    else if ( a.real < 0 && a.imag > 0 ) {
+        angle = std::atan(a.imag/a.real) + M_PI;
+        return angle;
+    }
+    else if ( a.real < 0 && a.imag <= 0 ) {
+        angle = std::atan(a.imag/a.real) - M_PI;
+        return angle;
+    }
+    else if ( std::abs(a.real) < 1e-8 && a.imag > 0 ) {
+        angle = M_PI/2;
+        return angle;
+    }
+    else {
+        angle = -M_PI/2;
+        return angle;
+    }
+
+
+
+}
+
+
+
diff --git a/common/common_DFE.cpp b/common/common_DFE.cpp
new file mode 100644
index 00000000..0c02b680
--- /dev/null
+++ b/common/common_DFE.cpp
@@ -0,0 +1,92 @@
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file common.cpp
+    \brief Provides commonly used functions and wrappers to CBLAS functions.
+*/
+
+//
+// @brief A base class responsible for constructing matrices of C-NOT, U3
+// gates acting on the N-qubit space
+
+#include "common_DFE.h"
+
+/**
+@brief ????????????
+@return ??????????
+*/
+void uploadMatrix2DFE( Matrix& input ) {
+
+    // first convert the input to float32
+    matrix_base<Complex8> input32( input.rows, input.cols ); // number of columns needed to be made twice due to complex -> real tranformation
+
+    size_t element_num = input.size();
+    QGD_Complex16* input_data = input.get_data();
+    Complex8* input32_data = input32.get_data();
+    for ( size_t idx=0; idx<element_num; idx++) {
+        input32_data[idx].real = (float)(input_data[idx].real);
+        input32_data[idx].imag = (float)(input_data[idx].imag);
+    }
+    
+std::cout << "size in bytes of uploading: " << element_num*sizeof(float) << std::endl;    
+
+    // load the data to LMEM
+    load2LMEM( input32_data, input.rows, input.cols );
+
+}
+
+
+
+/**
+@brief ????????????
+@return ??????????
+*/
+/*
+void DownloadMatrixFromDFE( std::vector<Matrix>& output_vec ) {
+
+    // first convert the input to float32
+    size_t element_num = output_vec[0].size();
+
+    std::vector<matrix_base<Complex8>> output32_vec;
+    for( int idx=0; idx<4; idx++) {
+        output32_vec.push_back(matrix_base<Complex8>( output_vec[0].rows, output_vec[0].cols ));
+    }
+    
+    Complex8* output32_data[4];
+    for( int idx=0; idx<4; idx++) {
+        output32_data[idx] = output32_vec[idx].get_data();
+    }    
+    
+
+    // load the data to LMEM
+    downloadFromLMEM( output32_data, output_vec[0].rows );
+
+    for( int idx=0; idx<4; idx++) {
+	QGD_Complex16* output_data = output_vec[idx].get_data();    
+	Complex8* output32_data_loc = output32_data[idx]; 	
+        for ( size_t jdx=0; jdx<element_num; jdx++) {
+            output_data[jdx].real = (double)(output32_data_loc[jdx].real);
+            output_data[jdx].imag = (double)(output32_data_loc[jdx].imag);
+        }
+    }
+
+
+
+}
+*/
diff --git a/common/include/Adam.h b/common/include/Adam.h
new file mode 100644
index 00000000..6bff3688
--- /dev/null
+++ b/common/include/Adam.h
@@ -0,0 +1,113 @@
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file Adam.h
+    \brief Header file for a class containing basic methods for the decomposition process.
+*/
+
+#ifndef ADAM_H
+#define ADAM_H
+
+#include "matrix_real.h"
+#include <map>
+#include <cstdlib>
+#include <time.h>
+#include <ctime>
+
+
+/**
+@brief A class for Adam optimization according to https://towardsdatascience.com/how-to-implement-an-adam-optimizer-from-scratch-76e7b217f1cc
+*/
+class Adam  {
+
+
+public:
+  
+    // learning rate of the Adam algorithm
+    double eta;  
+
+
+protected:
+
+    /// Store the number of OpenMP threads. (During the calculations OpenMP multithreading is turned off.)
+    int num_threads;
+    /// parameter beta1 of the Adam algorithm
+    double beta1;
+    /// parameter beta2 of the Adam algorithm
+    double beta2;
+    // epsilon regularization parameter of the Adam algorithm
+    double epsilon;
+  
+    /// momentum parameter of the Adam algorithm
+    Matrix_real mom;
+    /// variance parameter of the Adam algorithm
+    Matrix_real var;
+    /// iteration index
+    int64_t iter_t;
+
+    /// beta1^t
+    double beta1_t;
+    /// beta2^t
+    double beta2_t;    
+
+
+
+public:
+
+/** Nullary constructor of the class
+@return An instance of the class
+*/
+Adam();
+
+/** Contructor of the class
+@brief Constructor of the class.
+@param Umtx_in The unitary matrix to be decomposed
+@param qbit_num_in The number of qubits spanning the unitary to be decomposed.
+@param initial_guess_in Type to guess the initial values for the optimization. Possible values: ZEROS=0, RANDOM=1, CLOSE_TO_ZERO=2
+@return An instance of the class
+*/
+Adam( double beta1_in, double beta2_in, double epsilon_in, double eta_in);
+
+/**
+@brief Destructor of the class
+*/
+virtual ~Adam();
+
+
+/**
+@brief ?????????????
+*/
+void reset();
+
+
+/**
+@brief ?????????????
+*/
+void initialize_moment_and_variance(int parameter_num);
+
+/**
+@brief Call to set the number of gate blocks to be optimized in one shot
+@param optimization_block_in The number of gate blocks to be optimized in one shot
+*/
+void update( Matrix_real& parameters, Matrix_real& grad );
+
+};
+
+
+#endif //Adam
diff --git a/common/include/QGDTypes.h b/common/include/QGDTypes.h
index f9db79d8..8e499254 100644
--- a/common/include/QGDTypes.h
+++ b/common/include/QGDTypes.h
@@ -77,9 +77,14 @@ struct gates_num {
   int com;
   /// The number of adaptive gates
   int adap;
+  /// The total number of gates
+  int total;
 };
 
 
 
 
+
+
+
 #endif
diff --git a/common/include/common.h b/common/include/common.h
index a8a6e204..896294c6 100644
--- a/common/include/common.h
+++ b/common/include/common.h
@@ -64,6 +64,8 @@ extern "C"
 }
 #endif
 
+
+
 /**
 @brief ?????
 */
@@ -155,4 +157,15 @@ QGD_Complex16 mult( double a, QGD_Complex16 b );
 */
 void mult( QGD_Complex16 a, Matrix& b );
 
+
+
+/**
+@brief ???????????????????
+@param a A complex numberr.
+*/
+double arg( const QGD_Complex16& a );
+
+
+
+
 #endif
diff --git a/common/include/common_DFE.h b/common/include/common_DFE.h
new file mode 100644
index 00000000..7469e519
--- /dev/null
+++ b/common/include/common_DFE.h
@@ -0,0 +1,124 @@
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file common_DFE.h
+    \brief Header file for DFE support
+*/
+
+#ifndef common_DFE_H
+#define common_DFE_H
+
+
+#include <omp.h>
+#include "QGDTypes.h"
+#include "dot.h"
+
+
+#include <string>
+#include <stdio.h>
+#include <iostream>
+#include <cmath>
+#include <vector>
+#include <cstring>
+#include <sstream>
+
+extern "C"
+{
+
+
+/**
+ * \brief ???????????
+ * 
+ */
+typedef struct {
+  float real;
+  float imag;
+} Complex8;
+
+
+/**
+ * \brief ???????????
+ * 
+ */
+typedef struct {
+	int32_t ThetaOver2;
+	int32_t Phi;
+	int32_t Lambda;
+	int8_t target_qbit;
+	int8_t control_qbit;
+	int8_t gate_type;
+	uint8_t metadata;
+} DFEgate_kernel_type;
+
+
+/**
+@brief ????????????
+@return ??????????
+*/
+int calcqgdKernelDFE(size_t rows, size_t cols, DFEgate_kernel_type* gates, int gatesNum, int gateSetNum, double* trace);
+
+/**
+@brief ????????????
+@return ??????????
+*/
+int load2LMEM( Complex8* data, size_t rows, size_t cols );
+
+/**
+@brief ????????????
+@return ??????????
+*/
+void releive_DFE();
+
+/**
+@brief ????????????
+@return ??????????
+*/
+int initialize_DFE();
+
+/**
+ * \brief ???????????
+ * 
+ */
+//int downloadFromLMEM( Complex8** data, size_t dim );
+
+
+/**
+@brief ????????????
+@return ??????????
+*/
+void uploadMatrix2DFE( Matrix& input );
+
+
+/**
+@brief ????????????
+@return ??????????
+*/
+//void DownloadMatrixFromDFE( std::vector<Matrix>& output_vec );
+
+/**
+@brief ????????????
+@return ??????????
+*/
+int get_chained_gates_num();
+
+}
+
+
+#endif
+
diff --git a/common/include/logging.h b/common/include/logging.h
index 036f46cf..cf6c503e 100644
--- a/common/include/logging.h
+++ b/common/include/logging.h
@@ -32,52 +32,60 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include <fstream>
 
 
+#ifdef __MPI__
+#include "mpi_base.h"
+#endif
+
 
 /**
 @brief A class containing basic methods for setting up the verbosity level. 
 */
+#ifdef __MPI__
+class logging : public mpi_base {
+#else
 class logging {
+#endif
 
 public:
 
 
-/// Set the verbosity level of the output messages. 
-int verbose; 
+    /// Set the verbosity level of the output messages. 
+    int verbose; 
 
-/// Logical variable. Set true to write output messages to  the 'debug.txt' file.
-bool debug; 	
+    /// Logical variable. Set true to write output messages to  the 'debug.txt' file.
+    bool debug; 	
 
-/// String variable. Set the debug file name. 
-std::string debugfile_name;
+    /// String variable. Set the debug file name. 
+    std::string debugfile_name;
 
 
-/** Nullary constructor of the class
-@return An instance of the class
-*/
-logging();
+    /** Nullary constructor of the class
+    @return An instance of the class
+    */
+    logging();
  
-/**
-@brief Call to print output messages in the function of the verbosity level.
-@param sstream The stringstream input to store the output messages.
-@param verbose_level Integer input. High level means write more to the standart output, 0 means write nothing. The default value is set to 1. 
-*/
-void print(const std::stringstream& sstream, int verbose_level=1) const; 
+    /**
+    @brief Call to print output messages in the function of the verbosity level.
+    @param sstream The stringstream input to store the output messages.
+    @param verbose_level Integer input. High level means write more to the standart output, 0 means write nothing. The default value is set to 1. 
+    */
+    void print(const std::stringstream& sstream, int verbose_level=1) const; 
 
 
 
 
-/**
-@brief Call to set the verbose attribute.
-@param verbose_in Integer variable. Set the number to specify the verbosity level for output messages.
-*/
-void set_verbose( int verbose_in );
+    /**
+    @brief Call to set the verbose attribute.
+    @param verbose_in Integer variable. Set the number to specify the verbosity level for output messages.
+    */
+    void set_verbose( int verbose_in );
 
 
-/**
-@brief Call to set the debugfile name.
-@param debugfile String variable. Set the debugfile name. 
-*/
-void set_debugfile( std::string debugfile );
+    /**
+    @brief Call to set the debugfile name.
+    @param debugfile String variable. Set the debugfile name. 
+    */
+    void set_debugfile( std::string debugfile );
 
 
 
diff --git a/common/include/mpi_base.h b/common/include/mpi_base.h
new file mode 100644
index 00000000..b93af092
--- /dev/null
+++ b/common/include/mpi_base.h
@@ -0,0 +1,66 @@
+/*brief Header file for a print class in order to control the verbosity levels of output messages. 
+*/
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file mpi_base.h
+    \brief Header file for a class containing data on the current MPI process
+*/
+
+#ifndef MPIBASE_H
+#define MPIBASE_H
+
+#include <iostream>
+
+
+#ifdef __MPI__
+#include <mpi.h>
+#endif // MPI	
+
+/**
+@brief A class containing data on the current MPI process
+*/
+class mpi_base {
+
+protected:
+
+
+#ifdef __MPI__
+    /// The number of processes
+    int world_size;
+    /// The rank of the MPI process
+    int current_rank;
+#endif
+
+
+public:
+
+    /** Nullary constructor of the class
+    @return An instance of the class
+    */
+    mpi_base();
+ 
+
+};
+
+
+
+
+
+#endif
diff --git a/common/include/numpy_interface.h b/common/include/numpy_interface.h
new file mode 100644
index 00000000..dca6b34c
--- /dev/null
+++ b/common/include/numpy_interface.h
@@ -0,0 +1,59 @@
+#ifndef NUMPY_INTERFACE_H
+#define NUMPY_INTERFACE_H
+
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include "matrix.h"
+#include "matrix_real.h"
+
+
+/**
+@brief Method to cleanup the memory when the python object becomes released
+@param capsule Pointer to the memory capsule
+*/
+void capsule_cleanup(PyObject* capsule);
+
+
+
+/**
+@brief Call to make a numpy array from data stored via void pointer.
+@param ptr pointer pointing to the data
+@param dim The number of dimensions
+@param shape array containing the dimensions.
+@param np_type The data type stored in the numpy array (see possible values at https://numpy.org/doc/1.17/reference/c-api.dtype.html)
+*/
+PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type);
+
+
+
+/**
+@brief Call to make a numpy array from an instance of matrix class.
+@param mtx a matrix instance
+*/
+PyObject* matrix_to_numpy( Matrix &mtx );
+
+
+/**
+@brief Call to make a numpy array from an instance of matrix class.
+@param mtx a matrix instance
+*/
+PyObject* matrix_real_to_numpy( Matrix_real &mtx );
+
+
+
+/**
+@brief Call to create a PIC matrix representation of a numpy array
+*/
+Matrix numpy2matrix(PyObject *arr);
+
+
+/**
+@brief Call to create a PIC matrix_real representation of a numpy array
+*/
+Matrix_real numpy2matrix_real(PyObject *arr);
+
+
+
+
+
+#endif
diff --git a/common/mpi_base.cpp b/common/mpi_base.cpp
new file mode 100644
index 00000000..324f676d
--- /dev/null
+++ b/common/mpi_base.cpp
@@ -0,0 +1,42 @@
+/*
+Created on Fri Jun 26 14:13:26 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+*/
+/*! \file mpi_base.cpp
+    \brief class containing data on the current MPI process
+*/
+
+#include "mpi_base.h"
+ 
+
+
+/** Nullary constructor of the class
+*/
+mpi_base::mpi_base() {
+
+#ifdef __MPI__
+    // Get the number of processes
+    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+    // Get the rank of the process
+    MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
+
+#endif
+
+}
+
diff --git a/common/numpy_interface.cpp b/common/numpy_interface.cpp
new file mode 100644
index 00000000..26ecc901
--- /dev/null
+++ b/common/numpy_interface.cpp
@@ -0,0 +1,186 @@
+#ifndef NUMPY_INTERFACE_H
+#define NUMPY_INTERFACE_H
+
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include "matrix.h"
+#include "matrix_real.h"
+
+
+/**
+@brief Method to cleanup the memory when the python object becomes released
+@param capsule Pointer to the memory capsule
+*/
+void capsule_cleanup(PyObject* capsule) {
+
+    void *memory = PyCapsule_GetPointer(capsule, NULL);
+    // I'm going to assume your memory needs to be freed with free().
+    // If it needs different cleanup, perform whatever that cleanup is
+    // instead of calling free().
+    scalable_aligned_free(memory);
+
+
+}
+
+
+
+
+/**
+@brief Call to make a numpy array from data stored via void pointer.
+@param ptr pointer pointing to the data
+@param dim The number of dimensions
+@param shape array containing the dimensions.
+@param np_type The data type stored in the numpy array (see possible values at https://numpy.org/doc/1.17/reference/c-api.dtype.html)
+*/
+PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type) {
+
+        if (PyArray_API == NULL) {
+            import_array();
+        }
+
+
+        // create numpy array
+        PyObject* arr = PyArray_SimpleNewFromData(dim, shape, np_type, ptr);
+
+        // set memory keeper for the numpy array
+        PyObject *capsule = PyCapsule_New(ptr, NULL, capsule_cleanup);
+        PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
+
+        return arr;
+
+}
+
+
+
+/**
+@brief Call to make a numpy array from an instance of matrix class.
+@param mtx a matrix instance
+*/
+PyObject* matrix_to_numpy( Matrix &mtx ) {
+        // initialize Numpy API
+        import_array();
+
+
+        npy_intp shape[2];
+        shape[0] = (npy_intp) mtx.rows;
+        shape[1] = (npy_intp) mtx.cols;
+
+        QGD_Complex16* data = mtx.get_data();
+        return array_from_ptr( (void*) data, 2, shape, NPY_COMPLEX128);
+
+
+}
+
+
+
+/**
+@brief Call to make a numpy array from an instance of matrix class.
+@param mtx a matrix instance
+*/
+PyObject* matrix_real_to_numpy( Matrix_real &mtx ) {
+        // initialize Numpy API
+        import_array();
+
+
+        npy_intp shape[2];
+        shape[0] = (npy_intp) mtx.rows;
+        shape[1] = (npy_intp) mtx.cols;
+
+        double* data = mtx.get_data();
+        return array_from_ptr( (void*) data, 2, shape, NPY_DOUBLE);
+
+
+}
+
+
+
+
+/**
+@brief Call to create a matrix representation of a numpy array
+*/
+Matrix
+numpy2matrix(PyObject *arr) {
+
+    if ( arr == Py_None ) {
+        return Matrix(0,0);
+    }
+
+#ifdef DEBUG
+    // test C-style contiguous memory allocation of the arrays
+    // in production this case has to be handled outside
+    assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
+#endif
+
+    // get the pointer to the data stored in the input matrices
+    QGD_Complex16* data = (QGD_Complex16*)PyArray_DATA(arr);
+
+    // get the dimensions of the array self->C
+    int dim_num = PyArray_NDIM( arr );
+    npy_intp* dims = PyArray_DIMS(arr);
+
+    // create PIC version of the input matrices
+    if (dim_num == 2) {
+        Matrix mtx = Matrix(data, dims[0], dims[1]);
+        return mtx;
+    }
+    else if (dim_num == 1) {
+        Matrix mtx = Matrix(data, dims[0], 1);
+        return mtx;
+    }
+    else {
+        std::cerr << "numpy2matrix: Wrong matrix dimension was given" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+
+
+}
+
+
+/**
+@brief Call to create a PIC matrix_real representation of a numpy array
+*/
+Matrix_real
+numpy2matrix_real(PyObject *arr) {
+
+
+    if ( arr == Py_None ) {
+        return Matrix_real(0,0);
+    }
+
+#ifdef DEBUG
+    // test C-style contiguous memory allocation of the arrays
+    // in production this case has to be handled outside
+    assert( PyArray_IS_C_CONTIGUOUS(arr) && "array is not memory contiguous" );
+#endif
+
+    // get the pointer to the data stored in the input matrices
+    double *data = (double *)PyArray_DATA(arr);
+
+    // get the dimensions of the array self->C
+    int dim_num = PyArray_NDIM( arr );
+    npy_intp* dims = PyArray_DIMS(arr);
+
+    // create PIC version of the input matrices
+    if (dim_num == 2) {
+        Matrix_real mtx = Matrix_real(data, dims[0], dims[1]);
+        return mtx;
+    }
+    else if (dim_num == 1) {
+        Matrix_real mtx = Matrix_real(data, dims[0], 1);
+        return mtx;
+    }
+    else {
+        std::cerr << "numpy2matrix_real: Wrong matrix dimension was given" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+
+
+}
+
+
+
+
+
+#endif
diff --git a/decomposition/Decomposition_Base.cpp b/decomposition/Decomposition_Base.cpp
index 26b797f9..3e6c5bf7 100644
--- a/decomposition/Decomposition_Base.cpp
+++ b/decomposition/Decomposition_Base.cpp
@@ -23,7 +23,6 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 
 #include "Decomposition_Base.h"
 
-	
 
 
 // default layer numbers
@@ -82,6 +81,10 @@ Decomposition_Base::Decomposition_Base() {
 
     // The convergence threshold in the optimization process
     convergence_threshold = 1e-5;
+    
+    //global phase of the unitary matrix
+    global_phase.real = 1;
+    global_phase.imag = 1;
 
 
 
@@ -91,6 +94,8 @@ Decomposition_Base::Decomposition_Base() {
     num_threads = openblas_get_num_threads();
 #endif
 
+
+
 }
 
 
@@ -108,7 +113,7 @@ Decomposition_Base::Decomposition_Base( Matrix Umtx_in, int qbit_num_in, guess_t
    
     // the unitary operator to be decomposed
     Umtx = Umtx_in;
-
+   
     // logical value describing whether the decomposition was finalized or not
     decomposition_finalized = false;
 
@@ -150,6 +155,10 @@ Decomposition_Base::Decomposition_Base( Matrix Umtx_in, int qbit_num_in, guess_t
 
     // The convergence threshold in the optimization process
     convergence_threshold = 1e-5;
+    
+    //global phase of the unitary matrix
+    global_phase.real = 1;
+    global_phase.imag = 1;
 
 
 #if CBLAS==1
@@ -158,6 +167,15 @@ Decomposition_Base::Decomposition_Base( Matrix Umtx_in, int qbit_num_in, guess_t
     num_threads = openblas_get_num_threads();
 #endif
 
+#ifdef __MPI__
+    // Get the number of processes
+    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
+
+    // Get the rank of the process
+    MPI_Comm_rank(MPI_COMM_WORLD, &current_rank);
+
+#endif
+
 }
 
 /**
@@ -392,11 +410,22 @@ void  Decomposition_Base::solve_optimization_problem( double* solution_guess, in
             for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
                 optimized_parameters_gsl->data[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
             }
+
+#ifdef __MPI__        
+            MPI_Bcast( (void*)optimized_parameters_gsl->data, parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+
+
         }
         else if ( initial_guess == CLOSE_TO_ZERO ) {
             for(int idx = 0; idx < parameter_num-solution_guess_num; idx++) {
                 optimized_parameters_gsl->data[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI/100;
             }
+
+#ifdef __MPI__        
+            MPI_Bcast( (void*)optimized_parameters_gsl->data, parameter_num, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+
         }
         else {
 	     std::stringstream sstream;
@@ -433,7 +462,7 @@ void  Decomposition_Base::solve_optimization_problem( double* solution_guess, in
         ////////////////////////////////////////
         // Start the iterations
         int iter_idx;
-        for ( iter_idx=0;  iter_idx<max_iterations+1; iter_idx++) {
+        for ( iter_idx=0; iter_idx<max_iterations; iter_idx++) {
 
             //determine the range of blocks to be optimalized togedther
             block_idx_end = block_idx_start - optimization_block;
@@ -481,6 +510,7 @@ void  Decomposition_Base::solve_optimization_problem( double* solution_guess, in
             // Create a general gate describing the cumulative effect of gates following the optimized gates
             if (block_idx_end > 0) {
                 fixed_gate_post->set_matrix( gates_mtxs_post[block_idx_end-1] );
+                gates.push_back( fixed_gate_post ); 
             }
             else {
                 // release gate products
@@ -489,7 +519,6 @@ void  Decomposition_Base::solve_optimization_problem( double* solution_guess, in
             }
 
             // create a list of gates for the optimization process
-            gates.push_back( fixed_gate_post ); 
             for ( int idx=block_idx_end; idx<block_idx_start; idx++ ) {
                 gates.push_back( gates_loc[idx] );
             }
@@ -563,7 +592,7 @@ void  Decomposition_Base::solve_optimization_problem( double* solution_guess, in
         }
 
 
-        if (iter_idx == max_iterations ) {            
+        if (iter_idx == max_iterations && max_iterations>1) {            
 		std::stringstream sstream;
 		sstream << "Reached maximal number of iterations" << std::endl << std::endl;
 		print(sstream, 1);
@@ -1169,7 +1198,7 @@ std::vector<Gate*> Decomposition_Base::prepare_gates_to_export( std::vector<Gate
             U3* u3_gate = static_cast<U3*>(gate);
 
             if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_theta_parameter()) {
-                vartheta = std::fmod( parameters[parameter_idx], 4*M_PI);
+                vartheta = std::fmod( 2*parameters[parameter_idx], 4*M_PI);
                 varphi = 0;
                 varlambda =0;
                 parameter_idx = parameter_idx + 1;
@@ -1188,13 +1217,13 @@ std::vector<Gate*> Decomposition_Base::prepare_gates_to_export( std::vector<Gate
                 parameter_idx = parameter_idx + 1;
             }
             else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_phi_parameter() ) {
-                vartheta = std::fmod( parameters[ parameter_idx ], 4*M_PI);
+                vartheta = std::fmod( 2*parameters[ parameter_idx ], 4*M_PI);
                 varphi = std::fmod( parameters[ parameter_idx+1 ], 2*M_PI);
                 varlambda = 0;
                 parameter_idx = parameter_idx + 2;
             }
             else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_lambda_parameter() ) {
-                vartheta = std::fmod( parameters[ parameter_idx ], 4*M_PI);
+                vartheta = std::fmod( 2*parameters[ parameter_idx ], 4*M_PI);
                 varphi = 0;
                 varlambda = std::fmod( parameters[ parameter_idx+1 ], 2*M_PI);
                 parameter_idx = parameter_idx + 2;
@@ -1206,7 +1235,7 @@ std::vector<Gate*> Decomposition_Base::prepare_gates_to_export( std::vector<Gate
                 parameter_idx = parameter_idx + 2;
             }
             else if ((u3_gate->get_parameter_num() == 3)) {
-                vartheta = std::fmod( parameters[ parameter_idx ], 4*M_PI);
+                vartheta = std::fmod( 2*parameters[ parameter_idx ], 4*M_PI);
                 varphi = std::fmod( parameters[ parameter_idx+1 ], 2*M_PI);
                 varlambda = std::fmod( parameters[ parameter_idx+2 ], 2*M_PI);
                 parameter_idx = parameter_idx + 3;
@@ -1232,7 +1261,7 @@ std::vector<Gate*> Decomposition_Base::prepare_gates_to_export( std::vector<Gate
 
             RX* rx_gate = static_cast<RX*>(gate);
 
-            vartheta = std::fmod( parameters[parameter_idx], 4*M_PI);
+            vartheta = std::fmod( 2*parameters[parameter_idx], 4*M_PI);
             parameter_idx = parameter_idx + 1;
 
 
@@ -1250,7 +1279,7 @@ std::vector<Gate*> Decomposition_Base::prepare_gates_to_export( std::vector<Gate
 
             RY* ry_gate = static_cast<RY*>(gate);
 
-            vartheta = std::fmod( parameters[parameter_idx], 4*M_PI);
+            vartheta = std::fmod( 2*parameters[parameter_idx], 4*M_PI);
             parameter_idx = parameter_idx + 1;
 
 
@@ -1458,6 +1487,75 @@ double Decomposition_Base::get_current_minimum( ) {
 
 }
 
+/**
+@brief Call to calculate new global phase 
+@param global_phase The value of the phase
+*/
+void Decomposition_Base::calculate_new_global_phase(QGD_Complex16 global_phase_new){
+	global_phase = mult(global_phase, global_phase_new);
+	return;
+}
+
+/**
+@brief Call to get global phase 
+@param global_phase The value of the phase
+*/
+QGD_Complex16 Decomposition_Base::get_global_phase( ){
+	return global_phase;
+}
 
+/**
+@brief Call to set global phase 
+@param global_phase_new The value of the new phase
+*/
+void Decomposition_Base::set_global_phase(double global_phase_new_angle){
+	global_phase.real = sqrt(2)*cos(global_phase_new_angle);
+	global_phase.imag = sqrt(2)*sin(global_phase_new_angle);
+	return;
+}
 
+/**
+@brief Call to apply global phase of U3 matrices to matrix
+@param global_phase The value of the phase
+*/
+void Decomposition_Base::apply_global_phase_to_u3(QGD_Complex16 global_phase_new, Matrix& u3_gate){
+	mult(global_phase_new, u3_gate);
+	return;
+}
 
+/**
+@brief Call to apply the current global phase to the unitary matrix
+@param global_phase The value of the phase
+*/
+void Decomposition_Base::apply_global_phase(){
+	mult(global_phase, Umtx);
+	set_global_phase(3.141592653589793 / 4);
+	return;
+}
+
+void Decomposition_Base::export_unitary(Matrix& Umtx, std::string& filename){
+	FILE* pFile;
+	char* c_filename = filename.c_str();
+	pFile = fopen(c_filename, "wb");
+    	if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
+    	fwrite(&Umtx.rows, sizeof(int), 1, pFile);
+   	fwrite(&Umtx.cols, sizeof(int), 1, pFile);          
+   	fwrite(Umtx.get_data(), sizeof(QGD_Complex16), Umtx.size(), pFile);
+    	fclose(pFile);
+	return;
+}
+
+Matrix Decomposition_Base::import_unitary_from_binary(std::string& filename){
+	FILE* pFile;
+	char* c_filename = filename.c_str();
+	int cols;
+	int rows;
+	pFile = fopen(c_filename, "rb");
+    	if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
+	fread(&rows, sizeof(int), 1, pFile);
+	fread(&cols, sizeof(int), 1, pFile);
+	Matrix Umtx_ = Matrix(rows, cols);
+	fread(Umtx_.get_data(), sizeof(QGD_Complex16), rows*cols, pFile);
+    	fclose(pFile);
+	return Umtx_;
+}
diff --git a/decomposition/N_Qubit_Decomposition.cpp b/decomposition/N_Qubit_Decomposition.cpp
index 5315ebb2..99db9b2f 100644
--- a/decomposition/N_Qubit_Decomposition.cpp
+++ b/decomposition/N_Qubit_Decomposition.cpp
@@ -33,8 +33,8 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 */
 N_Qubit_Decomposition::N_Qubit_Decomposition() : N_Qubit_Decomposition_Base() {
 
-    iter_max = 10;
-    gradient_threshold = 1e-1;
+    set_optimizer( BFGS );
+
 
 }
 
@@ -52,9 +52,7 @@ N_Qubit_Decomposition::N_Qubit_Decomposition() : N_Qubit_Decomposition_Base() {
 */
 N_Qubit_Decomposition::N_Qubit_Decomposition( Matrix Umtx_in, int qbit_num_in, bool optimize_layer_num_in, guess_type initial_guess_in= CLOSE_TO_ZERO ) : N_Qubit_Decomposition_Base(Umtx_in, qbit_num_in, optimize_layer_num_in, initial_guess_in) {
 
-    iter_max = 10;
-    gradient_threshold = 1e-1;
-
+    set_optimizer( BFGS );
 }
 
 
diff --git a/decomposition/N_Qubit_Decomposition_Base.cpp b/decomposition/N_Qubit_Decomposition_Base.cpp
index 5027047f..9ec5b20c 100644
--- a/decomposition/N_Qubit_Decomposition_Base.cpp
+++ b/decomposition/N_Qubit_Decomposition_Base.cpp
@@ -24,7 +24,18 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 
 #include "N_Qubit_Decomposition_Base.h"
 #include "N_Qubit_Decomposition_Cost_Function.h"
+#include "Adam.h"
 
+#include <fstream>
+
+#ifdef __DFE__
+#include "common_DFE.h"
+#endif
+
+
+static double adam_time = 0;
+static double bfgs_time = 0;
+static double pure_DFE_time = 0;
 
 
 /**
@@ -42,6 +53,12 @@ N_Qubit_Decomposition_Base::N_Qubit_Decomposition_Base() {
     // The global minimum of the optimization problem
     global_target_minimum = 0;
 
+    // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
+    adaptive_eta = true;
+
+    // parameter to contron the radius of parameter randomization around the curren tminimum
+    radius = 1.0;
+
 }
 
 /**
@@ -73,6 +90,12 @@ N_Qubit_Decomposition_Base::N_Qubit_Decomposition_Base( Matrix Umtx_in, int qbit
         }
     }
 
+    // logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
+    adaptive_eta = true;
+
+    // parameter to contron the radius of parameter randomization around the curren tminimum
+    radius = 1.0;
+
 
 }
 
@@ -208,14 +231,214 @@ void  N_Qubit_Decomposition_Base::final_optimization() {
 }
 
 
-
 /**
-// @brief Call to solve layer by layer the optimization problem. The optimalized parameters are stored in attribute optimized_parameters.
-// @param num_of_parameters Number of parameters to be optimized
-// @param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+@brief Call to solve layer by layer the optimization problem via calling one of the implemented algorithms. The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
 */
 void N_Qubit_Decomposition_Base::solve_layer_optimization_problem( int num_of_parameters, gsl_vector *solution_guess_gsl) {
 
+
+    
+
+    switch ( alg ) {
+        case ADAM:
+            solve_layer_optimization_problem_ADAM( num_of_parameters, solution_guess_gsl);
+            return;
+        case BFGS:
+            solve_layer_optimization_problem_BFGS( num_of_parameters, solution_guess_gsl);
+            return;
+        case BFGS2:
+            solve_layer_optimization_problem_BFGS2( num_of_parameters, solution_guess_gsl);
+            return;
+        default:
+            std::string error("N_Qubit_Decomposition_Base::solve_layer_optimization_problem: unimplemented optimization algorithm");
+            throw error;
+    }
+
+
+}
+
+
+/**
+@brief Call to solve layer by layer the optimization problem via ADAM algorithm. (optimal for larger problems) The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+*/
+void N_Qubit_Decomposition_Base::solve_layer_optimization_problem_ADAM( int num_of_parameters, gsl_vector *solution_guess_gsl) {
+
+        if (gates.size() == 0 ) {
+            return;
+        }
+
+
+        if (solution_guess_gsl == NULL) {
+            solution_guess_gsl = gsl_vector_alloc(num_of_parameters);
+        }
+
+
+        if (optimized_parameters_mtx.size() == 0) {
+            optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
+            memcpy(optimized_parameters_mtx.get_data(), solution_guess_gsl->data, num_of_parameters*sizeof(double) );
+        }
+
+
+        int random_shift_count = 0;
+        int sub_iter_idx = 0;
+        double current_minimum_hold = current_minimum;
+    
+
+        tbb::tick_count adam_start = tbb::tick_count::now();
+        adam_time = 0.0;
+pure_DFE_time = 0.0;
+        Adam optimizer;
+        optimizer.initialize_moment_and_variance( num_of_parameters );
+
+
+
+        // the array storing the optimized parameters
+        gsl_vector* grad_gsl = gsl_vector_alloc(num_of_parameters);
+        gsl_vector* solution_guess_tmp = gsl_vector_alloc(num_of_parameters);
+        memcpy(solution_guess_tmp->data, solution_guess_gsl->data, num_of_parameters*sizeof(double) );
+
+        Matrix_real solution_guess_tmp_mtx = Matrix_real( solution_guess_tmp->data, num_of_parameters, 1 );
+        Matrix_real grad_mtx = Matrix_real( grad_gsl->data, num_of_parameters, 1 );
+        //solution_guess_tmp_mtx.print_matrix();
+
+
+        double f0 = DBL_MAX;
+        std::stringstream sstream;
+        sstream << "iter_max: " << iter_max << std::endl;
+        print(sstream, 2);   
+
+        for ( int iter_idx=0; iter_idx<iter_max; iter_idx++ ) {
+
+            
+
+
+            optimization_problem_combined( solution_guess_tmp, (void*)(this), &f0, grad_gsl );
+    
+            if (sub_iter_idx == 1 ) {
+                current_minimum_hold = f0;  
+                if ( adaptive_eta )  { 
+                    optimizer.eta = optimizer.eta > 1e-3 ? optimizer.eta : 1e-3; 
+                }  
+            }
+
+
+            if (current_minimum_hold*0.95 > f0 || (current_minimum_hold*0.97 > f0 && f0 < 1e-3) ||  (current_minimum_hold*0.99 > f0 && f0 < 1e-4) ) {
+                sub_iter_idx = 0;
+                current_minimum_hold = f0;        
+            }
+    
+    
+            if (current_minimum > f0 ) {
+                current_minimum = f0;
+                memcpy( optimized_parameters_mtx.get_data(),  solution_guess_tmp->data, num_of_parameters*sizeof(double) );
+                //double new_eta = 1e-3 * f0 * f0;
+                if ( adaptive_eta )  {
+                    double new_eta = 1e-3 * f0;
+                    optimizer.eta = new_eta > 1e-6 ? new_eta : 1e-6;
+                }
+            }
+    
+
+            if ( iter_idx % 5000 == 0 ) {
+                std::stringstream sstream;
+                sstream << "processed iterations " << (double)iter_idx/iter_max*100 << "\%, current minimum:" << current_minimum << std::endl;
+                print(sstream, 0);   
+                std::string filename("initial_circuit_iteration.binary");
+                export_gate_list_to_binary(optimized_parameters_mtx, this, filename);
+            }
+
+//std::cout << grad_norm  << std::endl;
+            if (f0 < optimization_tolerance || random_shift_count > random_shift_count_max ) {
+                break;
+            }
+
+
+//grad_mtx.print_matrix();
+
+            
+            if ( sub_iter_idx> 2500 ) {
+
+                //random_shift_count++;
+                sub_iter_idx = 0;
+                random_shift_count++;
+                current_minimum_hold = current_minimum;        
+                int factor = rand() % 10 + 1;
+
+                std::stringstream sstream;
+                sstream << "leaving local minimum " << f0 << ", radius of randomization: " << radius << std::endl;
+                print(sstream, 0);   
+        
+                bool just_reset_optimizer = (rand() % 5) == 0; 
+                
+                if ( just_reset_optimizer ) {
+
+                }
+                else {
+        
+                    int changed_parameters = 0;
+                    for ( int jdx=0; jdx<num_of_parameters; jdx++) {
+                        if ( rand() % 3 == 0 ) {
+                            solution_guess_tmp->data[jdx] = optimized_parameters_mtx[jdx] + (2*double(rand())/double(RAND_MAX)-1)*2*M_PI*std::sqrt(f0)/factor*radius;
+//                            solution_guess_tmp->data[jdx] = optimized_parameters_mtx[jdx] + (2*double(rand())/double(RAND_MAX)-1)*2*M_PI*f0/factor*radius;
+                            //solution_guess_tmp->data[jdx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;            
+                            changed_parameters++;
+                        }
+                        else {
+                            solution_guess_tmp->data[jdx] = optimized_parameters_mtx[jdx];
+                        }
+                    }
+                    //std::cout << "changing " << double(changed_parameters)/num_of_parameters*100 << "\% of parameters in radius " << radius << std::endl;
+                    
+                }
+
+
+#ifdef __MPI__        
+                MPI_Bcast( (void*)solution_guess_tmp->data, num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+        
+                optimizer.reset();
+                optimizer.initialize_moment_and_variance( num_of_parameters );       
+        
+            }
+            else {
+                optimizer.update(solution_guess_tmp_mtx, grad_mtx);
+            }
+
+            sub_iter_idx++;
+
+        }
+
+
+
+        sstream.str("");
+        sstream << "obtained minimum: " << current_minimum << std::endl;
+
+
+
+        gsl_vector_free(grad_gsl);
+        gsl_vector_free(solution_guess_tmp);
+        tbb::tick_count adam_end = tbb::tick_count::now();
+        adam_time  = adam_time + (adam_end-adam_start).seconds();
+        sstream << "adam time: " << adam_time << ", pure DFE time:  " << pure_DFE_time << " " << f0 << std::endl;
+        
+        print(sstream, 0); 
+
+}
+
+
+
+/**
+@brief Call to solve layer by layer the optimization problem via BBFG algorithm. (optimal for smaller problems) The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+*/
+void N_Qubit_Decomposition_Base::solve_layer_optimization_problem_BFGS( int num_of_parameters, gsl_vector *solution_guess_gsl) {
+
+
         if (gates.size() == 0 ) {
             return;
         }
@@ -297,6 +520,10 @@ void N_Qubit_Decomposition_Base::solve_layer_optimization_problem( int num_of_pa
                 gsl_multimin_fdfminimizer_free (s);
             }
 
+#ifdef __MPI__        
+            MPI_Bcast( (void*)solution_guess_gsl->data, num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+
 
 
         }
@@ -306,6 +533,192 @@ void N_Qubit_Decomposition_Base::solve_layer_optimization_problem( int num_of_pa
 
 
 
+/**
+@brief Call to solve layer by layer the optimization problem via BFGS algorithm. The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+*/
+void N_Qubit_Decomposition_Base::solve_layer_optimization_problem_BFGS2( int num_of_parameters, gsl_vector *solution_guess_gsl) {
+
+
+        if (gates.size() == 0 ) {
+            return;
+        }
+
+
+        if (solution_guess_gsl == NULL) {
+            solution_guess_gsl = gsl_vector_alloc(num_of_parameters);
+        }
+
+
+        if (optimized_parameters_mtx.size() == 0) {
+            optimized_parameters_mtx = Matrix_real(1, num_of_parameters);
+            memcpy(optimized_parameters_mtx.get_data(), solution_guess_gsl->data, num_of_parameters*sizeof(double) );
+        }
+
+        // maximal number of iteration loops
+        int iteration_loops_max;
+        try {
+            iteration_loops_max = std::max(iteration_loops[qbit_num], 1);
+        }
+        catch (...) {
+            iteration_loops_max = 1;
+        }
+
+
+        int random_shift_count = 0;
+        int sub_iter_idx = 0;
+        double current_minimum_hold = current_minimum;
+
+
+tbb::tick_count bfgs_start = tbb::tick_count::now();
+bfgs_time = 0.0;
+
+
+
+        // do the optimization loops
+        for (int idx=0; idx<iteration_loops_max; idx++) {
+
+            int iter_idx = 0;
+            int status = GSL_CONTINUE;
+
+            const gsl_multimin_fdfminimizer_type *T;
+            gsl_multimin_fdfminimizer *s;
+
+            N_Qubit_Decomposition_Base* par = this;
+
+
+            gsl_multimin_function_fdf my_func;
+
+
+            my_func.n = num_of_parameters;
+            my_func.f = optimization_problem;
+            my_func.df = optimization_problem_grad;
+            my_func.fdf = optimization_problem_combined;
+            my_func.params = par;
+
+
+            T = gsl_multimin_fdfminimizer_vector_bfgs2;
+            s = gsl_multimin_fdfminimizer_alloc (T, num_of_parameters);
+
+            gsl_multimin_fdfminimizer_set(s, &my_func, solution_guess_gsl, 0.01, 0.1);
+
+            do {
+                gsl_set_error_handler_off();
+                
+                if ( sub_iter_idx>2500 || status != GSL_CONTINUE ) {
+
+                    std::stringstream sstream;
+                    sstream << "leaving local minimum " << s->f << std::endl;
+                    print(sstream, 2); 
+                    
+                    sub_iter_idx = 0;
+                    random_shift_count++;
+                    current_minimum_hold = current_minimum;        
+
+                    int factor = rand() % 10 + 1;
+             
+                    for ( int jdx=0; jdx<num_of_parameters; jdx++) {
+                        solution_guess_gsl->data[jdx] = optimized_parameters_mtx[jdx] + (2*double(rand())/double(RAND_MAX)-1)*2*M_PI*std::sqrt(s->f)/factor;
+                    } 
+
+#ifdef __MPI__        
+                    MPI_Bcast( (void*)solution_guess_gsl->data, num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+                    
+                    status = 0;    
+                    
+                    gsl_multimin_fdfminimizer_free (s);                     
+                    s = gsl_multimin_fdfminimizer_alloc (T, num_of_parameters);  
+                    gsl_multimin_fdfminimizer_set(s, &my_func, solution_guess_gsl, 0.01, 0.1);                             
+        
+                }
+                else {
+                    status = gsl_multimin_fdfminimizer_iterate (s);
+                }
+                                
+/*
+                if (status) {
+                  break;
+                }
+*/
+                status = gsl_multimin_test_gradient (s->gradient, gradient_threshold);
+                
+                
+                if (sub_iter_idx == 1 ) {
+                     current_minimum_hold = s->f;    
+                }
+
+
+                if (current_minimum_hold*0.95 > s->f || (current_minimum_hold*0.97 > s->f && s->f < 1e-3) ||  (current_minimum_hold*0.99 > s->f && s->f < 1e-4) ) {
+                     sub_iter_idx = 0;
+                     current_minimum_hold = s->f;        
+                }
+    
+    
+                if (current_minimum > s->f ) {
+                     current_minimum = s->f;
+                     memcpy( optimized_parameters_mtx.get_data(),  solution_guess_gsl->data, num_of_parameters*sizeof(double) );
+                }
+    
+
+                if ( iter_idx % 100000 == 0 ) {
+                     std::stringstream sstream;
+                     sstream << "processed iterations " << (double)iter_idx/iter_max*100 << "\%, current minimum:" << current_minimum << std::endl;
+                     print(sstream, 2);  
+
+                     std::string filename("initial_circuit_iteration.binary");
+                     export_gate_list_to_binary(optimized_parameters_mtx, this, filename);
+                }
+
+
+                if (s->f < optimization_tolerance || random_shift_count > random_shift_count_max ) {
+                    break;
+                }
+
+
+                sub_iter_idx++;
+                iter_idx++;
+
+            } while (iter_idx < iter_max && s->f > optimization_tolerance);
+
+            if (current_minimum > s->f) {
+                current_minimum = s->f;
+                memcpy( optimized_parameters_mtx.get_data(), s->x->data, num_of_parameters*sizeof(double) );                
+
+                for ( int jdx=0; jdx<num_of_parameters; jdx++) {
+                    solution_guess_gsl->data[jdx] = solution_guess_gsl->data[jdx] + (2*double(rand())/double(RAND_MAX)-1)*2*M_PI/100;
+                }
+            }
+            else {
+                for ( int jdx=0; jdx<num_of_parameters; jdx++) {
+                    solution_guess_gsl->data[jdx] = solution_guess_gsl->data[jdx] + (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
+                }
+            }
+
+#ifdef __MPI__        
+            MPI_Bcast( (void*)solution_guess_gsl->data, num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+            
+            gsl_multimin_fdfminimizer_free (s);
+            
+            if (current_minimum < optimization_tolerance ) {
+                break;
+            }
+
+
+
+        }
+
+tbb::tick_count bfgs_end = tbb::tick_count::now();
+bfgs_time  = bfgs_time + (bfgs_end-bfgs_start).seconds();
+std::cout << "bfgs time: " << bfgs_time << " " << current_minimum << std::endl;
+
+
+}
+
+
+
 /**
 // @brief The optimization problem of the final optimization
 // @param parameters An array of the free parameters to be optimized. (The number of teh free paramaters should be equal to the number of parameters in one sub-layer)
@@ -332,14 +745,14 @@ double N_Qubit_Decomposition_Base::optimization_problem( Matrix_real& parameters
     // get the transformed matrix with the gates in the list
     if ( parameters.size() != parameter_num ) {
         std::stringstream sstream;
-	sstream << "Number of free paramaters should be " << parameter_num << ", but got " << parameters.size() << std::endl;
+	sstream << "N_Qubit_Decomposition_Base::optimization_problem: Number of free paramaters should be " << parameter_num << ", but got " << parameters.size() << std::endl;
         print(sstream, 0);	  
         exit(-1);
     }
 
 
     Matrix matrix_new = get_transformed_matrix( parameters, gates.begin(), gates.size(), Umtx );
-
+//matrix_new.print_matrix();
     return get_cost_function(matrix_new);
 
 }
@@ -397,9 +810,70 @@ void N_Qubit_Decomposition_Base::optimization_problem_combined( const gsl_vector
 
     int parameter_num_loc = instance->get_parameter_num();
 
+#ifdef __DFE__
+///////////////////////////////////////
+//std::cout << "number of qubits: " << instance->qbit_num << std::endl;
+//tbb::tick_count t0_DFE = tbb::tick_count::now();/////////////////////////////////    
+if ( instance->qbit_num >= 5 ) {
+    Matrix_real parameters_mtx(parameters->data, 1, parameters->size);
+
+    int gatesNum, redundantGateSets, gateSetNum;
+    DFEgate_kernel_type* DFEgates = instance->convert_to_DFE_gates_with_derivates( parameters_mtx, gatesNum, gateSetNum, redundantGateSets );
+
+    Matrix&& Umtx_loc = instance->get_Umtx();   
+    Matrix_real trace_DFE_mtx(1, gateSetNum);
+
+#ifdef __MPI__
+    // the number of decomposing layers are divided between the MPI processes
+
+    int mpi_gateSetNum = gateSetNum / instance->world_size;
+    int mpi_starting_gateSetIdx = gateSetNum/instance->world_size * instance->current_rank;
+
+    Matrix_real mpi_trace_DFE_mtx(1, mpi_gateSetNum);
+
+//uploadMatrix2DFE( Umtx_loc );
+//tbb::tick_count t0_DFE = tbb::tick_count::now();
+    calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates+mpi_starting_gateSetIdx*gatesNum, gatesNum, mpi_gateSetNum, mpi_trace_DFE_mtx.get_data() );
+
+    int bytes = mpi_trace_DFE_mtx.size()*sizeof(double);
+    MPI_Allgather(mpi_trace_DFE_mtx.get_data(), bytes, MPI_BYTE, trace_DFE_mtx.get_data(), bytes, MPI_BYTE, MPI_COMM_WORLD);
+
+#else
+
+//uploadMatrix2DFE( Umtx_loc );
+//tbb::tick_count t0_DFE = tbb::tick_count::now();
+    calcqgdKernelDFE( Umtx_loc.rows, Umtx_loc.cols, DFEgates, gatesNum, gateSetNum, trace_DFE_mtx.get_data() );
+//tbb::tick_count t1_DFE = tbb::tick_count::now();
+//pure_DFE_time = pure_DFE_time + (t1_DFE-t0_DFE).seconds();
+#endif  
+
+  
+//    double f0_DFE = 1-trace_DFE_mtx[0]/Umtx_loc.cols;
+    *f0 = 1-trace_DFE_mtx[0]/Umtx_loc.cols;
+    Matrix_real grad_components_DFE_mtx(1, parameter_num_loc);
+    for (int idx=0; idx<parameter_num_loc; idx++) {
+//        grad_components_DFE_mtx[idx] = -trace_DFE_mtx[idx+1]/Umtx_loc.cols;
+        gsl_vector_set(grad, idx, -trace_DFE_mtx[idx+1]/Umtx_loc.cols);
+    }
+
+    delete[] DFEgates;
+
+//tbb::tick_count t1_DFE = tbb::tick_count::now();/////////////////////////////////
+//std::cout << "uploaded data to DFE: " << (int)(gatesNum*gateSetNum*sizeof(DFEgate_kernel_type)) << " bytes" << std::endl;
+//std::cout << "time elapsed DFE: " << (t1_DFE-t0_DFE).seconds() << ", expected time: " << (((double)Umtx_loc.rows*(double)Umtx_loc.cols*gatesNum*gateSetNum/get_chained_gates_num()/4 + 4578*3*get_chained_gates_num()))/350000000 + 0.001<< std::endl;
+
+///////////////////////////////////////
+}
+else {
+
+#endif
+
     // vector containing gradients of the transformed matrix
     std::vector<Matrix> Umtx_deriv;
 
+#ifdef __DFE__
+tbb::tick_count t0_CPU = tbb::tick_count::now();/////////////////////////////////
+#endif
     tbb::parallel_invoke(
         [&]{*f0 = instance->optimization_problem(parameters, reinterpret_cast<void*>(instance)); },
         [&]{
@@ -417,9 +891,122 @@ void N_Qubit_Decomposition_Base::optimization_problem_combined( const gsl_vector
             //double grad_comp = (f*f - 1.0)/(*f0)/2;
             double grad_comp = (get_cost_function(Umtx_deriv[idx]) - 1.0);
             gsl_vector_set(grad, idx, grad_comp);
+
+
+
         }
     });
 
+#ifdef __DFE__
+}
+
+/*
+tbb::tick_count t1_CPU = tbb::tick_count::now();/////////////////////////////////
+std::cout << "time elapsed CPU: " << (t1_CPU-t0_CPU).seconds() << " number of parameters: " << parameter_num_loc << std::endl;
+std::cout << "cost function CPU: " << *f0 << " and DFE: " << f0_DFE << std::endl;
+
+for ( int idx=0; idx<parameter_num_loc; idx++ ) {
+
+    double diff = (grad_components_DFE_mtx[idx]-gsl_vector_get(grad, idx))*(grad_components_DFE_mtx[idx]-gsl_vector_get(grad, idx));
+    if ( diff > 1e-5 ) {
+        std::cout << "DFE and CPU cost functions differs at index " << idx << " " <<  grad_components_DFE_mtx[idx] << " and " <<  gsl_vector_get(grad, idx) << std::endl;
+        
+    }   
+
+}
+
+
+
+std::cout << "N_Qubit_Decomposition_Base::optimization_problem_combined" << std::endl;
+std::string error("N_Qubit_Decomposition_Base::optimization_problem_combined");
+        throw error;
+*/
+#endif
+
+
+}
+
+
+
+/**
+@brief ?????????????
+*/
+void N_Qubit_Decomposition_Base::set_iter_max( int iter_max_in  ) {
+
+    iter_max = iter_max_in;
+    
+}
+
+
+
+/**
+@brief ?????????????
+*/
+void N_Qubit_Decomposition_Base::set_random_shift_count_max( int random_shift_count_max_in  ) {
+
+    random_shift_count_max = random_shift_count_max_in;
+
+}
+
+
+/**
+@brief ?????????????
+*/
+void N_Qubit_Decomposition_Base::set_optimizer( optimization_aglorithms alg_in ) {
+
+    alg = alg_in;
+
+    switch ( alg ) {
+        case ADAM:
+            iter_max = 1e5;
+            random_shift_count_max = 100;
+            gradient_threshold = 1e-8;
+            max_iterations = 1;
+            return;
+
+        case BFGS:
+            iter_max = 100;
+            gradient_threshold = 1e-1;
+            random_shift_count_max = 1;   
+            return;
+
+        case BFGS2:
+            iter_max = 1e5;
+            random_shift_count_max = 100;
+            gradient_threshold = 1e-8;
+            max_iterations = 1;
+            return;
+
+        default:
+            std::string error("N_Qubit_Decomposition_Base::solve_layer_optimization_problem: unimplemented optimization algorithm");
+            throw error;
+    }
+
+
+
+}
+
+
+
+/**
+@brief ?????????????
+*/
+void 
+N_Qubit_Decomposition_Base::set_adaptive_eta( bool adaptive_eta_in  ) {
+
+    adaptive_eta = adaptive_eta_in;
+
+}
+
+
+
+/**
+@brief ?????????????
+*/
+void 
+N_Qubit_Decomposition_Base::set_randomized_radius( double radius_in  ) {
+
+    radius = radius_in;
 
 }
 
diff --git a/decomposition/N_Qubit_Decomposition_Cost_Function.cpp b/decomposition/N_Qubit_Decomposition_Cost_Function.cpp
index d1a020a5..ad92e729 100644
--- a/decomposition/N_Qubit_Decomposition_Cost_Function.cpp
+++ b/decomposition/N_Qubit_Decomposition_Cost_Function.cpp
@@ -33,7 +33,7 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 */
 double get_cost_function(Matrix matrix) {
 
-    int matrix_size = matrix.rows;
+    int matrix_size = matrix.cols ;
 /*
     tbb::combinable<double> priv_partial_cost_functions{[](){return 0;}};
     tbb::parallel_for( tbb::blocked_range<int>(0, matrix_size, 1), functor_cost_fnc( matrix, &priv_partial_cost_functions ));
@@ -100,6 +100,7 @@ double get_cost_function(Matrix matrix) {
     for (int idx=0; idx<matrix_size; idx++) {
         
         trace_real += matrix[idx*matrix.stride + idx].real;
+
     }
 
     //double cost_function = std::sqrt(1.0 - trace_real/matrix_size);
diff --git a/decomposition/N_Qubit_Decomposition_adaptive.cpp b/decomposition/N_Qubit_Decomposition_adaptive.cpp
index b839d914..ab3d2c5c 100644
--- a/decomposition/N_Qubit_Decomposition_adaptive.cpp
+++ b/decomposition/N_Qubit_Decomposition_adaptive.cpp
@@ -26,13 +26,24 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "N_Qubit_Decomposition_custom.h"
 #include "N_Qubit_Decomposition_Cost_Function.h"
 #include "Random_Orthogonal.h"
+#include "Random_Unitary.h"
+
+#include "X.h"
 
 #include <time.h>
 #include <stdlib.h>
 
 
+#ifdef __DFE__
+#include "common_DFE.h"
+#endif
+
 
 
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
 Matrix_real create_random_paramaters( Gates_block* gate_structure ) {
 
     int parameter_num = gate_structure->get_parameter_num();
@@ -72,8 +83,22 @@ N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive() : N_Qubit_Decom
     // set the level limit
     level_limit = 0;
 
-    iter_max = 10000;
-    gradient_threshold = 1e-8;
+
+
+    // BFGS is better for smaller problems, while ADAM for larger ones
+    if ( qbit_num <= 5 ) {
+        set_optimizer( BFGS );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 4;
+    }
+    else {
+        set_optimizer( ADAM );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 1;
+    }
+    
 
     srand(time(NULL));   // Initialization, should only be called once.
 }
@@ -96,11 +121,27 @@ N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix Umtx_in,
     level_limit = level_limit_in;
     level_limit_min = level_limit_min_in;
 
-    // Maximal number of iteartions in the optimization process
-    max_iterations = 4;
 
-    iter_max = 10000;
-    gradient_threshold = 1e-8;
+
+
+    // BFGS is better for smaller problems, while ADAM for larger ones
+    if ( qbit_num <= 5 ) {
+        set_optimizer( BFGS );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 4;
+        
+        iter_max = 10000;
+        gradient_threshold = 1e-8;
+
+    }
+    else {
+        set_optimizer( ADAM );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 1;
+
+    }
 
     srand(time(NULL));   // Initialization, should only be called once.
 }
@@ -126,11 +167,30 @@ N_Qubit_Decomposition_adaptive::N_Qubit_Decomposition_adaptive( Matrix Umtx_in,
     level_limit_min = level_limit_min_in;
 
     // Maximal number of iteartions in the optimization process
-    max_iterations = 4;
+    max_iterations = 1;
+
+    gradient_threshold = 1e-8;
 
     // setting the topology
     topology = topology_in;
 
+
+
+
+    // BFGS is better for smaller problems, while ADAM for larger ones
+    if ( qbit_num <= 5 ) {
+        alg = BFGS;
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 4;
+    }
+    else {
+        alg = ADAM;
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 1;
+    }
+
     srand(time(NULL));   // Initialization, should only be called once.
 }
 
@@ -189,64 +249,34 @@ N_Qubit_Decomposition_adaptive::start_decomposition(bool prepare_export) {
         std::stringstream sstream;
 	sstream << "please increase level limit" << std::endl;
         print(sstream, 0);	
-        exit(-1);
+        //exit(-1);
     }
-
 /*
-int qbit_num_loc = 2;
-int target_qbit = 0;
-int control_qbit = 1;
-
-                        RX*   rx_gate_1   = new RX(qbit_num_loc, target_qbit);
-                        Adaptive*   cz_gate     = new Adaptive(qbit_num_loc, target_qbit, control_qbit);
-                        RZ*   rz_gate     = new RZ(qbit_num_loc, control_qbit);
-                        RX*   rx_gate_2   = new RX(qbit_num_loc, target_qbit);
-
-
-                        Gates_block* czr_gate = new Gates_block(qbit_num_loc);
-                        czr_gate->add_gate(rx_gate_1);
-                        czr_gate->add_gate(cz_gate);
-                        czr_gate->add_gate(rz_gate);
-                        czr_gate->add_gate(rx_gate_2);
+            FILE* pFile = fopen("unitary_compression.binary", "rb");
+            if (pFile==NULL) {fputs ("File error to export unitary",stderr); exit (1);}
+int rows, cols;
+fread(&rows, sizeof(int), 1, pFile);
+fread(&cols, sizeof(int), 1, pFile);
+Umtx = Matrix(rows, cols );        
+fread(Umtx.get_data(), sizeof(QGD_Complex16), Umtx.size(), pFile);
+            fclose(pFile);
+*/
 
-Matrix_real params(1,4);
-params[0] = -M_PI/2;
-params[1] = -M_PI/2;
-params[2] = M_PI;
-params[3] = M_PI/2;
 
-Matrix mtx = czr_gate->get_matrix( params );
-mtx.print_matrix();
-exit(-1);
-*/
 /*
-optimized_parameters_mtx.print_matrix();
-std::cout << gate_structure->get_parameter_num() << std::endl;
-Matrix mtx = gate_structure->get_matrix( optimized_parameters_mtx );
-mtx.print_matrix();
-
+apply_imported_gate_structure();
+set_adaptive_gate_structure( "initial_circuit_iteration_2_rd53_135_0.0049.binary" );
+add_layer_to_imported_gate_structure();
 */
+//add_adaptive_gate_structure( "initial_circuit_iteration_2_rd53_135_0.0049.binary" );
 
-//    combine( gate_structure );
-//std::cout << optimization_problem(optimized_parameters_mtx.get_data()) << std::endl;
-
-
-//list_gates( 0);
-
-    // calculating the final error of the decomposition
- //   Matrix matrix_decomposed2 = get_transformed_matrix(optimized_parameters_mtx, gates.begin(), gates.size(), Umtx );
-//matrix_decomposed2.print_matrix();
-
-//exit(-1);
-
-
-//iteration_loops[4] = 2;    
-//Gates_block* gate_structure_loc = gate_structure->clone();
-//insert_random_layers( gate_structure_loc, optimized_parameters_mtx );
+#ifdef __DFE__
+    uploadMatrix2DFE( Umtx );
+#endif
 
 
     double optimization_tolerance_orig = optimization_tolerance;
-    optimization_tolerance = 1e-4;
+
 
     Gates_block* gate_structure_loc = NULL;
     if ( gate_structure != NULL ) {
@@ -263,6 +293,15 @@ mtx.print_matrix();
         gate_structure_loc = determine_initial_gate_structure(optimized_parameters_mtx);
     }
 
+
+
+    std::string filename("circuit_squander.binary");    
+    export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);
+/*
+Matrix_real parameters_imported;
+Gates_block* gate_structure_loc_imported = import_gate_list_from_binary(parameters_imported);
+*/
+//exit(0);///////////////////
     sstream.str("");
     sstream << std::endl;
     sstream << std::endl;
@@ -276,15 +315,9 @@ mtx.print_matrix();
     int uncompressed_iter_num = 0;
     while ( iter<25 || uncompressed_iter_num <= 5 ) {
 
-        if ( current_minimum > 1e-2 ) {
-             for (int idx=0; idx<optimized_parameters_mtx.size(); idx++ ) {
-                 optimized_parameters_mtx[idx] = (2*double(rand())/double(RAND_MAX)-1)*2*M_PI;
-             }
-        }
-
-    sstream.str("");
-    sstream << "iteration " << iter+1 << ": ";
-    print(sstream, 1);	
+        sstream.str("");
+        sstream << "iteration " << iter+1 << ": ";
+        print(sstream, 1);	
 
        
         Gates_block* gate_structure_compressed = compress_gate_structure( gate_structure_loc );
@@ -300,6 +333,12 @@ mtx.print_matrix();
             delete( gate_structure_loc );
             gate_structure_loc = gate_structure_compressed;
             gate_structure_compressed = NULL;
+            
+
+            std::string filename("circuit_compression.binary");
+            export_gate_list_to_binary(optimized_parameters_mtx, gate_structure_loc, filename, verbose);    
+            std::string filename_unitary("unitary_compression_unitary");
+            export_unitary(Umtx, filename_unitary);
         }
 
         iter++;
@@ -342,11 +381,16 @@ mtx.print_matrix();
     	
     // reset the global minimum before final tuning
     current_minimum = DBL_MAX;
+std::cout << "iter_max: " << iter_max << std::endl;
+iter_max = 1e4;
+
+    set_adaptive_eta( true );
 
     // final tuning of the decomposition parameters
     final_optimization();
 
-
+    std::string filename2("circuit_final.binary");
+    export_gate_list_to_binary(optimized_parameters_mtx, this, filename2, verbose);  
 
     // prepare gates to export
     if (prepare_export) {
@@ -386,35 +430,11 @@ mtx.print_matrix();
     	print(sstream, 1);	    	
     	
             
-               
+#ifdef __DFE__
+releive_DFE();
+#endif               
     
 
-/*
-Matrix_real param0 = optimized_parameters_mtx;
-std::cout << "gradient test "<< std::endl;
-std::vector<Matrix>&& Umtx_deriv = apply_derivate_to( param0, Umtx );
-std::cout << "parameter num: " << param0.size() << ", gradient num: " << Umtx_deriv.size() << std::endl;
-
-int deriv_idx_test = 26;
-
-Matrix mtx0 = Umtx.copy();
-Matrix mtx_delta = Umtx.copy();
-
-apply_to(param0, mtx0 );
-
-Matrix_real param_delta =  param0.copy();
-param_delta[deriv_idx_test] += 1e-8;
-apply_to(param_delta, mtx_delta );
-for (int idx=0; idx<Umtx.size(); idx++ ) {
-    mtx_delta[idx].real = (mtx_delta[idx].real-mtx0[idx].real)/1e-8;
-    mtx_delta[idx].imag = (mtx_delta[idx].imag-mtx0[idx].imag)/1e-8;
-}
-
-mtx_delta.print_matrix();
-Umtx_deriv[deriv_idx_test].print_matrix();
-*/
-
-
 
 #if BLAS==0 // undefined BLAS
     omp_set_num_threads(num_threads);
@@ -439,6 +459,9 @@ N_Qubit_Decomposition_adaptive::optimize_imported_gate_structure(Matrix_real& op
     //measure the time for the decompositin
     tbb::tick_count start_time_loc = tbb::tick_count::now();
 
+    std::stringstream sstream;
+    sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
+    print(sstream, 1);	
 
     // solve the optimization problem
     N_Qubit_Decomposition_custom cDecomp_custom;
@@ -451,7 +474,15 @@ N_Qubit_Decomposition_adaptive::optimize_imported_gate_structure(Matrix_real& op
     cDecomp_custom.set_verbose(0);
     cDecomp_custom.set_debugfile("");
     cDecomp_custom.set_iteration_loops( iteration_loops );
-    cDecomp_custom.set_optimization_tolerance( optimization_tolerance );  
+    cDecomp_custom.set_optimization_tolerance( optimization_tolerance ); 
+    if (alg==ADAM || alg==BFGS2) { 
+        int param_num_loc = gate_structure_loc->get_parameter_num();
+        int iter_max_loc = (double)param_num_loc/852 * 1e7;
+        cDecomp_custom.set_iter_max( iter_max_loc );  
+        cDecomp_custom.set_random_shift_count_max( 10000 );   
+        cDecomp_custom.set_adaptive_eta( true );      
+        cDecomp_custom.set_randomized_radius( radius );             
+    }
     cDecomp_custom.start_decomposition(true);
     //cDecomp_custom.list_gates(0);
 
@@ -480,7 +511,7 @@ N_Qubit_Decomposition_adaptive::optimize_imported_gate_structure(Matrix_real& op
         optimization_tolerance = 1.5*current_minimum < 1e-2 ? 1.5*current_minimum : 1e-2;
     }
 
-    std::stringstream sstream;
+    sstream.str("");
     sstream << "Continue with the compression of gate structure consisting of " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
     print(sstream, 1);	
     return gate_structure_loc;
@@ -509,6 +540,7 @@ N_Qubit_Decomposition_adaptive::determine_initial_gate_structure(Matrix_real& op
 
         // create gate structure to be optimized
         Gates_block* gate_structure_loc = new Gates_block(qbit_num);
+
         for (int idx=0; idx<level; idx++) {
 
             // create the new decomposing layer
@@ -525,9 +557,15 @@ N_Qubit_Decomposition_adaptive::determine_initial_gate_structure(Matrix_real& op
 
         N_Qubit_Decomposition_custom cDecomp_custom_random, cDecomp_custom_close_to_zero;
 
+        std::stringstream sstream;
+        sstream << "Starting optimization with " << gate_structure_loc->get_gate_num() << " decomposing layers." << std::endl;
+        print(sstream, 1);	
+
+#ifndef __DFE__
         // try the decomposition withrandom and with close to zero initial values
         tbb::parallel_invoke(
             [&]{            
+#endif
                 // solve the optimization problem in isolated optimization process
                 cDecomp_custom_random = N_Qubit_Decomposition_custom( Umtx.copy(), qbit_num, false, RANDOM);
                 cDecomp_custom_random.set_custom_gate_structure( gate_structure_loc );
@@ -535,8 +573,18 @@ N_Qubit_Decomposition_adaptive::determine_initial_gate_structure(Matrix_real& op
                 cDecomp_custom_random.set_max_iteration( max_iterations );
                 cDecomp_custom_random.set_verbose(0);
                 cDecomp_custom_random.set_debugfile("");
-                cDecomp_custom_random.set_optimization_tolerance( optimization_tolerance );  
+                cDecomp_custom_random.set_optimization_tolerance( optimization_tolerance );
+                if ( alg == ADAM || alg == BFGS2 ) {
+                    int param_num_loc = gate_structure_loc->get_parameter_num();
+                    int iter_max_loc = (double)param_num_loc/852 * 1e7;
+                    cDecomp_custom_random.set_iter_max( iter_max_loc );  
+                    cDecomp_custom_random.set_random_shift_count_max( 10000 );       
+                    cDecomp_custom_random.set_adaptive_eta( true );    
+                    cDecomp_custom_random.set_randomized_radius( radius );          
+                }
                 cDecomp_custom_random.start_decomposition(true);
+
+#ifndef __DFE__
             },
             [&]{
                 // solve the optimization problem in isolated optimization process
@@ -550,9 +598,14 @@ N_Qubit_Decomposition_adaptive::determine_initial_gate_structure(Matrix_real& op
                 cDecomp_custom_close_to_zero.start_decomposition(true);
                }
          );
+#endif
 
          tbb::tick_count end_time_loc = tbb::tick_count::now();
-
+/*
+#ifdef __DFE__
+return NULL;
+#endif
+*/
          double current_minimum_random         = cDecomp_custom_random.get_current_minimum();
          double current_minimum_close_to_zero = cDecomp_custom_close_to_zero.get_current_minimum();
          double current_minimum_loc;
@@ -688,36 +741,34 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
 
     while ( (int)layers_to_remove.size() > layer_num_max ) {
         int remove_idx = rand() % layers_to_remove.size();
+       
         layers_to_remove.erase( layers_to_remove.begin() + remove_idx );
     }
+    
+#ifdef __MPI__        
+    MPI_Bcast( &layers_to_remove[0], layers_to_remove.size(), MPI_INT, 0, MPI_COMM_WORLD);
+#endif    
 
+    // make a copy of the original unitary. (By removing trivial gates global phase might be added to the unitary)
+    Matrix&& Umtx_orig = Umtx.copy();
 
     int panelties_num = layer_num_max < layer_num_orig ? layer_num_max : layer_num_orig;
 
     // preallocate panelties associated with the number of remaining two-qubit controlled gates
-    matrix_base<int> panelties(1,panelties_num);
+    std::vector<unsigned int> panelties(panelties_num, 1<<31);
     std::vector<Gates_block*> gate_structures_vec(panelties_num, NULL);
     std::vector<Matrix_real> optimized_parameters_vec(panelties_num, Matrix_real(0,0));
-    std::vector<double> current_minimum_vec(panelties_num, 0.0);
-
-
+    std::vector<double> current_minimum_vec(panelties_num, DBL_MAX);
+    std::vector<Matrix> Umtx_vec(panelties_num);
 
-    tbb::parallel_for( 0, panelties_num, 1, [&](int idx_to_remove) {
-    //for (int idx_to_remove=0; idx_to_remove<layer_num_orig; idx_to_remove++) {
+    for (int idx=0; idx<panelties_num; idx++) {
 
-        double current_minimum_loc = 0.0;
-        Matrix_real optimized_parameters_loc;
+        Umtx = Umtx_orig.copy();
 
-        if ( current_minimum > 1e-2 ) {
-             optimized_parameters_loc = Matrix_real(0, 0);
-        }
-        else {
-            optimized_parameters_loc = optimized_parameters_mtx.copy();
-        }
-
-   //         optimized_parameters_loc = optimized_parameters_mtx.copy();
+        double current_minimum_loc = DBL_MAX;//current_minimum;
+        Matrix_real optimized_parameters_loc = optimized_parameters_mtx.copy();
 
-        Gates_block* gate_structure_reduced = compress_gate_structure( gate_structure, layers_to_remove[idx_to_remove], optimized_parameters_loc,  current_minimum_loc  );
+        Gates_block* gate_structure_reduced = compress_gate_structure( gate_structure, layers_to_remove[idx], optimized_parameters_loc,  current_minimum_loc  );
         if ( optimized_parameters_loc.size() == 0 ) optimized_parameters_loc = optimized_parameters_mtx.copy();
 
         // remove further adaptive gates if possible
@@ -728,17 +779,22 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
         else {
             gate_structure_tmp = remove_trivial_gates( gate_structure_reduced, optimized_parameters_loc, current_minimum_loc );
         }
-        panelties[idx_to_remove] = get_panelty(gate_structure_tmp, optimized_parameters_loc);
-        gate_structures_vec[idx_to_remove] = gate_structure_tmp;
-        optimized_parameters_vec[idx_to_remove] = optimized_parameters_loc;
-        current_minimum_vec[idx_to_remove] = current_minimum_loc;
+        panelties[idx] = get_panelty(gate_structure_tmp, optimized_parameters_loc);
+        gate_structures_vec[idx] = gate_structure_tmp;
+        optimized_parameters_vec[idx] = optimized_parameters_loc;
+        current_minimum_vec[idx] = current_minimum_loc;
+        Umtx_vec[idx] = Umtx;
         
 
         delete(gate_structure_reduced);
 
+#ifdef __DFE__
+        if ( current_minimum_vec[idx] < optimization_tolerance ) {
+            break;
+        }
+#endif
+    }
 
-    //}
-    });    
 
 //panelties.print_matrix();
 
@@ -754,18 +810,25 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
 
             // randomly choose the solution between identical penalties
             if ( (rand() % 2) == 1 ) {
-                panelty_min = panelties[idx];
                 idx_min = idx;
+
+                panelty_min = panelties[idx];
             }
 
         }
     }
+    
+#ifdef __MPI__        
+    MPI_Bcast( &idx_min, 1, MPI_INT, 0, MPI_COMM_WORLD);
+#endif    
 
     for (int idx=0; idx<panelties.size(); idx++) {
         if (idx==idx_min) continue;
         if ( gate_structures_vec[idx] == gate_structure) continue;
-        delete( gate_structures_vec[idx] );
-        gate_structures_vec[idx] = NULL;
+        if ( gate_structures_vec[idx]  ) {
+            delete( gate_structures_vec[idx] );
+            gate_structures_vec[idx] = NULL;
+        }
     }
 
 
@@ -773,7 +836,8 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
     gate_structure = gate_structures_vec[idx_min];
     optimized_parameters_mtx = optimized_parameters_vec[idx_min];
     current_minimum =  current_minimum_vec[idx_min];
-
+    Umtx = Umtx_vec[idx_min];
+    
     int layer_num = gate_structure->get_gate_num();
 
     if ( layer_num < layer_num_orig+1 ) {
@@ -806,12 +870,12 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
 
     Matrix_real parameters_reduced;
     if ( optimized_parameters.size() > 0 ) {
-        parameters_reduced = create_reduced_parameters( gate_structure_reduced, optimized_parameters, layer_idx );
+        parameters_reduced = create_reduced_parameters( gate_structure, optimized_parameters, layer_idx );
     }
     else {
         parameters_reduced = Matrix_real(0, 0);
     }
-
+    
 
 
     N_Qubit_Decomposition_custom cDecomp_custom;
@@ -826,6 +890,12 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
     cDecomp_custom.set_iteration_loops( iteration_loops );
     cDecomp_custom.set_optimization_blocks( gate_structure_reduced->get_gate_num() ) ;
     cDecomp_custom.set_optimization_tolerance( optimization_tolerance );
+    if ( alg == ADAM || alg==BFGS2) {
+        cDecomp_custom.set_iter_max( 1e5 );  
+        cDecomp_custom.set_random_shift_count_max( 1 );     
+        cDecomp_custom.set_adaptive_eta( false );
+        cDecomp_custom.set_randomized_radius( radius );          
+    }
     cDecomp_custom.start_decomposition(true);
     double current_minimum_tmp = cDecomp_custom.get_current_minimum();
 
@@ -845,7 +915,7 @@ N_Qubit_Decomposition_adaptive::compress_gate_structure( Gates_block* gate_struc
 /**
 @brief ???????????????
 */
-int 
+unsigned int 
 N_Qubit_Decomposition_adaptive::get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters ) {
 
 
@@ -858,17 +928,17 @@ N_Qubit_Decomposition_adaptive::get_panelty( Gates_block* gate_structure, Matrix
         double parameter = optimized_parameters[parameter_idx];
  
         
-        if ( std::abs(std::sin(parameter/2)) < 0.99 && std::abs(std::cos(parameter/2)) < 1e-2 ) {
+        if ( std::abs(std::sin(parameter)) < 0.999 && std::abs(std::cos(parameter)) < 1e-3 ) {
             // Condition of pure CNOT gate
-            panelty += 2;
+            panelty += 1;
         }
-        else if ( std::abs(std::sin(parameter/2)) < 1e-2 && std::abs(1-std::cos(parameter/2)) < 1e-2 ) {
+        else if ( std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3 ) {
             // Condition of pure Identity gate
             panelty++;
         }
         else {
             // Condition of controlled rotation gate
-            panelty += 4;
+            panelty += 1;
         }
 
         Gate* gate = gate_structure->get_gate( idx );
@@ -923,7 +993,7 @@ N_Qubit_Decomposition_adaptive::replace_trivial_CRY_gates( Gates_block* gate_str
 
 //std::cout << param[0] << " " << (gate_tmp->get_type() == ADAPTIVE_OPERATION) << " "  << std::abs(std::sin(param[0])) << " "  << 1+std::cos(param[0]) << std::endl;
 
-                    if ( gate_tmp->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter/2)) > 0.99 && std::abs(std::cos(parameter/2)) < 1e-2 ) {
+                    if ( gate_tmp->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter)) > 0.999 && std::abs(std::cos(parameter)) < 1e-3 ) {
 
                         // convert to CZ gate
                         int target_qbit = gate_tmp->get_target_qbit();
@@ -947,19 +1017,18 @@ N_Qubit_Decomposition_adaptive::replace_trivial_CRY_gates( Gates_block* gate_str
                         memcpy(parameters_new.get_data(), optimized_parameters.get_data(), parameter_idx*sizeof(double));
                         memcpy(parameters_new.get_data()+parameter_idx+3, optimized_parameters.get_data()+parameter_idx+1, (optimized_parameters.size()-parameter_idx-1)*sizeof(double));
                         optimized_parameters = parameters_new;
-                        if ( std::sin(parameter/2) < 0 ) {
+                        if ( std::sin(parameter) < 0 ) {
                             optimized_parameters[parameter_idx] = -M_PI/2; // rz parameter
                         }
                         else{
                             optimized_parameters[parameter_idx] = M_PI/2; // rz parameter
                         }
-                        optimized_parameters[parameter_idx+1] = M_PI/2; // rx_2 parameter
-                        optimized_parameters[parameter_idx+2] = -M_PI/2; // rx_1 parameter
+                        optimized_parameters[parameter_idx+1] = M_PI/4; // rx_2 parameter
+                        optimized_parameters[parameter_idx+2] = -M_PI/4; // rx_1 parameter
                         parameter_idx += 3;
 
-
                     }
-                    else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter/2)) < 1e-2 && std::abs(1-std::cos(parameter/2)) < 1e-2  ) {
+                    else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3  ) {
                         // release trivial gate  
 
                         layer->release_gate( jdx );
@@ -972,7 +1041,7 @@ N_Qubit_Decomposition_adaptive::replace_trivial_CRY_gates( Gates_block* gate_str
 
                     }
                     else if ( gate_tmp->get_type() == ADAPTIVE_OPERATION ) {
-                        // controlled Z rotation decomposed into 2 CNOT gates
+                        // controlled Y rotation decomposed into 2 CNOT gates
 
                         int target_qbit = gate_tmp->get_target_qbit();
                         int control_qbit = gate_tmp->get_control_qbit();
@@ -1037,47 +1106,156 @@ N_Qubit_Decomposition_adaptive::remove_trivial_gates( Gates_block* gate_structur
 
     Gates_block* gate_structure_loc = gate_structure->clone();
 
-    int idx = 0;
-    while (idx<layer_num ) {
+    for (int idx=0; idx<layer_num; idx++ ) {
 
         Gates_block* layer = static_cast<Gates_block*>( gate_structure_loc->get_gate(idx) );
 
-        for ( int jdx=0; jdx<layer->get_gate_num(); jdx++ ) {
+        int param_num = layer->get_parameter_num();
+
+        if ( layer->get_gate_num() != 3 ) {
+            // every adaptive layers contains a single adaptive gate ant two U3 gates
+            parameter_idx += param_num;
+            continue;
+        }
+
+        Gate* gate_adaptive = layer->get_gate(0);
+        double parameter = optimized_parameters_loc[parameter_idx];
+        parameter = activation_function(parameter, 1);//limit_max);
+/*       
+        N_Qubit_Decomposition_custom cDecomp_custom( Umtx.copy(), qbit_num, false, initial_guess);
+        cDecomp_custom.set_custom_gate_structure( gate_structure_loc );
+        cDecomp_custom.add_gate_layers();
+        std::cout << "bbbbbbbbbbbbbbbbbb 2: " << cDecomp_custom.optimization_problem( optimized_parameters_loc ) << std::endl;
+*/
+        if ( gate_adaptive->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter)) < 1e-3 && std::abs(1-std::cos(parameter)) < 1e-3  ) {
 
-            Gate* gate_tmp = layer->get_gate(jdx);
-            int param_num = gate_tmp->get_parameter_num();
 
+            int parameter_idx_to_be_removed = parameter_idx+1;
 
-            double parameter = optimized_parameters[parameter_idx];
-            parameter = activation_function(parameter, 1);//limit_max);
 
+            // find matching U3 gates
+            std::vector<int>&& involved_qbits = layer->get_involved_qubits();
+            for( int rdx=0; rdx<involved_qbits.size(); rdx++ ) {
 
-            if ( gate_tmp->get_type() == ADAPTIVE_OPERATION &&  std::abs(std::sin(parameter/2)) < 1e-2 && std::abs(1-std::cos(parameter/2)) < 1e-2  ) {
+                U3* U_gate_to_be_removed = static_cast<U3*>(layer->get_gate(rdx+1));
+                int qbit_to_be_matched = U_gate_to_be_removed->get_target_qbit();
 
-               
-                // remove gate from the structure
-                Gates_block* gate_structure_tmp = compress_gate_structure( gate_structure_loc, idx, optimized_parameters_loc, current_minimum_loc );
+                int parameter_idx_loc = parameter_idx;
 
-                optimized_parameters = optimized_parameters_loc;
-                delete( gate_structure_loc );
-                gate_structure_loc = gate_structure_tmp;
-                layer_num = gate_structure_loc->get_gate_num();   
-                break;            
+                bool found_match = false;
+                U3* matching_gate = NULL;
 
-          
-            }
+                for ( int kdx=idx-1; kdx>=0; kdx-- ) {
+                    
 
+                    Gates_block* layer_test = static_cast<Gates_block*>( gate_structure_loc->get_gate(kdx) );
 
-            parameter_idx += param_num;
-            
+                    int gate_num = layer_test->get_gate_num();
+                    for ( int hdx=gate_num-1; hdx>=0; hdx-- ) {
+
+                        Gate* gate_test = layer_test->get_gate(hdx);
+
+                        parameter_idx_loc = parameter_idx_loc - gate_test->get_parameter_num();
+
+                        if ( gate_test->get_type() == U3_OPERATION ) {
+                            int target_qbit_loc = gate_test->get_target_qbit();
+
+                            if ( qbit_to_be_matched == target_qbit_loc ) {
+                                found_match = true;
+                                matching_gate = static_cast<U3*>(gate_test);
+                                break;                             
+                            }
+
+
+                        }
+
+                    }
+
+                    if ( found_match ) break;
+        
+
+                }
+
+
+                Matrix_real param1( &optimized_parameters_loc[parameter_idx_to_be_removed], 1, U_gate_to_be_removed->get_parameter_num() );
+                Matrix U3_matrix1 = U_gate_to_be_removed->calc_one_qubit_u3(param1[0], param1[1], param1[2] );
+
+                Matrix_real param2( &optimized_parameters_loc[parameter_idx_loc], 1, matching_gate->get_parameter_num() );
+                Matrix U3_matrix2 = matching_gate->calc_one_qubit_u3(param2[0], param2[1], param2[2] );
+
+                Matrix U3_prod = dot(U3_matrix2, U3_matrix1);
+
+                optimized_parameters_loc[parameter_idx_to_be_removed] = 0.0;
+                optimized_parameters_loc[parameter_idx_to_be_removed+1] = 0.0;
+                optimized_parameters_loc[parameter_idx_to_be_removed+2] = 0.0;
+                parameter_idx_to_be_removed = parameter_idx_to_be_removed + U_gate_to_be_removed->get_parameter_num();
+
+                // calculate the new theta/2, phi, lambda parameters from U3_prod, and replace them in param2
+                //  global phase on Umtx
+                double ctheta3_over2 = std::sqrt(U3_prod[0].real*U3_prod[0].real+U3_prod[0].imag*U3_prod[0].imag); // cos( theta/2 )
+                double stheta3_over2 = std::sqrt(U3_prod[2].real*U3_prod[2].real+U3_prod[2].imag*U3_prod[2].imag); // sin( theta/2 )
+                double theta3_over2 = std::atan2(stheta3_over2,ctheta3_over2); // theta/2
+
+		double alpha = std::atan2(U3_prod[0].imag,U3_prod[0].real); // the global phase
+
+		double lambda3;
+		double phi3;
+
+		if (std::abs(stheta3_over2)<4e-8){
+		    lambda3 = (std::atan2(U3_prod[3].imag,U3_prod[3].real)-alpha)/2;
+		    phi3 = lambda3;
+		}
+		else {
+		    lambda3 = std::atan2(-1*U3_prod[1].imag,-1*U3_prod[1].real)-alpha;
+		    phi3 = std::atan2(U3_prod[2].imag,U3_prod[2].real)-alpha;
+		}
+
+                // the product U3 matrix
+		Matrix U3_new = matching_gate->calc_one_qubit_u3(theta3_over2,phi3,lambda3);
+		QGD_Complex16 global_phase_new;
+		global_phase_new.real = std::cos(alpha);
+		global_phase_new.imag = std::sin(alpha);
+		apply_global_phase_to_u3(global_phase_new, U3_new);
+                // test for the product U3 matrix
+		if (std::sqrt((U3_new[3].real-U3_prod[3].real)*(U3_new[3].real-U3_prod[3].real)) + std::sqrt((U3_new[3].imag-U3_prod[3].imag)*(U3_new[3].imag-U3_prod[3].imag)) < 1e-8 && (stheta3_over2*stheta3_over2+ctheta3_over2*ctheta3_over2) > 0.99) {
+
+            // setting the resulting parameters if test passed
+		    param2[0] = theta3_over2;
+		    param2[1] = phi3;
+		    param2[2] = lambda3;
+    		apply_global_phase_to_u3(global_phase_new, Umtx);
+
+		}
+/*
+	        N_Qubit_Decomposition_custom cDecomp_custom_( Umtx.copy(), qbit_num, false, initial_guess);
+                cDecomp_custom_.set_custom_gate_structure( gate_structure_loc );
+   	        cDecomp_custom_.add_gate_layers();
+	        std::cout << "aaaaaaaaaaaaaaaaaaaaa 2: " << cDecomp_custom_.optimization_problem( optimized_parameters_loc ) << std::endl;
+*/
+            }
 
-        }
 
+            std::stringstream sstream;
+            sstream << "N_Qubit_Decomposition_adaptive::remove_trivial_gates: Removing trivial gateblock" << std::endl;
+            print(sstream, 3);
+               
+            // remove gate from the structure
+            Gates_block* gate_structure_tmp = compress_gate_structure( gate_structure_loc, idx, optimized_parameters_loc, current_minimum_loc );
 
+            optimized_parameters = optimized_parameters_loc;
+            delete( gate_structure_loc );
+            gate_structure_loc = gate_structure_tmp;
+            layer_num = gate_structure_loc->get_gate_num();   
+            break;            
 
-        idx++;
+          
+            
+        }
 
+        parameter_idx += param_num;
+            
 
+        
     }
 //std::cout << "N_Qubit_Decomposition_adaptive::remove_trivial_gates :" << gate_structure->get_gate_num() << " reduced to " << gate_structure_loc->get_gate_num() << std::endl;
     return gate_structure_loc;
@@ -1206,6 +1384,9 @@ N_Qubit_Decomposition_adaptive::construct_gate_layer( const int& _target_qbit, c
 */
     while (layers.size()>0) { 
         int idx = std::rand() % layers.size();
+#ifdef __MPI__        
+        MPI_Bcast( &idx, 1, MPI_INT, 0, MPI_COMM_WORLD);
+#endif
         block->add_gate( layers[idx] );
         layers.erase( layers.begin() + idx );
     }
@@ -1234,15 +1415,38 @@ N_Qubit_Decomposition_adaptive::add_finalyzing_layer( Gates_block* gate_structur
 */
     for (int idx=0; idx<qbit_num; idx++) {
             bool Theta = true;
-            bool Phi = false;
+            bool Phi = true;
             bool Lambda = true;
              block->add_u3(idx, Theta, Phi, Lambda);
 //        block->add_ry(idx);
     }
-
+/*
+    // creating block of gates
+    Gates_block* block1 = new Gates_block( qbit_num );
+
+block1->add_u3(3, true, false, true);
+block1->add_u3(3, true, false, true);
+block1->add_u3(3, true, false, true);
+block1->add_u3(2, true, false, true);
+block1->add_u3(0, true, false, true);
+block1->add_u3(3, true, false, true);
+*/
+/*
+    Gates_block* block2 = new Gates_block( qbit_num );
+
+block2->add_u3(4, true, false, true);
+block2->add_u3(2, true, false, true);
+block2->add_u3(5, true, false, true);
+block2->add_u3(2, true, false, true);
+block2->add_u3(4, true, false, true);
+block2->add_u3(3, true, false, true);
+*/
     // adding the opeartion block to the gates
     gate_structure->add_gate( block );
 
+    //gate_structure->add_gate( block1 );
+    //gate_structure->add_gate( block2 );
+
 }
 
 
@@ -1258,6 +1462,131 @@ N_Qubit_Decomposition_adaptive::set_adaptive_gate_structure( Gates_block* gate_s
 
 }
 
+/**
+@brief Call to set custom layers to the gate structure that are intended to be used in the subdecomposition.
+@param filename
+*/
+void 
+N_Qubit_Decomposition_adaptive::set_adaptive_gate_structure( std::string filename ) {
+
+    if ( gate_structure ) {
+        delete gate_structure;
+        optimized_parameters_mtx = Matrix_real(0,0);
+    }
+
+    gate_structure = import_gate_list_from_binary(optimized_parameters_mtx, filename, verbose);
+
+}
+
+/**
+@brief set unitary matrix from binary file
+@param filename .binary file to import unitary from
+*/
+void 
+N_Qubit_Decomposition_adaptive::set_unitary_from_file( std::string filename ) {
+
+    Umtx = import_unitary_from_binary(filename);
+
+}
+/**
+@brief call to set Unitary from mtx
+@param matrix to set over
+*/
+void 
+N_Qubit_Decomposition_adaptive::set_unitary( Matrix& Umtx_new ) {
+
+    Umtx = Umtx_new;
+
+}
+
+/**
+@brief Call to append custom layers to the gate structure that are intended to be used in the decomposition.
+@param filename
+*/
+void 
+N_Qubit_Decomposition_adaptive::add_adaptive_gate_structure( std::string filename ) {
+
+
+
+    Matrix_real optimized_parameters_mtx_tmp;
+    Gates_block* gate_structure_tmp = import_gate_list_from_binary(optimized_parameters_mtx_tmp, filename, verbose);
+
+    if ( gate_structure ) {
+        gate_structure_tmp->combine( gate_structure );
+        delete gate_structure;
+        gate_structure = gate_structure_tmp;
+
+        Matrix_real optimized_parameters_mtx_tmp2( 1, optimized_parameters_mtx_tmp.size() + optimized_parameters_mtx.size() );
+        memcpy( optimized_parameters_mtx_tmp2.get_data(), optimized_parameters_mtx_tmp.get_data(), optimized_parameters_mtx_tmp.size()*sizeof(double) );
+        memcpy( optimized_parameters_mtx_tmp2.get_data()+optimized_parameters_mtx_tmp.size(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
+        optimized_parameters_mtx = optimized_parameters_mtx_tmp2;
+    }
+    else {
+        gate_structure = gate_structure_tmp;
+        optimized_parameters_mtx = optimized_parameters_mtx_tmp;
+    }
+
+}
+
+
+
+/**
+@brief Call to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gfate structure is released.
+*/
+void 
+N_Qubit_Decomposition_adaptive::apply_imported_gate_structure() {
+
+    if ( gate_structure==NULL ) return;
+
+    std::vector<Gate*> gates_loc = gate_structure->get_gates();
+    Matrix Umtx_new = get_transformed_matrix( optimized_parameters_mtx, gates_loc.begin(), gates_loc.size(), Umtx );
+
+    std::stringstream sstream;
+    sstream << "The cost function after applying the imported gate structure is:" << get_cost_function(Umtx_new) << std::endl;
+    print(sstream, 3);	
+
+    Umtx = Umtx_new;
+    delete gate_structure;
+    gate_structure = NULL;
+    optimized_parameters_mtx = Matrix_real(0,0);
+
+
+}
+
+
+/**
+@brief Call to add an adaptive layer to the gate structure previously imported
+@param filename
+*/
+void 
+N_Qubit_Decomposition_adaptive::add_layer_to_imported_gate_structure() {
+
+
+    std::stringstream sstream;
+    sstream << "Add new layer to the adaptive gate structure." << std::endl;	        
+    print(sstream, 2);
+
+    Gates_block* layer = construct_gate_layer(0,0);
+
+
+    if ( gate_structure == NULL ) {
+        gate_structure = layer;
+        return;
+    }
+
+
+    gate_structure->combine( layer );
+
+    Matrix_real tmp( 1, optimized_parameters_mtx.size() + layer->get_parameter_num() );
+    memset( tmp.get_data(), 0, tmp.size()*sizeof(double) );
+    memcpy( tmp.get_data(), optimized_parameters_mtx.get_data(), optimized_parameters_mtx.size()*sizeof(double) );
+
+    optimized_parameters_mtx = tmp;    
+
+}
+
+
+
 
 
 
diff --git a/decomposition/N_Qubit_Decomposition_adaptive_general.cpp b/decomposition/N_Qubit_Decomposition_adaptive_general.cpp
index 708acc1b..c1c99725 100644
--- a/decomposition/N_Qubit_Decomposition_adaptive_general.cpp
+++ b/decomposition/N_Qubit_Decomposition_adaptive_general.cpp
@@ -408,7 +408,7 @@ bool are_two_qubits_independent( Gates_block* two_qubit_gate, Matrix_real& param
 /**
 @brief ???????????????
 */
-int 
+unsigned int 
 N_Qubit_Decomposition_adaptive_general::get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters ) {
 
     //The stringstream input to store the output messages.
diff --git a/decomposition/N_Qubit_Decomposition_custom.cpp b/decomposition/N_Qubit_Decomposition_custom.cpp
index 7875c85c..5cd556f8 100644
--- a/decomposition/N_Qubit_Decomposition_custom.cpp
+++ b/decomposition/N_Qubit_Decomposition_custom.cpp
@@ -36,8 +36,21 @@ N_Qubit_Decomposition_custom::N_Qubit_Decomposition_custom() : N_Qubit_Decomposi
     // initialize custom gate structure
     gate_structure = NULL;
 
-    iter_max = 10000;
-    gradient_threshold = 1e-8;
+
+    // BFGS is better for smaller problems, while ADAM for larger ones
+    if ( qbit_num <= 5 ) {
+        set_optimizer( BFGS );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 4;
+    }
+    else {
+        set_optimizer( ADAM );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 1;
+    }
+
 }
 
 /**
@@ -53,8 +66,20 @@ N_Qubit_Decomposition_custom::N_Qubit_Decomposition_custom( Matrix Umtx_in, int
     // initialize custom gate structure
     gate_structure = NULL;
 
-    iter_max = 10000;
-    gradient_threshold = 1e-8;
+
+    // BFGS is better for smaller problems, while ADAM for larger ones
+    if ( qbit_num <= 5 ) {
+        set_optimizer( BFGS );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 4;
+    }
+    else {
+        set_optimizer( ADAM );
+
+        // Maximal number of iteartions in the optimization process
+        max_iterations = 1;
+    }
 
 }
 
@@ -106,15 +131,26 @@ N_Qubit_Decomposition_custom::start_decomposition(bool prepare_export) {
 
     //measure the time for the decompositin
     tbb::tick_count start_time = tbb::tick_count::now();
-
     // setting the gate structure for optimization
     add_gate_layers();
-//std::cout << optimization_problem( optimized_parameters_mtx.get_data() ) << std::endl;
+
+#ifdef __DFE__
+    uploadMatrix2DFE( Umtx );
+#endif
+
+/*
+if (optimized_parameters_mtx.size() > 0 ) {
+    std::cout << "cost function of the imported circuit: " << optimization_problem( optimized_parameters_mtx ) << std::endl;
+}   
+std::cout << "ooooooooooooo " <<  optimized_parameters_mtx.size() << std::endl;
+*/
 
     // final tuning of the decomposition parameters
     final_optimization();
 
-
+#ifdef __DFE__
+//return;
+#endif
     // prepare gates to export
     if (prepare_export) {
         prepare_gates_to_export();
@@ -124,7 +160,6 @@ N_Qubit_Decomposition_custom::start_decomposition(bool prepare_export) {
     Matrix matrix_decomposed = get_transformed_matrix(optimized_parameters_mtx, gates.begin(), gates.size(), Umtx );
     calc_decomposition_error( matrix_decomposed );
 
-
     // get the number of gates used in the decomposition
     gates_num gates_num = get_gate_nums();
 
@@ -144,7 +179,7 @@ N_Qubit_Decomposition_custom::start_decomposition(bool prepare_export) {
     if ( gates_num.syc>0 ) sstream << gates_num.syc << " Sycamore opeartions," << std::endl;
     if ( gates_num.un>0 ) sstream << gates_num.un << " UN opeartions," << std::endl;
     if ( gates_num.adap>0 ) sstream << gates_num.adap << " Adaptive opeartions," << std::endl;
-    	
+
     	 
     sstream << std::endl;
     tbb::tick_count current_time = tbb::tick_count::now();
@@ -287,3 +322,6 @@ void N_Qubit_Decomposition_custom::set_custom_gate_structure( Gates_block* gate_
 
 
 
+
+
+
diff --git a/decomposition/Sub_Matrix_Decomposition.cpp b/decomposition/Sub_Matrix_Decomposition.cpp
index bfafb6f5..5c09ecf1 100644
--- a/decomposition/Sub_Matrix_Decomposition.cpp
+++ b/decomposition/Sub_Matrix_Decomposition.cpp
@@ -25,6 +25,9 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "Sub_Matrix_Decomposition_Cost_Function.h"
 
 
+#ifdef __MPI__
+#include <mpi.h>
+#endif // MPI
 
 //tbb::spin_mutex my_mutex;
 
@@ -49,8 +52,6 @@ Sub_Matrix_Decomposition::Sub_Matrix_Decomposition( ) {
     // custom gate structure used in the decomposition
     unit_gate_structure = NULL;
 
-
-
 }
 
 
@@ -90,10 +91,6 @@ Sub_Matrix_Decomposition::Sub_Matrix_Decomposition( Matrix Umtx_in, int qbit_num
     unit_gate_structure = NULL;
 
 
-
-
-
-
 }
 
 
@@ -485,6 +482,10 @@ void Sub_Matrix_Decomposition::solve_layer_optimization_problem( int num_of_para
                 gsl_multimin_fdfminimizer_free (s);
             }
 
+#ifdef __MPI__        
+            MPI_Bcast( (void*)solution_guess_gsl->data, num_of_parameters, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+#endif
+
 
 
         }
diff --git a/decomposition/include/Decomposition_Base.h b/decomposition/include/Decomposition_Base.h
index d3eb5f95..5e757d27 100644
--- a/decomposition/include/Decomposition_Base.h
+++ b/decomposition/include/Decomposition_Base.h
@@ -70,6 +70,9 @@ class Decomposition_Base : public Gates_block {
 
     /// The maximal allowed error of the optimization problem (The error of the decomposition would scale with the square root of this value)
     double optimization_tolerance;
+    
+    ///The global phase
+    QGD_Complex16 global_phase;
 
 protected:
 
@@ -120,6 +123,10 @@ class Decomposition_Base : public Gates_block {
 
     /// The convergence threshold in the optimization process
     double convergence_threshold;
+    
+
+
+
 
 public:
 
@@ -387,8 +394,46 @@ double get_decomposition_error( );
 @return Returns with the minimum of the cost function
 */
 double get_current_minimum( );
+/**
+@brief  Calculate the new global phase of the Unitary matrix after removing a trivial U3 matrix
+@param global_phase_new: global phase calculated from the product of two U3 matrices
+*/
+void calculate_new_global_phase( QGD_Complex16 global_phase_new );
+
+/**
+@brief Get the global phase of the Unitary matrix 
+@return The current global phase
+*/
+QGD_Complex16 get_global_phase( );
 
+/**
+@brief Call to set global phase 
+@param global_phase_new The value of the new phase
+*/
+void Decomposition_Base::set_global_phase(double global_phase_new_angle);
 
-};
 
+/**
+@brief Call to apply the global phase to a matrix
+@return Returns with the minimum of the cost function
+*/
+void apply_global_phase_to_u3(QGD_Complex16 global_phase_new, Matrix& u3_gate);
+
+/**
+@brief Call to apply the current global phase to the unitary matrix
+@param global_phase The value of the phase
+*/
+void Decomposition_Base::apply_global_phase();
+
+/**
+@brief   exports unitary matrix to binary file
+@param  
+*/
+void export_unitary(Matrix& Umtx, std::string& filename);
+/**
+@brief Import a Unitary matrix from a file
+@param filename  .binary file to read
+*/
+Matrix import_unitary_from_binary(std::string& filename);
+};
 #endif //DECOMPOSITION_BASE
diff --git a/decomposition/include/N_Qubit_Decomposition.h b/decomposition/include/N_Qubit_Decomposition.h
index 43676a34..4afc04a1 100644
--- a/decomposition/include/N_Qubit_Decomposition.h
+++ b/decomposition/include/N_Qubit_Decomposition.h
@@ -172,6 +172,7 @@ int set_identical_blocks( std::map<int, int> identical_blocks_in );
 
 
 
+
 };
 
 
diff --git a/decomposition/include/N_Qubit_Decomposition_Base.h b/decomposition/include/N_Qubit_Decomposition_Base.h
index 0fa207e3..fe3f0e35 100644
--- a/decomposition/include/N_Qubit_Decomposition_Base.h
+++ b/decomposition/include/N_Qubit_Decomposition_Base.h
@@ -53,6 +53,10 @@ int LAPACKE_zggev 	( 	int  	matrix_layout,
 #endif
 
 
+/// implemented optimization algorithms
+enum optimization_aglorithms{ ADAM, BFGS, BFGS2 };
+
+
 /**
 @brief A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gates.
 This class contains the non-template implementation of the decomposition class.
@@ -62,9 +66,14 @@ class N_Qubit_Decomposition_Base : public Decomposition_Base {
 
 public:
 
-
+    ///
     int iter_max;
+    ///
     int gradient_threshold;
+    /// 
+    int random_shift_count_max;
+    ///    
+    double eta;
 
 protected:
 
@@ -74,9 +83,12 @@ class N_Qubit_Decomposition_Base : public Decomposition_Base {
 
     /// A map of <int n: int num> indicating that how many identical successive blocks should be used in the disentanglement of the nth qubit from the others
     std::map<int,int> identical_blocks;
-
-
-
+    ///
+    optimization_aglorithms alg;
+    /// logical variable indicating whether adaptive learning reate is used in the ADAM algorithm
+    bool adaptive_eta;
+    /// parameter to contron the radius of parameter randomization around the curren tminimum
+    double radius;
 
 
 
@@ -127,18 +139,39 @@ virtual void add_finalyzing_layer();
 */
 void final_optimization();
 
-
 /**
-@brief Call to solve layer by layer the optimization problem. The optimalized parameters are stored in attribute optimized_parameters.
+@brief Call to solve layer by layer the optimization problem via calling one of the implemented algorithms. The optimalized parameters are stored in attribute optimized_parameters.
 @param num_of_parameters Number of parameters to be optimized
 @param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
 */
 void solve_layer_optimization_problem( int num_of_parameters, gsl_vector *solution_guess_gsl);
 
+
+
+
 /**
-@brief ??????????????????????????????
+@brief Call to solve layer by layer the optimization problem via BBFG algorithm. (optimal for smaller problems) The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
 */
-int gradient_descent_iteration( Matrix_real Optimized_parameter_loc, double& current_minimum_loc);
+void solve_layer_optimization_problem_BFGS( int num_of_parameters, gsl_vector *solution_guess_gsl);
+
+
+
+/**
+@brief Call to solve layer by layer the optimization problem via BBFG algorithm. The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+*/
+void solve_layer_optimization_problem_BFGS2( int num_of_parameters, gsl_vector *solution_guess_gsl);
+
+
+/**
+@brief Call to solve layer by layer the optimization problem via ADAM algorithm. (optimal for larger problems) The optimalized parameters are stored in attribute optimized_parameters.
+@param num_of_parameters Number of parameters to be optimized
+@param solution_guess_gsl A GNU Scientific Library vector containing the solution guess.
+*/
+void solve_layer_optimization_problem_ADAM( int num_of_parameters, gsl_vector *solution_guess_gsl);
 
 
 /**
@@ -195,6 +228,36 @@ static void optimization_problem_combined( const gsl_vector* parameters, void* v
 double optimization_problem_panelty( double* parameters, Gates_block* gates_block );
 
 
+
+/**
+@brief ?????????????
+*/
+void set_iter_max( int iter_max_in  );
+
+
+/**
+@brief ?????????????
+*/
+void set_random_shift_count_max( int random_shift_count_max_in  );
+
+
+/**
+@brief ?????????????
+*/
+void set_optimizer( optimization_aglorithms alg_in );
+
+
+/**
+@brief ?????????????
+*/
+void set_adaptive_eta( bool adaptive_eta_in  );
+
+
+/**
+@brief ?????????????
+*/
+void set_randomized_radius( double radius_in  );
+
 };
 
 
diff --git a/decomposition/include/N_Qubit_Decomposition_adaptive.h b/decomposition/include/N_Qubit_Decomposition_adaptive.h
index 0bc8d4ee..dc683d24 100644
--- a/decomposition/include/N_Qubit_Decomposition_adaptive.h
+++ b/decomposition/include/N_Qubit_Decomposition_adaptive.h
@@ -74,6 +74,7 @@ class N_Qubit_Decomposition_adaptive : public N_Qubit_Decomposition_Base {
     ///
     std::vector<matrix_base<int>> topology;
     
+    
 
 public:
 
@@ -151,7 +152,7 @@ Gates_block* replace_trivial_CRY_gates( Gates_block* gate_structure, Matrix_real
 /**
 @brief ???????????????
 */
-virtual int get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters );
+virtual unsigned int get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters );
 
 
 /**
@@ -177,13 +178,56 @@ void add_finalyzing_layer( Gates_block* gate_structure );
 
 
 /**
-@brief Call to set custom layers to the gate structure that are intended to be used in the subdecomposition.
-@param gate_structure An <int, Gates_block*> map containing the gate structure used in the individual subdecomposition (default is used, if a gate structure for specific subdecomposition is missing).
+@brief Call to set custom layers to the gate structure that are intended to be used in the decomposition.
+@param gate_structure_in
 */
 void set_adaptive_gate_structure( Gates_block* gate_structure_in );
 
+
+/**
+@brief Call to set custom layers to the gate structure that are intended to be used in the decomposition.
+@param filename
+*/
+void set_adaptive_gate_structure( std::string filename );
+ 
+ 
+ /**
+ @brief Set unitary matrix from file
+ @param filename file to read unitary from
+ */
+ void set_unitary_from_file( std::string filename );
+
+
+ /** 
+ @brief Set unitary matrix 
+ @param matrix to set unitary to
+ */
+ void set_unitary( Matrix& Umtx_new ) ;
+
+/**
+@brief Call to append custom layers to the gate structure that are intended to be used in the decomposition.
+@param filename
+*/
+void add_adaptive_gate_structure( std::string filename );
+
+/**
+@brief Call to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gfate structure is released.
+*/
+void apply_imported_gate_structure();
+
+
+/**
+@brief Call to add an adaptive layer to the gate structure previously imported gate structure
+@param filename
+*/
+void add_layer_to_imported_gate_structure();
+
+
 };
 
 
 
+
+
+
 #endif
diff --git a/decomposition/include/N_Qubit_Decomposition_adaptive_general.h b/decomposition/include/N_Qubit_Decomposition_adaptive_general.h
index 152db730..9b43428d 100644
--- a/decomposition/include/N_Qubit_Decomposition_adaptive_general.h
+++ b/decomposition/include/N_Qubit_Decomposition_adaptive_general.h
@@ -87,7 +87,7 @@ virtual void start_decomposition(bool prepare_export=true);
 /**
 @brief ???????????????
 */
-int get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters );
+unsigned int get_panelty( Gates_block* gate_structure, Matrix_real& optimized_parameters );
 
 
 /**
diff --git a/examples2/example.py b/examples2/example.py
deleted file mode 100644
index 986d41d2..00000000
--- a/examples2/example.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Jun 26 14:42:56 2020
-Copyright (C) 2020 Peter Rakyta, Ph.D.
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see http://www.gnu.org/licenses/.
-
-@author: Peter Rakyta, Ph.D.
-"""
-## \file example.py
-## \brief Simple example python code demonstrating the basic usage of the Python interface of the Quantum Gate Decomposer package
-
-## [import]
-from qgd_python.decomposition.qgd_N_Qubit_Decomposition import qgd_N_Qubit_Decomposition 
-## [import]
-
-print('******************** Decomposing general 3-qubit matrix *******************************')
-
-# cerate unitary q-bit matrix
-from scipy.stats import unitary_group
-import numpy as np
-
-    
-# the number of qubits spanning the unitary
-qbit_num = 3
-
-# determine the soze of the unitary to be decomposed
-matrix_size = int(2**qbit_num)
-   
-# creating a random unitary to be decomposed
-Umtx = unitary_group.rvs(matrix_size)
-
-# creating a class to decompose the 
-cDecompose = qgd_N_Qubit_Decomposition( Umtx.conj().T, False, "ZEROS"  )
-
-# setting the tolerance of the optimization process. The final error of the decomposition would scale with the square root of this value.
-cDecompose.set_Optimization_Tolerance( 1e-12 )
-
-# starting the decomposition
-cDecompose.Start_Decomposition(True, True)
-
-# list the decomposing operations
-cDecompose.List_Gates()
-
-print(' ')
-print(' ')
-print(' ')
-print('**********************************************************************************')
-print('**********************************************************************************')
-print('******************** Solving the 4th IBM chellenge *******************************')
-print(' ')
-print(' ')
-print(' ')
-
-
-#******************************
-## [load Umtx]
-from scipy.io import loadmat
-    
-## load the unitary from file
-data = loadmat('Umtx.mat')  
-## The unitary to be decomposed  
-Umtx = data['Umtx']
-## [load Umtx]
-
-## [create decomposition class]
-## creating a class to decompose the unitary
-cDecompose = qgd_N_Qubit_Decomposition( Umtx.conj().T, optimize_layer_num=True, initial_guess="zeros" )
-## [create decomposition class]
-
-## [set parameters]
-# set the number of successive identical blocks in the optimalization of disentanglement of the n-th qubits
-cDecompose.set_Identical_Blocks( {4: 2, 3: 1} )
-
-# set the maximal number of layers in the decomposition
-cDecompose.set_Max_Layer_Num( {4: 9, 3:4})
-
-# set the number of iteration loops in the decomposition
-cDecompose.set_Iteration_Loops({4: 3, 3: 3, 2: 3})
-
-# setting the verbosity of the decomposition
-cDecompose.set_Verbose( True )
-
-# setting the debugfile name
-cDecompose.set_Debugfile( "debug" )
-
-## [set parameters]
-
-## [start decomposition]
-# starting the decomposition
-cDecompose.Start_Decomposition(finalize_decomp=True, prepare_export=True)
-
-# list the decomposing operations
-cDecompose.List_Gates()
-## [start decomposition]
-
-
-## [qiskit]
-print(' ')
-print('Constructing quantum circuit:')
-print(' ')
-## Qiskit quantum circuit
-quantum_circuit = cDecompose.get_Quantum_Circuit()
-
-print(quantum_circuit)
-
-from qiskit import execute
-from qiskit import Aer
-import numpy.linalg as LA
-    
-# test the decomposition of the matrix
-## Qiskit backend for simulator
-backend = Aer.get_backend('unitary_simulator')
-    
-## job execution and getting the result as an object
-job = execute(quantum_circuit, backend)
-## the result of the Qiskit job
-result = job.result()
-    
-## the unitary matrix from the result object
-decomposed_matrix = result.get_unitary(quantum_circuit)
-product_matrix = np.dot(Umtx,decomposed_matrix.conj().T)
-phase = np.angle(product_matrix[0,0])
-product_matrix = product_matrix*np.exp(-1j*phase)
-    
-product_matrix = np.eye(matrix_size)*2 - product_matrix - product_matrix.conj().T
-# the error of the decomposition
-decomposition_error =  np.sqrt(LA.norm(product_matrix, 2))
-       
-print('The error of the decomposition is ' + str(decomposition_error))
-
-## [qiskit]
-
-
-
-
-
diff --git a/gates/CH.cpp b/gates/CH.cpp
index 089da0dc..e835b8b4 100644
--- a/gates/CH.cpp
+++ b/gates/CH.cpp
@@ -121,62 +121,14 @@ CH::get_matrix() {
 void 
 CH::apply_to( Matrix& input ) {
 
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset = current_idx_loc*input.stride;
-            int row_offset_pair = current_idx_pair_loc*input.stride;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-                double sqrt2 = sqrt(2.0);
-
-                for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                    int index      = row_offset+col_idx;
-                    int index_pair = row_offset_pair+col_idx;                
-
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];              
-
-
-                    input[index].real = (element.real + element_pair.real)/sqrt2;
-                    input[index].imag = (element.imag + element_pair.imag)/sqrt2;
-                    input[index_pair].real = (element.real - element_pair.real)/sqrt2;
-                    input[index_pair].imag = (element.imag - element_pair.imag)/sqrt2;
-
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
+    // the Hadamard gate of one qubit
+    Matrix h_1qbit(2,2);
+    h_1qbit[0].real = 1.0/sqrt(2); h_1qbit[0].imag = 0.0; 
+    h_1qbit[1].real = 1.0/sqrt(2); h_1qbit[1].imag = 0.0;
+    h_1qbit[2].real = 1.0/sqrt(2); h_1qbit[2].imag = 0.0;
+    h_1qbit[3].real = -1.0/sqrt(2); h_1qbit[3].imag = 0.0;
 
+    apply_kernel_to(h_1qbit, input);
 
 }
 
@@ -189,70 +141,15 @@ CH::apply_to( Matrix& input ) {
 void 
 CH::apply_from_right( Matrix& input ) {
 
+    // the Hadamard gate of one qubit
+    Matrix h_1qbit(2,2);
+    h_1qbit[0].real = 1.0/sqrt(2); h_1qbit[0].imag = 0.0; 
+    h_1qbit[1].real = 1.0/sqrt(2); h_1qbit[1].imag = 0.0;
+    h_1qbit[2].real = 1.0/sqrt(2); h_1qbit[2].imag = 0.0;
+    h_1qbit[3].real = -1.0/sqrt(2); h_1qbit[3].imag = 0.0;
 
 
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-
-                double sqrt2 = sqrt(2.0);
-
-                for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
-
-                    int row_offset = row_idx*input.stride;
-
-                    int index      = row_offset+current_idx_loc;
-                    int index_pair = row_offset+current_idx_pair_loc;                
-
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];   
-
-                    input[index].real = (element.real + element_pair.real)/sqrt2;
-                    input[index].imag = (element.imag + element_pair.imag)/sqrt2;
-                    input[index_pair].real = (element.real - element_pair.real)/sqrt2;
-                    input[index_pair].imag = (element.imag - element_pair.imag)/sqrt2;
-           
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
-
-
-
-
-
-
-
+    apply_kernel_from_right(h_1qbit, input);
 
 }
 
diff --git a/gates/CNOT.cpp b/gates/CNOT.cpp
index 4ce7dbb0..a6ea91f2 100644
--- a/gates/CNOT.cpp
+++ b/gates/CNOT.cpp
@@ -119,62 +119,22 @@ CNOT::get_matrix() {
 */
 void 
 CNOT::apply_to( Matrix& input ) {
-
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset = current_idx_loc*input.stride;
-            int row_offset_pair = current_idx_pair_loc*input.stride;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-                for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                    int index      = row_offset+col_idx;
-                    int index_pair = row_offset_pair+col_idx;                
-
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];              
-
-                    input[index] = element_pair;
-                    input[index_pair] = element;
-
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
+ 
+    // the not gate of one qubit
+    Matrix not_1qbit(2,2);
+    not_1qbit[0].real = 0.0; not_1qbit[0].imag = 0.0; 
+    not_1qbit[1].real = 1.0; not_1qbit[1].imag = 0.0;
+    not_1qbit[2].real = 1.0; not_1qbit[2].imag = 0.0;
+    not_1qbit[3].real = 0.0; not_1qbit[3].imag = 0.0;
 
 
-    }
+    apply_kernel_to(not_1qbit, input);
 
 
 }
 
 
+
 /**
 @brief Call to apply the gate on the input array/matrix by input*CNOT
 @param input The input array on which the gate is applied
@@ -182,63 +142,15 @@ CNOT::apply_to( Matrix& input ) {
 void 
 CNOT::apply_from_right( Matrix& input ) {
 
+   
+    // the not gate of one qubit
+    Matrix not_1qbit(2,2);
+    not_1qbit[0].real = 0.0; not_1qbit[0].imag = 0.0; 
+    not_1qbit[1].real = 1.0; not_1qbit[1].imag = 0.0;
+    not_1qbit[2].real = 1.0; not_1qbit[2].imag = 0.0;
+    not_1qbit[3].real = 0.0; not_1qbit[3].imag = 0.0;
 
-
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-                for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
-
-                    int row_offset = row_idx*input.stride;
-
-                    int index      = row_offset+current_idx_loc;
-                    int index_pair = row_offset+current_idx_pair_loc;                
-
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];              
-
-                    input[index] = element_pair;
-                    input[index_pair] = element;
-
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
-
-
-
-
-
+    apply_kernel_from_right(not_1qbit, input);
 
 
 
@@ -246,6 +158,7 @@ CNOT::apply_from_right( Matrix& input ) {
 
 
 
+
 /**
 @brief Call to set the number of qubits spanning the matrix of the gate
 @param qbit_num The number of qubits
diff --git a/gates/CRY.cpp b/gates/CRY.cpp
index ceed8a0b..ce0df20e 100644
--- a/gates/CRY.cpp
+++ b/gates/CRY.cpp
@@ -83,7 +83,7 @@ CRY::~CRY() {
 */
 
 void 
-CRY::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
+CRY::apply_to( Matrix_real& parameters, Matrix& input ) {
 
 
     if (input.rows != matrix_size ) {
@@ -94,13 +94,13 @@ CRY::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
     }
 
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
 /*
-    Theta = theta0;
+    ThetaOver2 = theta0;
     Phi = parameters[0];
     Lambda = lambda0;
 */
@@ -112,68 +112,11 @@ Phi = Phi - M_PI;
 //Phi = 0.5*(1.0-std::cos(Phi))*M_PI;
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-
-        //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-        for( int idx=0; idx<index_step_target; idx++ )  {
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset = current_idx_loc*input.stride;
-            int row_offset_pair = current_idx_pair_loc*input.stride;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                continue;
-            }
-            else {          
-
-                for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                    int index      = row_offset+col_idx;
-                    int index_pair = row_offset_pair+col_idx;
-  
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];
-
-                    QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
-                    QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
-                    input[index].real = tmp1.real + tmp2.real;
-                    input[index].imag = tmp1.imag + tmp2.imag;
-
-                    tmp1 = mult(u3_1qbit[2], element);
-                    tmp2 = mult(u3_1qbit[3], element_pair);
-                    input[index_pair].real = tmp1.real + tmp2.real;
-                    input[index_pair].imag = tmp1.imag + tmp2.imag;
-                }
-
-            };         
-
-
-//std::cout << current_idx << " " << current_idx_pair << std::endl;
-        }
-        //});
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
-
+    // apply the computing kernel on the matrix
+    apply_kernel_to(u3_1qbit, input);
 
 }
 
@@ -195,13 +138,13 @@ CRY::apply_from_right( Matrix_real& parameters, Matrix& input ) {
         exit(-1);
     }
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
 /*
-    Theta = theta0;
+    ThetaOver2 = theta0;
     Phi = parameters[0];
     Lambda = lambda0;
 */
@@ -214,73 +157,10 @@ Phi = Phi - M_PI;
 
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
-
-
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-
-    while ( current_idx_pair < matrix_size ) {
-
-        //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-        for( int idx=0; idx<index_step_target; idx++ )  {
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                continue;
-            }
-            else {
-
-                for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
-
-                    int row_offset = row_idx*input.stride;
-
-
-                    int index      = row_offset+current_idx_loc;
-                    int index_pair = row_offset+current_idx_pair_loc;
-
-                    QGD_Complex16 element      = input[index];
-                    QGD_Complex16 element_pair = input[index_pair];
-
-                    QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
-                    QGD_Complex16 tmp2 = mult(u3_1qbit[2], element_pair);
-                    input[index].real = tmp1.real + tmp2.real;
-                    input[index].imag = tmp1.imag + tmp2.imag;
-
-                    tmp1 = mult(u3_1qbit[1], element);
-                    tmp2 = mult(u3_1qbit[3], element_pair);
-                    input[index_pair].real = tmp1.real + tmp2.real;
-                    input[index_pair].imag = tmp1.imag + tmp2.imag;
-
-                }                       
-
-            }
-
-
-
-
-//std::cout << current_idx << " " << current_idx_pair << std::endl;
-        }
-        //});
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
+    // apply the computing kernel on the matrix
+    apply_kernel_from_right(u3_1qbit, input);
 
 }
 
@@ -300,87 +180,25 @@ CRY::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     std::vector<Matrix> ret;
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters_mtx[0]+M_PI;
+    ThetaOver2 = parameters_mtx[0]+M_PI/2;
     Phi = phi0;
     Lambda = lambda0;
 
     // the resulting matrix
-    Matrix res_mtx = input.copy();
-    
+    Matrix res_mtx = input.copy();   
 
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, 0.5 );
-
-
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-
-        //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-        for( int idx=0; idx<index_step_target; idx++ )  {
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset = current_idx_loc*res_mtx.stride;
-            int row_offset_pair = current_idx_pair_loc*res_mtx.stride;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                memset( res_mtx.get_data()+row_offset, 0.0, matrix_size*sizeof(QGD_Complex16));
-                memset( res_mtx.get_data()+row_offset_pair, 0.0, matrix_size*sizeof(QGD_Complex16));
-            }
-            else {          
-
-                for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                    int index      = row_offset+col_idx;
-                    int index_pair = row_offset_pair+col_idx;
-  
-                    QGD_Complex16 element      = res_mtx[index];
-                    QGD_Complex16 element_pair = res_mtx[index_pair];
-
-                    QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
-                    QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
-                    res_mtx[index].real = tmp1.real + tmp2.real;
-                    res_mtx[index].imag = tmp1.imag + tmp2.imag;
-
-                    tmp1 = mult(u3_1qbit[2], element);
-                    tmp2 = mult(u3_1qbit[3], element_pair);
-                    res_mtx[index_pair].real = tmp1.real + tmp2.real;
-                    res_mtx[index_pair].imag = tmp1.imag + tmp2.imag;
-                }
-
-            };         
-
-
-//std::cout << current_idx << " " << current_idx_pair << std::endl;
-        }
-        //});
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
+    // apply the computing kernel on the matrix
+    bool deriv = true;
+    apply_kernel_to(u3_1qbit, res_mtx, deriv);
 
     ret.push_back(res_mtx);
-
-
-
-    
     return ret;
 
 
@@ -390,22 +208,22 @@ CRY::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
 /**
 @brief Call to set the final optimized parameters of the gate.
-@param Theta Real parameter standing for the parameter theta.
+@param ThetaOver2 Real parameter standing for the parameter theta.
 @param Phi Real parameter standing for the parameter phi.
 @param Lambda Real parameter standing for the parameter lambda.
 */
-void CRY::set_optimized_parameters(double Theta ) {
+void CRY::set_optimized_parameters(double ThetaOver2 ) {
 
     parameters = Matrix_real(1, parameter_num);
 
-    parameters[0] = Theta;
+    parameters[0] = ThetaOver2;
 
 }
 
 
 /**
 @brief Call to get the final optimized parameters of the gate.
-@param parameters_in Preallocated pointer to store the parameters Theta, Phi and Lambda of the U3 gate.
+@param parameters_in Preallocated pointer to store the parameters ThetaOver2, Phi and Lambda of the U3 gate.
 */
 Matrix_real CRY::get_optimized_parameters() {
 
diff --git a/gates/CZ.cpp b/gates/CZ.cpp
index be00f56a..5a84edd3 100644
--- a/gates/CZ.cpp
+++ b/gates/CZ.cpp
@@ -122,52 +122,15 @@ CZ::get_matrix() {
 void 
 CZ::apply_to( Matrix& input ) {
 
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
+    // the not gate of one qubit
+    Matrix z_1qbit(2,2);
+    z_1qbit[0].real = 1.0; z_1qbit[0].imag = 0.0; 
+    z_1qbit[1].real = 0.0; z_1qbit[1].imag = 0.0;
+    z_1qbit[2].real = 0.0; z_1qbit[2].imag = 0.0;
+    z_1qbit[3].real = -1.0; z_1qbit[3].imag = 0.0;
 
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset_pair = current_idx_pair_loc*input.stride;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_pair_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-                for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                    int index_pair = row_offset_pair+col_idx;                
-
-                    QGD_Complex16 element_pair = input[index_pair];              
-
-                    input[index_pair].real = -element_pair.real;
-                    input[index_pair].imag = -element_pair.imag;
-
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
 
+    CNOT::apply_kernel_to(z_1qbit, input);
 
 }
 
@@ -180,63 +143,14 @@ CZ::apply_to( Matrix& input ) {
 void 
 CZ::apply_from_right( Matrix& input ) {
 
+    // the not gate of one qubit
+    Matrix z_1qbit(2,2);
+    z_1qbit[0].real = 1.0; z_1qbit[0].imag = 0.0; 
+    z_1qbit[1].real = 0.0; z_1qbit[1].imag = 0.0;
+    z_1qbit[2].real = 0.0; z_1qbit[2].imag = 0.0;
+    z_1qbit[3].real = -1.0; z_1qbit[3].imag = 0.0;
 
-
-    int index_step_target = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step_target;
-
-    int index_step_control = Power_of_2(control_qbit);
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-    while ( current_idx_pair < matrix_size ) {
-
-        tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            // determine the action according to the state of the control qubit
-            if ( (current_idx_loc/index_step_control) % 2 == 0) {
-                // leave the state as it is
-                return;
-            }
-            else {
-                for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
-
-                    int row_offset = row_idx*input.stride;
-
-                    int index_pair = row_offset+current_idx_pair_loc;                
-
-                    QGD_Complex16 element_pair = input[index_pair];              
-
-                    input[index_pair].real = -element_pair.real;
-                    input[index_pair].imag = -element_pair.imag;
-
-                }                     
-
-            }
-
-
-//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
-
-
-        });
-
-
-        current_idx = current_idx + 2*index_step_target;
-        current_idx_pair = current_idx_pair + 2*index_step_target;
-
-
-    }
-
-
-
-
-
-
-
+    apply_kernel_from_right(z_1qbit, input);
 
 }
 
diff --git a/gates/Gate.cpp b/gates/Gate.cpp
index 5aa222c3..a4d08b05 100644
--- a/gates/Gate.cpp
+++ b/gates/Gate.cpp
@@ -245,3 +245,157 @@ Gate* Gate::clone() {
 }
 
 
+
+/**
+@brief ???????????
+*/
+void 
+Gate::apply_kernel_to(Matrix& u3_1qbit, Matrix& input, bool deriv) {
+
+
+    
+    int index_step_target = 1 << target_qbit;
+    int current_idx = 0;
+    int current_idx_pair = current_idx+index_step_target;
+
+//std::cout << "target qbit: " << target_qbit << std::endl;
+
+    while ( current_idx_pair < matrix_size ) {
+
+        for(int idx=0; idx<index_step_target; idx++) {  
+        //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
+
+            int current_idx_loc = current_idx + idx;
+            int current_idx_pair_loc = current_idx_pair + idx;
+
+            int row_offset = current_idx_loc*input.stride;
+            int row_offset_pair = current_idx_pair_loc*input.stride;
+
+           if ( control_qbit<0 || ((current_idx_loc >> control_qbit) & 1) ) {
+
+                for ( int col_idx=0; col_idx<input.cols; col_idx++) {
+   			
+                    int index      = row_offset+col_idx;
+                    int index_pair = row_offset_pair+col_idx;                
+
+                    QGD_Complex16 element      = input[index];
+                    QGD_Complex16 element_pair = input[index_pair];              
+
+                    QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
+                    QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
+ 
+                    input[index].real = tmp1.real + tmp2.real;
+                    input[index].imag = tmp1.imag + tmp2.imag;
+
+                    tmp1 = mult(u3_1qbit[2], element);
+                    tmp2 = mult(u3_1qbit[3], element_pair);
+
+                    input[index_pair].real = tmp1.real + tmp2.real;
+                    input[index_pair].imag = tmp1.imag + tmp2.imag;
+
+                }
+
+            }
+            else if (deriv) {
+                // when calculating derivatives, the constant element should be zeros
+                memset( input.get_data()+row_offset, 0.0, input.cols*sizeof(QGD_Complex16));
+                memset( input.get_data()+row_offset_pair, 0.0, input.cols*sizeof(QGD_Complex16));
+            }
+            else {
+                // leave the state as it is
+                continue; 
+            }
+
+
+//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
+
+        
+        //});
+        }
+
+
+        current_idx = current_idx + (index_step_target << 1);
+        current_idx_pair = current_idx_pair + (index_step_target << 1);
+
+
+    }
+
+
+}
+
+
+
+
+
+/**
+@brief Call to apply the gate on the input array/matrix by input*CNOT
+@param input The input array on which the gate is applied
+*/
+void 
+Gate::apply_kernel_from_right( Matrix& u3_1qbit, Matrix& input ) {
+
+   
+    int index_step_target = 1 << target_qbit;
+    int current_idx = 0;
+    int current_idx_pair = current_idx+index_step_target;
+
+//std::cout << "target qbit: " << target_qbit << std::endl;
+
+    while ( current_idx_pair < input.cols ) {
+
+        for(int idx=0; idx<index_step_target; idx++) { 
+        //tbb::parallel_for(0, index_step_target, 1, [&](int idx) {  
+
+            int current_idx_loc = current_idx + idx;
+            int current_idx_pair_loc = current_idx_pair + idx;
+
+            // determine the action according to the state of the control qubit
+            if ( control_qbit<0 || ((current_idx_loc >> control_qbit) & 1) ) {
+
+                for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
+
+                    int row_offset = row_idx*input.stride;
+
+
+                    int index      = row_offset+current_idx_loc;
+                    int index_pair = row_offset+current_idx_pair_loc;
+
+                    QGD_Complex16 element      = input[index];
+                    QGD_Complex16 element_pair = input[index_pair];
+
+                    QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
+                    QGD_Complex16 tmp2 = mult(u3_1qbit[2], element_pair);
+                    input[index].real = tmp1.real + tmp2.real;
+                    input[index].imag = tmp1.imag + tmp2.imag;
+
+                    tmp1 = mult(u3_1qbit[1], element);
+                    tmp2 = mult(u3_1qbit[3], element_pair);
+                    input[index_pair].real = tmp1.real + tmp2.real;
+                    input[index_pair].imag = tmp1.imag + tmp2.imag;
+
+                }
+
+            }
+            else {
+                // leave the state as it is
+                continue;
+            }        
+
+
+//std::cout << current_idx_target << " " << current_idx_target_pair << std::endl;
+
+
+        //});
+        }
+
+
+        current_idx = current_idx + (index_step_target << 1);
+        current_idx_pair = current_idx_pair + (index_step_target << 1);
+
+
+    }
+
+
+}
+
+
diff --git a/gates/Gates_block.cpp b/gates/Gates_block.cpp
index a1a9fbdb..6592ac4d 100644
--- a/gates/Gates_block.cpp
+++ b/gates/Gates_block.cpp
@@ -40,6 +40,7 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 
 
 
+
 //static tbb::spin_mutex my_mutex;
 /**
 @brief Default constructor of the class.
@@ -497,7 +498,7 @@ Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input )
                         cnot_operation->apply_to( input_loc );    
                     }
                     else {
-                        cnot_operation->apply_to_list( grad_loc );
+                        cnot_operation->Gate::apply_to_list(grad_loc );
                     }
                 }
                 else if (operation->get_type() == CZ_OPERATION) {
@@ -506,7 +507,7 @@ Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input )
                         cz_operation->apply_to( input_loc );    
                     }
                     else {
-                        cz_operation->apply_to_list( grad_loc );
+                        cz_operation->Gate::apply_to_list(grad_loc );
                     }
                 }
                 else if (operation->get_type() == CH_OPERATION) {
@@ -515,7 +516,7 @@ Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input )
                         ch_operation->apply_to( input_loc );    
                     }
                     else {
-                        ch_operation->apply_to_list( grad_loc );
+                        ch_operation->Gate::apply_to_list(grad_loc );
                     }
                 }    
     
@@ -603,7 +604,7 @@ Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input )
                         x_operation->apply_to( input_loc );    
                     }
                     else {
-                        x_operation->apply_to_list( grad_loc );
+                        x_operation->Gate::apply_to_list( grad_loc );
                     }
                 }
                 else if (operation->get_type() == SX_OPERATION) {
@@ -612,7 +613,7 @@ Gates_block::apply_derivate_to( Matrix_real& parameters_mtx_in, Matrix& input )
                         sx_operation->apply_to( input_loc );    
                     }
                     else {
-                        sx_operation->apply_to_list( grad_loc );
+                        sx_operation->Gate::apply_to_list(grad_loc );
                     }
                 }
                 else if (operation->get_type() == GENERAL_OPERATION) {
@@ -1271,6 +1272,7 @@ gates_num Gates_block::get_gate_nums() {
         gate_nums.com     = 0;
         gate_nums.general = 0;
         gate_nums.adap = 0;
+        gate_nums.total = 0;
 
         for(std::vector<Gate*>::iterator it = gates.begin(); it != gates.end(); ++it) {
             // get the specific gate or block of gates
@@ -1295,54 +1297,72 @@ gates_num Gates_block::get_gate_nums() {
                 gate_nums.on   = gate_nums.on + gate_nums_loc.on;
                 gate_nums.com  = gate_nums.com + gate_nums_loc.com;
                 gate_nums.adap = gate_nums.adap + gate_nums_loc.adap;
+                gate_nums.total = gate_nums.total + gate_nums_loc.total;
+
             }
             else if (gate->get_type() == U3_OPERATION) {
                 gate_nums.u3   = gate_nums.u3 + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == RX_OPERATION) {
                 gate_nums.rx   = gate_nums.rx + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == RY_OPERATION) {
                 gate_nums.ry   = gate_nums.ry + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == CRY_OPERATION) {
                 gate_nums.cry   = gate_nums.cry + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == RZ_OPERATION) {
                 gate_nums.rz   = gate_nums.rz + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == CNOT_OPERATION) {
                 gate_nums.cnot   = gate_nums.cnot + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == CZ_OPERATION) {
                 gate_nums.cz   = gate_nums.cz + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == CH_OPERATION) {
                 gate_nums.ch   = gate_nums.ch + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == X_OPERATION) {
                 gate_nums.x   = gate_nums.x + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == SX_OPERATION) {
                 gate_nums.sx   = gate_nums.sx + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == SYC_OPERATION) {
                 gate_nums.syc   = gate_nums.syc + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == GENERAL_OPERATION) {
                 gate_nums.general   = gate_nums.general + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == UN_OPERATION) {
                 gate_nums.un   = gate_nums.un + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == ON_OPERATION) {
                 gate_nums.on   = gate_nums.on + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == COMPOSITE_OPERATION) {
                 gate_nums.com   = gate_nums.com + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
             else if (gate->get_type() == ADAPTIVE_OPERATION) {
                 gate_nums.adap   = gate_nums.adap + 1;
+                gate_nums.total = gate_nums.total + 1;
             }
 
         }
@@ -1454,13 +1474,13 @@ void Gates_block::list_gates( const Matrix_real &parameters, int start_index ) {
                     parameter_idx = parameter_idx - 1;
                 }
                 else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_phi_parameter() ) {
-                    vartheta = std::fmod( parameters_data[parameter_idx-2], 4*M_PI);
+                    vartheta = std::fmod( 2*parameters_data[parameter_idx-2], 4*M_PI);
                     varphi = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
                     varlambda = 0;
                     parameter_idx = parameter_idx - 2;
                 }
                 else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_lambda_parameter() ) {
-                    vartheta = std::fmod( parameters_data[parameter_idx-2], 4*M_PI);
+                    vartheta = std::fmod( 2*parameters_data[parameter_idx-2], 4*M_PI);
                     varphi = 0;
                     varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
                     parameter_idx = parameter_idx - 2;
@@ -1472,7 +1492,7 @@ void Gates_block::list_gates( const Matrix_real &parameters, int start_index ) {
                     parameter_idx = parameter_idx - 2;
                 }
                 else if ((u3_gate->get_parameter_num() == 3)) {
-                    vartheta = std::fmod( parameters_data[parameter_idx-3], 4*M_PI);
+                    vartheta = std::fmod( 2*parameters_data[parameter_idx-3], 4*M_PI);
                     varphi = std::fmod( parameters_data[parameter_idx-2], 2*M_PI);
                     varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
                     parameter_idx = parameter_idx - 3;
@@ -1492,7 +1512,7 @@ void Gates_block::list_gates( const Matrix_real &parameters, int start_index ) {
                 double vartheta;
                 // get the inverse parameters of the U3 rotation
                 RX* rx_gate = static_cast<RX*>(gate);		
-                vartheta = std::fmod( parameters_data[parameter_idx-1], 4*M_PI);
+                vartheta = std::fmod( 2*parameters_data[parameter_idx-1], 4*M_PI);
                 parameter_idx = parameter_idx - 1;
 
 		std::stringstream sstream;
@@ -1505,7 +1525,7 @@ void Gates_block::list_gates( const Matrix_real &parameters, int start_index ) {
                 double vartheta;
                 // get the inverse parameters of the U3 rotation
                 RY* ry_gate = static_cast<RY*>(gate);
-                vartheta = std::fmod( parameters_data[parameter_idx-1], 4*M_PI);
+                vartheta = std::fmod( 2*parameters_data[parameter_idx-1], 4*M_PI);
                 parameter_idx = parameter_idx - 1;
 
 		std::stringstream sstream;
@@ -2136,3 +2156,1095 @@ bool Gates_block::contains_adaptive_gate(int idx) {
 }
 
 
+
+
+#ifdef __DFE__
+
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+DFEgate_kernel_type* Gates_block::convert_to_DFE_gates_with_derivates( Matrix_real& parameters_mtx, int& gatesNum, int& gateSetNum, int& redundantGateSets, bool only_derivates ) {
+
+    int parameter_num = get_parameter_num();
+    if ( parameter_num != parameters_mtx.size() ) {
+        std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: wrong number of parameters");
+        throw error;
+    }
+
+    gates_num gate_nums   = get_gate_nums();
+    int gates_total_num   = gate_nums.total; 
+    int chained_gates_num = get_chained_gates_num();
+    int gate_padding      = gates_total_num % chained_gates_num == 0 ? 0 : chained_gates_num - (gates_total_num % chained_gates_num);
+    gatesNum              = gates_total_num+gate_padding;
+/*
+std::cout << "chained gates num: " << chained_gates_num << std::endl;
+std::cout << "number of gates: " << gatesNum << std::endl;
+*/
+
+
+    gateSetNum = only_derivates ? parameter_num : parameter_num+1;
+
+#ifdef __MPI__
+    int rem = gateSetNum % (4 * world_size );
+    if ( rem == 0 ) {
+        redundantGateSets = 0;
+    }
+    else {
+        redundantGateSets = (4 * world_size ) - (gateSetNum % (4 * world_size ));
+        gateSetNum = gateSetNum + redundantGateSets;
+    }
+#else
+    int rem = gateSetNum % 4;
+    if ( rem == 0 ) {
+        redundantGateSets = 0;
+    }
+    else {
+        redundantGateSets = 4 - (gateSetNum % 4);
+        gateSetNum = gateSetNum + redundantGateSets;
+    }
+#endif
+
+
+    DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gatesNum*gateSetNum];
+    
+
+    int gate_idx = 0;
+    convert_to_DFE_gates( parameters_mtx, DFEgates, gate_idx );
+
+
+    // padding with identity gates
+    for (int idx=gate_idx; idx<gatesNum; idx++ ){
+
+        DFEgate_kernel_type& DFEGate = DFEgates[idx];
+
+        
+        DFEGate.target_qbit = 0;
+        DFEGate.control_qbit = -1;
+        DFEGate.gate_type = U3_OPERATION;
+        DFEGate.ThetaOver2 = (int32_t)(0);
+        DFEGate.Phi = (int32_t)(0);
+        DFEGate.Lambda = (int32_t)(0); 
+        DFEGate.metadata = 0;
+
+    }
+/*
+    for ( int idx=0; idx<gatesNum; idx++ ) {
+
+        std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " gate type: " << (int)DFEgates[idx].gate_type << std::endl; 
+    }
+*/
+
+    // adjust parameters for derivation
+    if (only_derivates ) {
+        for (int idx=1; idx<(gateSetNum-1); idx++) {
+           memcpy(DFEgates+idx*gatesNum, DFEgates, gatesNum*sizeof(DFEgate_kernel_type));
+        }
+    }
+    else {
+        for (int idx=0; idx<(gateSetNum-1); idx++) {
+           memcpy(DFEgates+(idx+1)*gatesNum, DFEgates, gatesNum*sizeof(DFEgate_kernel_type));
+        }
+    }
+
+    gate_idx = 0;
+    int gate_set_index = parameter_num-1;
+    if (only_derivates) {
+        adjust_parameters_for_derivation( DFEgates, gatesNum, gate_idx, gate_set_index );
+    } 
+    else {
+        adjust_parameters_for_derivation( DFEgates+gatesNum, gatesNum, gate_idx, gate_set_index );
+    }
+
+/*
+    for ( int idx=0; idx<gatesNum*(parameter_num+1); idx++ ) {
+
+        std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " theta: " << (int)DFEgates[idx].ThetaOver2 << " lambda: " << (int)DFEgates[idx].Lambda << std::endl; 
+    }
+*/
+    return DFEgates;
+
+}
+
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+void Gates_block::adjust_parameters_for_derivation( DFEgate_kernel_type* DFEgates, const int gatesNum, int& gate_idx, int& gate_set_index) {
+
+        int parameter_idx = parameter_num;
+        //int gate_set_index = parameter_num-1;
+
+        int32_t parameter_shift = (int32_t)(M_PI/2*(1<<25));
+
+        for(int op_idx = gates.size()-1; op_idx>=0; op_idx--) { 
+
+            Gate* gate = gates[op_idx];
+//std::cout <<   gate_idx << " " <<   gate_set_index << " " << gate->get_type() << std::endl;        
+
+            if (gate->get_type() == CNOT_OPERATION) {
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CZ_OPERATION) {            
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CH_OPERATION) {        	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == SYC_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: SYC_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == U3_OPERATION) {
+
+                // definig the U3 parameters
+                double varthetaOver2;
+                double varphi;
+                double varlambda;
+		
+                // get the inverse parameters of the U3 rotation
+
+                U3* u3_gate = static_cast<U3*>(gate);
+
+                if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_theta_parameter()) {		   
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                    DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    parameter_idx                = parameter_idx - 1;
+
+                }
+                else if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_phi_parameter()) { // not checked
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.Phi                  = DFEGate.Phi + parameter_shift;
+                    DFEGate.metadata             = 3 + (1<<7); // The 0th and 1st element in kernel matrix should be zero for derivates and 3 = 0011, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    parameter_idx                = parameter_idx - 1;
+                }
+                else if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_lambda_parameter()) { // not checked
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.Lambda               = DFEGate.Lambda + parameter_shift;
+                    DFEGate.metadata             = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 6 = 0101, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    parameter_idx                = parameter_idx - 1;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_phi_parameter() ) { //not checked
+
+                    DFEgate_kernel_type& DFEGate2= DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate2.Phi                 = DFEGate2.Phi + parameter_shift;
+                    DFEGate2.metadata            = 3 + (1<<7); // The 0th and 1st element in kernel matrix should be zero for derivates and 3 = 0011, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                    DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+
+
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_lambda_parameter() ) {
+//////////////////////////////////////////////////////
+                    DFEgate_kernel_type& DFEGate2= DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate2.Lambda              = DFEGate2.Lambda + parameter_shift;
+                    DFEGate2.metadata            = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 6 = 0101, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                    DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+
+
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_phi_parameter() && u3_gate->is_lambda_parameter() ) { // not checked
+
+                    DFEgate_kernel_type& DFEGate2= DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate2.Lambda              = DFEGate2.Lambda + parameter_shift;
+                    DFEGate2.metadata            = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 6 = 0101, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.Phi                  = DFEGate.Phi + parameter_shift;
+                    DFEGate.metadata             = 3 + (1<<7); // The 0th and 1st element in kernel matrix should be zero for derivates and 3 = 0011, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 3)) {
+
+                    DFEgate_kernel_type& DFEGate3= DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate3.Lambda              = DFEGate3.Lambda + parameter_shift;
+                    DFEGate3.metadata            = 5 + (1<<7); // The 0st and 3nd element in kernel matrix should be zero for derivates and 6 = 0101, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    DFEgate_kernel_type& DFEGate2= DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate2.Phi                 = DFEGate2.Phi + parameter_shift;
+                    DFEGate2.metadata            = 3 + (1<<7); // The 0th and 1st element in kernel matrix should be zero for derivates and 3 = 0011, plus the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+
+                    DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                    DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                    DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                    gate_set_index               = gate_set_index - 1;
+
+                    parameter_idx = parameter_idx - 3;
+                }
+
+                gate_idx = gate_idx + 1;
+
+            }
+            else if (gate->get_type() == RX_OPERATION) { // Did not cehcked
+
+                DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                gate_set_index               = gate_set_index - 1;
+	
+                parameter_idx = parameter_idx - 1;    
+	    	 
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == RY_OPERATION) { // Did not cehcked
+
+                DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                gate_set_index               = gate_set_index - 1;
+
+                parameter_idx = parameter_idx - 1;
+
+    	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CRY_OPERATION) {
+
+                DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                DFEGate.metadata             = (1<<7); // the leading bit indicates that derivate is processed
+                gate_set_index               = gate_set_index - 1;
+
+                parameter_idx = parameter_idx - 1;
+	    		                    
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == RZ_OPERATION) { // Did not cehcked
+
+                DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                DFEGate.Phi                  = DFEGate.Phi + parameter_shift;
+                DFEGate.metadata             = 3 + (1<<7); // The 0th and 1st element in kernel matrix should be zero for derivates and 3 = 0011, plus the leading bit indicates that derivate is processed
+                gate_set_index               = gate_set_index - 1;
+
+                parameter_idx = parameter_idx - 1;
+	    	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == X_OPERATION) {
+
+            }
+            else if (gate->get_type() == SX_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: SX_gate not implemented");
+                throw error;	    	
+            }
+            else if (gate->get_type() == BLOCK_OPERATION) {
+
+                Gates_block* block_gate = static_cast<Gates_block*>(gate);
+                block_gate->adjust_parameters_for_derivation( DFEgates, gatesNum, gate_idx, gate_set_index);         
+                //gate_set_index               = gate_set_index - block_gate->get_parameter_num();
+
+                parameter_idx = parameter_idx - block_gate->get_parameter_num();
+            }
+            else if (gate->get_type() == UN_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: UN_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == ON_OPERATION) {
+
+                // THE LAST GATE IS A GENERAL GATE APPENDED IN THE BLOCK-WISE OPTIMISATION ROUTINE OF DECOMPOSITION_BASE
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: ON_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == COMPOSITE_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: Composite_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == GENERAL_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: general_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == ADAPTIVE_OPERATION) {
+
+                DFEgate_kernel_type& DFEGate = DFEgates[gate_set_index*gatesNum + gate_idx];
+                DFEGate.metadata              = (1<<7); // the leading bit indicates that derivate is processed
+                DFEGate.ThetaOver2           = DFEGate.ThetaOver2 + parameter_shift;
+                gate_set_index               = gate_set_index - 1;
+
+                parameter_idx = parameter_idx - 1;
+	    		                    
+                gate_idx = gate_idx + 1;
+            }
+
+        }
+
+
+}
+
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+DFEgate_kernel_type* Gates_block::convert_to_DFE_gates( Matrix_real& parameters_mtx, int& gatesNum ) {
+
+    int parameter_num = get_parameter_num();
+    if ( parameter_num != parameters_mtx.size() ) {
+        std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: wrong number of parameters");
+        throw error;
+    }
+
+
+    gates_num gate_nums   = get_gate_nums();
+    int gates_total_num   = gate_nums.total; 
+    int chained_gates_num = get_chained_gates_num();
+    int gate_padding      = chained_gates_num - (gates_total_num % chained_gates_num);
+    gatesNum              = gates_total_num+gate_padding;
+
+
+
+    DFEgate_kernel_type* DFEgates = new DFEgate_kernel_type[gates_total_num+gate_padding];
+    
+    int gate_idx = 0;
+    convert_to_DFE_gates( parameters_mtx, DFEgates, gate_idx );
+
+
+    // padding with identity gates
+    for (int idx=gate_idx; idx<gatesNum; idx++ ){
+
+        DFEgate_kernel_type& DFEGate = DFEgates[idx];
+
+        
+        DFEGate.target_qbit = 0;
+        DFEGate.control_qbit = -1;
+        DFEGate.gate_type = U3_OPERATION;
+        DFEGate.ThetaOver2 = (int32_t)(0);
+        DFEGate.Phi = (int32_t)(0);
+        DFEGate.Lambda = (int32_t)(0); 
+        DFEGate.metadata = 0;
+
+    }
+/*
+    for ( int idx=0; idx<gatesNum; idx++ ) {
+
+        std::cout << "target qubit: " << (int)DFEgates[idx].target_qbit << " control qubit: " << (int)DFEgates[idx].control_qbit << " gate type: " << (int)DFEgates[idx].gate_type << std::endl; 
+    }
+*/
+
+
+    return DFEgates;
+
+}
+
+
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+void Gates_block::convert_to_DFE_gates( const Matrix_real& parameters_mtx, DFEgate_kernel_type* DFEgates, int& start_index ) {
+
+   	
+        int& gate_idx = start_index;
+        int parameter_idx = parameter_num;
+	double *parameters_data = parameters_mtx.get_data();
+	//const_cast <Matrix_real&>(parameters);
+
+
+        for(int op_idx = gates.size()-1; op_idx>=0; op_idx--) { 
+
+            Gate* gate = gates[op_idx];
+            DFEgate_kernel_type& DFEGate = DFEgates[gate_idx];
+
+            if (gate->get_type() == CNOT_OPERATION) {
+                CNOT* cnot_gate = static_cast<CNOT*>(gate);
+                DFEGate.target_qbit = cnot_gate->get_target_qbit();
+                DFEGate.control_qbit = cnot_gate->get_control_qbit();
+                DFEGate.gate_type = CNOT_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(M_PI/2*(1<<25));
+                DFEGate.Phi = (int32_t)(0);
+                DFEGate.Lambda = (int32_t)(M_PI*(1<<25)); 
+                DFEGate.metadata = 0;
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CZ_OPERATION) {
+                CZ* cz_gate = static_cast<CZ*>(gate);    
+                DFEGate.target_qbit = cz_gate->get_target_qbit();
+                DFEGate.control_qbit = cz_gate->get_control_qbit();
+                DFEGate.gate_type = CZ_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(M_PI*(1<<25));  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!	
+                DFEGate.metadata = 0;             
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CH_OPERATION) {
+                CH* ch_gate = static_cast<CH*>(gate);   
+                DFEGate.target_qbit = ch_gate->get_target_qbit();
+                DFEGate.control_qbit = ch_gate->get_control_qbit();
+                DFEGate.gate_type = CH_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(M_PI/4*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(M_PI*(1<<25));  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!	 
+                DFEGate.metadata = 0;        	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == SYC_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: SYC_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == U3_OPERATION) {
+
+                // definig the U3 parameters
+                double varthetaOver2;
+                double varphi;
+                double varlambda;
+		
+                // get the inverse parameters of the U3 rotation
+
+                U3* u3_gate = static_cast<U3*>(gate);
+
+                if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_theta_parameter()) {
+		   
+                    varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    varphi = 0;
+                    varlambda =0;
+                    parameter_idx = parameter_idx - 1;
+
+                }
+                else if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_phi_parameter()) {
+                    varthetaOver2 = 0;
+                    varphi = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    varlambda =0;
+                    parameter_idx = parameter_idx - 1;
+                }
+                else if ((u3_gate->get_parameter_num() == 1) && u3_gate->is_lambda_parameter()) {
+                    varthetaOver2 = 0;
+                    varphi =  0;
+                    varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    parameter_idx = parameter_idx - 1;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_phi_parameter() ) {
+                    varthetaOver2 = std::fmod( parameters_data[parameter_idx-2], 2*M_PI);
+                    varphi = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    varlambda = 0;
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_theta_parameter() && u3_gate->is_lambda_parameter() ) {
+                    varthetaOver2 = std::fmod( parameters_data[parameter_idx-2], 2*M_PI);
+                    varphi = 0;
+                    varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 2) && u3_gate->is_phi_parameter() && u3_gate->is_lambda_parameter() ) {
+                    varthetaOver2 = 0;
+                    varphi = std::fmod( parameters_data[parameter_idx-2], 2*M_PI);
+                    varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    parameter_idx = parameter_idx - 2;
+                }
+                else if ((u3_gate->get_parameter_num() == 3)) {
+                    varthetaOver2 = std::fmod( parameters_data[parameter_idx-3], 2*M_PI);
+                    varphi = std::fmod( parameters_data[parameter_idx-2], 2*M_PI);
+                    varlambda = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                    parameter_idx = parameter_idx - 3;
+                }
+
+                DFEGate.target_qbit = u3_gate->get_target_qbit();
+                DFEGate.control_qbit = -1;
+                DFEGate.gate_type = U3_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); 
+                DFEGate.Phi = (int32_t)(varphi*(1<<25)); 
+                DFEGate.Lambda = (int32_t)(varlambda*(1<<25));	
+                DFEGate.metadata = 0;
+                gate_idx = gate_idx + 1;
+
+            }
+            else if (gate->get_type() == RX_OPERATION) {
+	        // definig the rotation parameter
+                double varthetaOver2;
+                // get the inverse parameters of the U3 rotation
+                RX* rx_gate = static_cast<RX*>(gate);		
+                varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                parameter_idx = parameter_idx - 1;
+
+                DFEGate.target_qbit = rx_gate->get_target_qbit();
+                DFEGate.control_qbit = -1;
+                DFEGate.gate_type = RX_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(-M_PI/2*(1<<25));  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(M_PI/2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!	
+                DFEGate.metadata = 0;    
+	    	 
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == RY_OPERATION) {
+                // definig the rotation parameter
+                double varthetaOver2;
+                // get the inverse parameters of the U3 rotation
+                RY* ry_gate = static_cast<RY*>(gate);
+                varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                parameter_idx = parameter_idx - 1;
+
+                DFEGate.target_qbit = ry_gate->get_target_qbit();
+                DFEGate.control_qbit = -1;
+                DFEGate.gate_type = RY_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(0);  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!	
+                DFEGate.metadata = 0;
+    	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == CRY_OPERATION) {
+                // definig the rotation parameter
+                double Phi;
+                // get the inverse parameters of the U3 rotation
+                CRY* cry_gate = static_cast<CRY*>(gate);
+                double varthetaOver2 = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                parameter_idx = parameter_idx - 1;
+                DFEGate.target_qbit = cry_gate->get_target_qbit();
+                DFEGate.control_qbit = cry_gate->get_control_qbit();
+                DFEGate.gate_type = CRY_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(0);  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.metadata = 0;
+	    		                    
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == RZ_OPERATION) {
+                // definig the rotation parameter
+                double varphi;
+                // get the inverse parameters of the U3 rotation
+                RZ* rz_gate = static_cast<RZ*>(gate);
+                varphi = std::fmod( parameters_data[parameter_idx-1], 2*M_PI);
+                parameter_idx = parameter_idx - 1;
+
+                DFEGate.target_qbit = rz_gate->get_target_qbit();
+                DFEGate.control_qbit = -1;
+                DFEGate.gate_type = RZ_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Phi = (int32_t)(varphi*(1<<25));  // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.Lambda = (int32_t)(0); // TODO: check !!!!!!!!!!!!!!!!!!!!!!!!!!!
+                DFEGate.metadata = 0;
+	    	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == X_OPERATION) {
+                // get the inverse parameters of the U3 rotation
+                X* x_gate = static_cast<X*>(gate);
+
+                DFEGate.target_qbit = x_gate->get_target_qbit();
+                DFEGate.control_qbit = -1;
+                DFEGate.gate_type = X_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(M_PI/2*(1<<25));
+                DFEGate.Phi = (int32_t)(0);
+                DFEGate.Lambda = (int32_t)(M_PI*(1<<25)); 
+                DFEGate.metadata = 0;
+	    	
+                gate_idx = gate_idx + 1;
+            }
+            else if (gate->get_type() == SX_OPERATION) {
+                // get the inverse parameters of the U3 rotation
+                SX* sx_gate = static_cast<SX*>(gate);
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: SX_gate not implemented");
+                throw error;	    	
+            }
+            else if (gate->get_type() == BLOCK_OPERATION) {
+                Gates_block* block_gate = static_cast<Gates_block*>(gate);
+                const Matrix_real parameters_layer_mtx(parameters_mtx.get_data() + parameter_idx - gate->get_parameter_num(), 1, gate->get_parameter_num() );
+                block_gate->convert_to_DFE_gates( parameters_layer_mtx, DFEgates, gate_idx );
+                parameter_idx = parameter_idx - block_gate->get_parameter_num();
+            }
+            else if (gate->get_type() == UN_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: UN_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == ON_OPERATION) {
+
+                // THE LAST GATE IS A GENERAL GATE APPENDED IN THE BLOCK-WISE OPTIMISATION ROUTINE OF DECOMPOSITION_BASE
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: ON_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == COMPOSITE_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: Composite_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == GENERAL_OPERATION) {
+                std::string error("N_Qubit_Decomposition_Base::convert_to_DFE_gates: general_gate not implemented");
+                throw error;
+            }
+            else if (gate->get_type() == ADAPTIVE_OPERATION) {
+                // definig the rotation parameter
+                double Phi;
+                // get the inverse parameters of the U3 rotation
+                Adaptive* ad_gate = static_cast<Adaptive*>(gate);
+                double varthetaOver2 = std::fmod( activation_function(parameters_data[parameter_idx-1], ad_gate->get_limit()), 2*M_PI);
+                parameter_idx = parameter_idx - 1;
+                DFEGate.target_qbit = ad_gate->get_target_qbit();
+                DFEGate.control_qbit = ad_gate->get_control_qbit();
+                DFEGate.gate_type = ADAPTIVE_OPERATION;
+                DFEGate.ThetaOver2 = (int32_t)(varthetaOver2*(1<<25)); 
+                DFEGate.Phi = (int32_t)(0);  
+                DFEGate.Lambda = (int32_t)(0); 
+                DFEGate.metadata = 0;
+	    		                    
+                gate_idx = gate_idx + 1;
+            }
+
+        }
+
+
+    return;
+
+}
+#endif
+
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+void 
+export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, const std::string& filename, int verbosity) {
+
+    std::stringstream sstream;
+    sstream << "Exporting circuit into binary format. Filename: " << filename << std::endl;
+    logging log;
+    log.verbose = verbosity;
+    log.print(sstream, 2);	
+
+    FILE* pFile;
+    char* c_filename = filename.c_str();
+    
+    pFile = fopen(c_filename, "wb");
+    if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
+
+    export_gate_list_to_binary( parameters, gates_block, pFile, verbosity );
+
+    fclose(pFile);
+    return;
+
+}
+
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+void 
+export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, FILE* pFile, int verbosity) {
+
+    int qbit_num = gates_block->get_qbit_num();   
+    fwrite(&qbit_num, sizeof(int), 1, pFile);
+
+    int parameter_num = gates_block->get_parameter_num();
+    fwrite(&parameter_num, sizeof(int), 1, pFile);
+
+
+    int gates_num = gates_block->get_gate_num();
+    fwrite(&gates_num, sizeof(int), 1, pFile);
+
+
+
+    std::vector<Gate*> gates = gates_block->get_gates();
+    double* parameters_data = parameters.get_data();
+
+    for ( std::vector<Gate*>::iterator it=gates.begin(); it != gates.end(); ++it ) {
+        Gate* op = *it;
+
+        gate_type gt_type = op->get_type();
+
+        fwrite(&gt_type, sizeof(gate_type), 1, pFile);
+
+        int parameter_num = op->get_parameter_num();
+
+        if (gt_type == CNOT_OPERATION || gt_type == CZ_OPERATION || gt_type == CH_OPERATION || gt_type == SYC_OPERATION) {
+            int target_qbit = op->get_target_qbit();
+            int control_qbit = op->get_control_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+            fwrite(&control_qbit, sizeof(int), 1, pFile);
+        }
+        else if (gt_type == U3_OPERATION) {
+            int target_qbit = op->get_target_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+
+            U3* u3_op = static_cast<U3*>( op );
+
+            int theta_bool  = u3_op->is_theta_parameter();
+            int phi_bool    = u3_op->is_phi_parameter();
+            int lambda_bool = u3_op->is_lambda_parameter();
+
+            fwrite(&theta_bool, sizeof(int), 1, pFile);
+            fwrite(&phi_bool, sizeof(int), 1, pFile);
+            fwrite(&lambda_bool, sizeof(int), 1, pFile);
+
+
+            fwrite(parameters_data, sizeof(double), parameter_num, pFile);
+
+            
+        }
+        else if (gt_type == RX_OPERATION || gt_type == RY_OPERATION || gt_type == RZ_OPERATION ) {
+            int target_qbit = op->get_target_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+
+            fwrite(parameters_data, sizeof(double), parameter_num, pFile);
+        }
+        else if (gt_type == CRY_OPERATION) {
+            int target_qbit = op->get_target_qbit();
+            int control_qbit = op->get_control_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+            fwrite(&control_qbit, sizeof(int), 1, pFile);
+
+            fwrite(parameters_data, sizeof(double), parameter_num, pFile);
+        }
+        
+        else if (gt_type == X_OPERATION || gt_type == SX_OPERATION) {
+            int target_qbit = op->get_target_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+        }
+        else if (gt_type == BLOCK_OPERATION) {
+            Gates_block* block_op = static_cast<Gates_block*>( op );
+            Matrix_real parameters_loc(parameters_data, 1, parameter_num);
+            export_gate_list_to_binary( parameters_loc, block_op, pFile );
+
+        }
+        else if (gt_type == ADAPTIVE_OPERATION) {
+            int target_qbit = op->get_target_qbit();
+            int control_qbit = op->get_control_qbit();
+            fwrite(&target_qbit, sizeof(int), 1, pFile);
+            fwrite(&control_qbit, sizeof(int), 1, pFile);
+
+            fwrite(parameters_data, sizeof(double), parameter_num, pFile);
+        }
+
+
+        parameters_data = parameters_data + parameter_num;
+
+    }
+
+}
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+Gates_block* import_gate_list_from_binary(Matrix_real& parameters, const std::string& filename, int verbosity) {
+
+    std::stringstream sstream;
+    sstream << "Importing quantum circuit from binary file " << filename << std::endl;	
+    logging log;
+    log.verbose = verbosity;
+    log.print(sstream, 2);	
+
+    FILE* pFile;
+    char* c_filename = filename.c_str();
+    
+    pFile = fopen(c_filename, "rb");
+    if (pFile==NULL) {fputs ("File error",stderr); exit (1);}
+
+    Gates_block* ret = import_gate_list_from_binary(parameters, pFile, verbosity);
+
+    fclose(pFile);
+    return ret;
+}
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+Gates_block* import_gate_list_from_binary(Matrix_real& parameters, FILE* pFile, int verbosity) {
+
+    std::stringstream sstream;
+
+    int qbit_num;
+    fread(&qbit_num, sizeof(int), 1, pFile);
+    sstream << "qbit_num: " << qbit_num << std::endl;
+    Gates_block* gate_block = new Gates_block(qbit_num);
+
+    int parameter_num;
+    fread(&parameter_num, sizeof(int), 1, pFile);
+    sstream << "parameter_num: " << parameter_num << std::endl;
+    parameters = Matrix_real(1, parameter_num);
+    double* parameters_data = parameters.get_data();
+
+    int gates_num;
+    fread(&gates_num, sizeof(int), 1, pFile);
+    sstream << "gates_num: " << gates_num << std::endl;
+
+    std::vector<int> gate_block_level_gates_num;
+    std::vector<Gates_block*> gate_block_levels;
+    gate_block_level_gates_num.push_back( gates_num );
+    gate_block_levels.push_back(gate_block);
+    int current_level = 0;
+
+    
+
+    int iter_max = 1e5;
+    int iter = 0;
+    while ( gate_block_level_gates_num[0] > 0 && iter < iter_max) {
+
+        gate_type gt_type;
+        fread(&gt_type, sizeof(gate_type), 1, pFile);
+
+        //std::cout << "gate type: " << gt_type << std::endl;
+
+        if (gt_type == CNOT_OPERATION) {
+            sstream << "importing CNOT gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_cnot_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+        }
+        else if (gt_type == CZ_OPERATION) {
+            sstream << "importing CZ gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_cz_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+        }
+        else if (gt_type == CH_OPERATION) {
+            sstream << "importing CH gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_ch_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+        }
+        else if (gt_type == SYC_OPERATION) {
+            sstream << "importing SYCAMORE gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_syc_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+        }
+        else if (gt_type == U3_OPERATION) {
+            sstream << "importing U3 gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int Theta;
+            int Phi;
+            int Lambda;
+
+            fread(&Theta, sizeof(int), 1, pFile);
+            fread(&Phi, sizeof(int), 1, pFile);
+            fread(&Lambda, sizeof(int), 1, pFile);
+
+            int parameter_num = Theta + Phi + Lambda;
+            fread(parameters_data, sizeof(double), parameter_num, pFile);
+            parameters_data = parameters_data + parameter_num;
+
+            gate_block_levels[current_level]->add_u3_to_end(target_qbit, Theta, Phi, Lambda);
+            gate_block_level_gates_num[current_level]--;
+            
+        }
+        else if (gt_type == RX_OPERATION) {
+
+            sstream << "importing RX gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            fread(parameters_data, sizeof(double), 1, pFile);
+            parameters_data++;
+
+            gate_block_levels[current_level]->add_rx_to_end(target_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == RY_OPERATION) {
+
+            sstream << "importing RY gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            fread(parameters_data, sizeof(double), 1, pFile);
+            parameters_data++;
+
+            gate_block_levels[current_level]->add_ry_to_end(target_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == CRY_OPERATION) {
+
+            sstream << "importing CRY gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            fread(parameters_data, sizeof(double), 1, pFile);
+            parameters_data++;
+
+            gate_block_levels[current_level]->add_cry_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == RZ_OPERATION) {
+
+            sstream << "importing RZ gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            fread(parameters_data, sizeof(double), 1, pFile);
+            parameters_data++;
+
+            gate_block_levels[current_level]->add_rz_to_end(target_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == X_OPERATION) {
+
+            sstream << "importing X gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_x_to_end(target_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == SX_OPERATION) {
+
+            sstream << "importing SX gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            gate_block_levels[current_level]->add_sx_to_end(target_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+        else if (gt_type == BLOCK_OPERATION) {
+
+
+            sstream << "******* importing gates block ********" << std::endl;
+
+            int qbit_num_loc;
+            fread(&qbit_num_loc, sizeof(int), 1, pFile);
+            //std::cout << "qbit_num_loc: " << qbit_num_loc << std::endl;
+            Gates_block* gate_block_inner = new Gates_block(qbit_num_loc);
+
+            int parameter_num_loc;
+            fread(&parameter_num_loc, sizeof(int), 1, pFile);
+            //std::cout << "parameter_num_loc: " << parameter_num_loc << std::endl;
+        
+
+            int gates_num_loc;
+            fread(&gates_num_loc, sizeof(int), 1, pFile);
+            //std::cout << "gates_num_loc: " << gates_num_loc << std::endl;
+            
+            gate_block_levels[ current_level ]->add_gate_to_end( static_cast<Gate*>(gate_block_inner) );
+            gate_block_levels.push_back( gate_block_inner );
+            gate_block_level_gates_num.push_back(gates_num_loc);
+            current_level++;
+        }
+        else if (gt_type == ADAPTIVE_OPERATION) {
+
+            sstream << "importing adaptive gate" << std::endl;
+
+            int target_qbit;
+            fread(&target_qbit, sizeof(int), 1, pFile);
+            sstream << "target_qbit: " << target_qbit << std::endl;
+
+            int control_qbit;
+            fread(&control_qbit, sizeof(int), 1, pFile);
+            sstream << "control_qbit: " << control_qbit << std::endl;
+
+            fread(parameters_data, sizeof(double), 1, pFile);
+            parameters_data++;
+
+            gate_block_levels[current_level]->add_adaptive_to_end(target_qbit, control_qbit);
+            gate_block_level_gates_num[current_level]--;
+
+        }
+
+
+        if ( gate_block_level_gates_num[current_level] == 0 ) {
+            gate_block_levels.pop_back();
+            gate_block_level_gates_num.pop_back();
+            current_level--;
+            gate_block_level_gates_num[current_level]--;
+            sstream << "finishing gates block" << std::endl;
+        }
+
+
+        iter++;
+    }
+
+    logging log;
+    log.verbose = verbosity;
+    log.print(sstream, 4);	
+
+  
+    if ( iter == iter_max ) {
+        std::string error("Corrupted input file, reached end of the file before contructing the whole gate structure");
+        throw error;
+    }
+
+    return gate_block;
+
+}
+
diff --git a/gates/RX.cpp b/gates/RX.cpp
index 0f979edc..d20165bc 100644
--- a/gates/RX.cpp
+++ b/gates/RX.cpp
@@ -127,7 +127,7 @@ RX::~RX() {
 @param input The input array on which the gate is applied
 */
 void 
-RX::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
+RX::apply_to( Matrix_real& parameters, Matrix& input ) {
 
     if (input.rows != matrix_size ) {
         std::stringstream sstream;
@@ -137,15 +137,15 @@ RX::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
     }
 
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, scale );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_to( u3_1qbit, input );
@@ -171,15 +171,15 @@ RX::apply_from_right( Matrix_real& parameters, Matrix& input ) {
     }
 
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_from_right(u3_1qbit, input);
@@ -209,7 +209,7 @@ RX::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     parameters_tmp[0] = parameters_mtx[0] + M_PI;
     Matrix res_mtx = input.copy();
-    apply_to(parameters_tmp, res_mtx, 0.5);
+    apply_to(parameters_tmp, res_mtx );
     ret.push_back(res_mtx);
 
     
@@ -223,23 +223,23 @@ RX::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
 /**
 @brief Call to set the final optimized parameters of the gate.
-@param Theta Real parameter standing for the parameter theta.
+@param ThetaOver2 Real parameter standing for the parameter theta.
 @param Phi Real parameter standing for the parameter phi.
 @param Lambda Real parameter standing for the parameter lambda.
 */
-void RX::set_optimized_parameters(double Theta ) {
+void RX::set_optimized_parameters(double ThetaOver2 ) {
 
     parameters = Matrix_real(1, parameter_num);
 
 
-    parameters[0] = Theta;
+    parameters[0] = ThetaOver2;
 
 }
 
 
 /**
 @brief Call to get the final optimized parameters of the gate.
-@param parameters_in Preallocated pointer to store the parameters Theta, Phi and Lambda of the U3 gate.
+@param parameters_in Preallocated pointer to store the parameters ThetaOver2, Phi and Lambda of the U3 gate.
 */
 Matrix_real RX::get_optimized_parameters() {
 
diff --git a/gates/RY.cpp b/gates/RY.cpp
index f505e9bb..2533565c 100644
--- a/gates/RY.cpp
+++ b/gates/RY.cpp
@@ -140,15 +140,15 @@ RY::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
     }
 
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, scale );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_to( u3_1qbit, input );
@@ -173,15 +173,15 @@ RY::apply_from_right( Matrix_real& parameters, Matrix& input ) {
         exit(-1);
     }
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
-    Theta = parameters[0];
+    ThetaOver2 = parameters[0];
     Phi = phi0;
     Lambda = lambda0;
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_from_right(u3_1qbit, input);
@@ -209,7 +209,7 @@ RY::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     Matrix_real parameters_tmp(1,1);
 
-    parameters_tmp[0] = parameters_mtx[0] + M_PI;
+    parameters_tmp[0] = parameters_mtx[0] + M_PI/2;
     Matrix res_mtx = input.copy();
     apply_to(parameters_tmp, res_mtx, 0.5);
     ret.push_back(res_mtx);
@@ -225,22 +225,22 @@ RY::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
 /**
 @brief Call to set the final optimized parameters of the gate.
-@param Theta Real parameter standing for the parameter theta.
+@param ThetaOver2 Real parameter standing for the parameter theta.
 @param Phi Real parameter standing for the parameter phi.
 @param Lambda Real parameter standing for the parameter lambda.
 */
-void RY::set_optimized_parameters(double Theta ) {
+void RY::set_optimized_parameters(double ThetaOver2 ) {
 
     parameters = Matrix_real(1, parameter_num);
 
-    parameters[0] = Theta;
+    parameters[0] = ThetaOver2;
 
 }
 
 
 /**
 @brief Call to get the final optimized parameters of the gate.
-@param parameters_in Preallocated pointer to store the parameters Theta, Phi and Lambda of the U3 gate.
+@param parameters_in Preallocated pointer to store the parameters ThetaOver2, Phi and Lambda of the U3 gate.
 */
 Matrix_real RY::get_optimized_parameters() {
 
diff --git a/gates/RZ.cpp b/gates/RZ.cpp
index db1f71ed..78670ddd 100644
--- a/gates/RZ.cpp
+++ b/gates/RZ.cpp
@@ -136,7 +136,7 @@ RZ::~RZ() {
 @param input The input array on which the gate is applied
 */
 void 
-RZ::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
+RZ::apply_to( Matrix_real& parameters, Matrix& input ) {
 
 
     if (input.rows != matrix_size ) {
@@ -154,7 +154,7 @@ RZ::apply_to( Matrix_real& parameters, Matrix& input, const double scale ) {
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, scale );
+    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
 
 
     apply_kernel_to( u3_1qbit, input );
@@ -222,7 +222,7 @@ RZ::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
     
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, 1.0 );
+    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
     memset(u3_1qbit.get_data(), 0.0, 2*sizeof(QGD_Complex16));
 
 
diff --git a/gates/SX.cpp b/gates/SX.cpp
index 17330cbc..cb75a9d6 100644
--- a/gates/SX.cpp
+++ b/gates/SX.cpp
@@ -145,7 +145,11 @@ SX::apply_to( Matrix& input ) {
     sx_1qbit[2].real = 0.5; sx_1qbit[2].imag = -0.5;
     sx_1qbit[3].real = 0.5; sx_1qbit[3].imag = 0.5;
 
-    int index_step = Power_of_2(target_qbit);
+   
+    //apply_kernel_to function to SX gate 
+    apply_kernel_to( sx_1qbit, input );
+   
+  /*  int index_step = Power_of_2(target_qbit);
     int current_idx = 0;
     int current_idx_pair = current_idx+index_step;
 
@@ -191,12 +195,13 @@ SX::apply_to( Matrix& input ) {
 
 
     }
-
+*/
 
 }
 
 
 
+
 /**
 @brief Call to apply the gate on the input array/matrix by input*U3
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
@@ -220,8 +225,11 @@ SX::apply_from_right( Matrix& input ) {
     sx_1qbit[2].real = 0.5; sx_1qbit[2].imag = -0.5;
     sx_1qbit[3].real = 0.5; sx_1qbit[3].imag = 0.5;
 
+    
+    //apply_kernel_from_right function to SX gate 
+    apply_kernel_from_right(sx_1qbit, input);
 
-    int index_step = Power_of_2(target_qbit);
+  /*  int index_step = Power_of_2(target_qbit);
     int current_idx = 0;
     int current_idx_pair = current_idx+index_step;
 
@@ -269,8 +277,7 @@ SX::apply_from_right( Matrix& input ) {
 
 
     }
-
-
+*/
 
 }
 
diff --git a/gates/U3.cpp b/gates/U3.cpp
index 41ea0f56..45d90880 100644
--- a/gates/U3.cpp
+++ b/gates/U3.cpp
@@ -23,6 +23,8 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 
 #include "U3.h"
 
+// pi/2
+static double M_PIOver2 = M_PI/2;
 
 
 //static tbb::spin_mutex my_mutex;
@@ -174,11 +176,11 @@ U3::get_matrix( Matrix_real& parameters ) {
 @param input The input array on which the gate is applied
 */
 void 
-U3::apply_to_list( Matrix_real& parameters_mtx, std::vector<Matrix>& input, const double scale ) {
+U3::apply_to_list( Matrix_real& parameters_mtx, std::vector<Matrix>& input ) {
 
 
     for ( std::vector<Matrix>::iterator it=input.begin(); it != input.end(); it++ ) {
-        apply_to( parameters_mtx, *it, scale );
+        apply_to( parameters_mtx, *it );
     }
 
 }
@@ -191,7 +193,7 @@ U3::apply_to_list( Matrix_real& parameters_mtx, std::vector<Matrix>& input, cons
 @param input The input array on which the gate is applied
 */
 void 
-U3::apply_to( Matrix_real& parameters_mtx, Matrix& input, const double scale ) {
+U3::apply_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
 
     if (input.rows != matrix_size ) {
@@ -202,59 +204,59 @@ U3::apply_to( Matrix_real& parameters_mtx, Matrix& input, const double scale ) {
     }
 
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
     if (theta && !phi && lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = parameters_mtx[1];
     }
 
     else if (theta && phi && lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = parameters_mtx[2];
     }
 
     else if (!theta && phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = parameters_mtx[1];
     }
 
     else if (theta && phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = 0.0;
     }
 
     else if (!theta && !phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = parameters_mtx[0];
     }
 
     else if (!theta && phi && !lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = 0.0;
     }
 
     else if (theta && !phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = 0.0;
     }
 
     else {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = 0.0;
     }
 
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda, scale );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_to( u3_1qbit, input );
@@ -264,63 +266,7 @@ U3::apply_to( Matrix_real& parameters_mtx, Matrix& input, const double scale ) {
 
 
 
-/**
-@brief ???????????
-*/
-void 
-U3::apply_kernel_to( Matrix& u3_1qbit, Matrix& input ) {
-
-
-    int index_step = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step;
-
-//std::cout << "target qbit: " << target_qbit << std::endl;
-
-
-    while ( current_idx_pair < matrix_size ) {
-
-
-        //tbb::parallel_for(0, index_step, 1, [&](int idx) {  
-        for( int idx=0; idx<index_step; idx++ )  {
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-            int row_offset = current_idx_loc*input.stride;
-            int row_offset_pair = current_idx_pair_loc*input.stride;
-
-            for ( int col_idx=0; col_idx<matrix_size; col_idx++) {
-                int index      = row_offset+col_idx;
-                int index_pair = row_offset_pair+col_idx;
-
-                QGD_Complex16 element      = input[index];
-                QGD_Complex16 element_pair = input[index_pair];
 
-                QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
-                QGD_Complex16 tmp2 = mult(u3_1qbit[1], element_pair);
-                input[index].real = tmp1.real + tmp2.real;
-                input[index].imag = tmp1.imag + tmp2.imag;
-
-                tmp1 = mult(u3_1qbit[2], element);
-                tmp2 = mult(u3_1qbit[3], element_pair);
-                input[index_pair].real = tmp1.real + tmp2.real;
-                input[index_pair].imag = tmp1.imag + tmp2.imag;
-
-            };         
-
-//std::cout << current_idx << " " << current_idx_pair << std::endl;
-        }
-        //});
-
-
-        current_idx = current_idx + 2*index_step;
-        current_idx_pair = current_idx_pair + 2*index_step;
-
-
-    }
-
-}
 
 
 
@@ -340,58 +286,58 @@ U3::apply_from_right( Matrix_real& parameters_mtx, Matrix& input ) {
         exit(-1);
     }
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
     if (theta && !phi && lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = parameters_mtx[1];
     }
 
     else if (theta && phi && lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = parameters_mtx[2];
     }
 
     else if (!theta && phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = parameters_mtx[1];
     }
 
     else if (theta && phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = 0.0;
     }
 
     else if (!theta && !phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = parameters_mtx[0];
     }
 
     else if (!theta && phi && !lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = 0.0;
     }
 
     else if (theta && !phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = 0.0;
     }
 
     else {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = 0.0;
     }
 
     // get the U3 gate of one qubit
-    Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
+    Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda );
 
 
     apply_kernel_from_right(u3_1qbit, input);
@@ -400,64 +346,6 @@ U3::apply_from_right( Matrix_real& parameters_mtx, Matrix& input ) {
 }
 
 
-/**
-@brief Call to apply the gate on the input array/matrix by input*U3
-@param parameters An array of parameters to calculate the matrix of the U3 gate.
-@param input The input array on which the gate is applied
-*/
-void 
-U3::apply_kernel_from_right( Matrix& u3_1qbit, Matrix& input ) {
-
-    int index_step = Power_of_2(target_qbit);
-    int current_idx = 0;
-    int current_idx_pair = current_idx+index_step;
-
-
-    while ( current_idx_pair < matrix_size ) {
-
-
-        //tbb::parallel_for(0, index_step, 1, [&](int idx) {  
-        for( int idx=0; idx<index_step; idx++ )  {
-
-            int current_idx_loc = current_idx + idx;
-            int current_idx_pair_loc = current_idx_pair + idx;
-
-
-            for ( int row_idx=0; row_idx<matrix_size; row_idx++) {
-
-                int row_offset = row_idx*input.stride;
-
-
-                int index      = row_offset+current_idx_loc;
-                int index_pair = row_offset+current_idx_pair_loc;
-
-                QGD_Complex16 element      = input[index];
-                QGD_Complex16 element_pair = input[index_pair];
-
-                QGD_Complex16 tmp1 = mult(u3_1qbit[0], element);
-                QGD_Complex16 tmp2 = mult(u3_1qbit[2], element_pair);
-                input[index].real = tmp1.real + tmp2.real;
-                input[index].imag = tmp1.imag + tmp2.imag;
-
-                tmp1 = mult(u3_1qbit[1], element);
-                tmp2 = mult(u3_1qbit[3], element_pair);
-                input[index_pair].real = tmp1.real + tmp2.real;
-                input[index_pair].imag = tmp1.imag + tmp2.imag;
-
-            };         
-
-//std::cout << current_idx << " " << current_idx_pair << std::endl;
-        }
-        //});
-
-
-        current_idx = current_idx + 2*index_step;
-        current_idx_pair = current_idx_pair + 2*index_step;
-
-
-    }
-
-}
 
 
 /**
@@ -476,53 +364,53 @@ U3::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     std::vector<Matrix> ret;
 
-    double Theta, Phi, Lambda;
+    double ThetaOver2, Phi, Lambda;
 
     if (theta && !phi && lambda) {
 
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = parameters_mtx[1];        
     }
 
     else if (theta && phi && lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = parameters_mtx[2];
     }
 
     else if (!theta && phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = parameters_mtx[1];
     }
 
     else if (theta && phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = parameters_mtx[1];
         Lambda = 0.0;
     }
 
     else if (!theta && !phi && lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = parameters_mtx[0];
     }
 
     else if (!theta && phi && !lambda) {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = parameters_mtx[0];
         Lambda = 0.0;
     }
 
     else if (theta && !phi && !lambda) {
-        Theta = parameters_mtx[0];
+        ThetaOver2 = parameters_mtx[0];
         Phi = 0.0;
         Lambda = 0.0;
     }
 
     else {
-        Theta = 0.0;
+        ThetaOver2 = 0.0;
         Phi = 0.0;
         Lambda = 0.0;
     }
@@ -531,7 +419,7 @@ U3::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     if (theta) {
 
-        Matrix u3_1qbit = calc_one_qubit_u3(Theta+M_PI, Phi, Lambda, 0.5);
+        Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2+M_PIOver2, Phi, Lambda);
         Matrix res_mtx = input.copy();
         apply_kernel_to( u3_1qbit, res_mtx );
         ret.push_back(res_mtx);
@@ -542,7 +430,7 @@ U3::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     if (phi) {
 
-        Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi+M_PI/2, Lambda, 1.0 );
+        Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi+M_PIOver2, Lambda );
         memset(u3_1qbit.get_data(), 0.0, 2*sizeof(QGD_Complex16) );
 
         Matrix res_mtx = input.copy();
@@ -555,7 +443,7 @@ U3::apply_derivate_to( Matrix_real& parameters_mtx, Matrix& input ) {
 
     if (lambda) {
 
-        Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda+M_PI/2, 1.0 );
+        Matrix u3_1qbit = calc_one_qubit_u3(ThetaOver2, Phi, Lambda+M_PIOver2 );
         memset(u3_1qbit.get_data(), 0.0, sizeof(QGD_Complex16) );
         memset(u3_1qbit.get_data()+2, 0.0, sizeof(QGD_Complex16) );
 
@@ -624,19 +512,19 @@ bool U3::is_lambda_parameter() {
 
 /**
 @brief Calculate the matrix of a U3 gate gate corresponding to the given parameters acting on a single qbit space.
-@param Theta Real parameter standing for the parameter theta.
+@param ThetaOver2 Real parameter standing for the parameter theta.
 @param Phi Real parameter standing for the parameter phi.
 @param Lambda Real parameter standing for the parameter lambda.
 @return Returns with the matrix of the one-qubit matrix.
 */
-Matrix U3::calc_one_qubit_u3(double Theta, double Phi, double Lambda, const double scale ) {
+Matrix U3::calc_one_qubit_u3(double ThetaOver2, double Phi, double Lambda ) {
 
     Matrix u3_1qbit = Matrix(2,2);
 
 #ifdef DEBUG
-    if (isnan(Theta)) {
+    if (isnan(ThetaOver2)) {
         std::stringstream sstream;
-	sstream << "Matrix U3::calc_one_qubit_u3: Theta is NaN." << std::endl;
+	sstream << "Matrix U3::calc_one_qubit_u3: ThetaOver2 is NaN." << std::endl;
         print(sstream, 1);	    
     }
     if (isnan(Phi)) {
@@ -652,8 +540,8 @@ Matrix U3::calc_one_qubit_u3(double Theta, double Phi, double Lambda, const doub
 #endif // DEBUG
 
 
-    double cos_theta = cos(Theta/2)*scale;
-    double sin_theta = sin(Theta/2)*scale;
+    double cos_theta = cos(ThetaOver2);
+    double sin_theta = sin(ThetaOver2);
 
 
     // the 1,1 element
@@ -696,15 +584,15 @@ U3* U3::clone() {
 
 /**
 @brief Call to set the final optimized parameters of the gate.
-@param Theta Real parameter standing for the parameter theta.
+@param ThetaOver2 Real parameter standing for the parameter theta.
 @param Phi Real parameter standing for the parameter phi.
 @param Lambda Real parameter standing for the parameter lambda.
 */
-void U3::set_optimized_parameters(double Theta, double Phi, double Lambda ) {
+void U3::set_optimized_parameters(double ThetaOver2, double Phi, double Lambda ) {
 
     parameters = Matrix_real(1, 3);
 
-    parameters[0] = Theta;
+    parameters[0] = ThetaOver2;
     parameters[1] = Phi;
     parameters[2] = Lambda;
 
@@ -713,7 +601,7 @@ void U3::set_optimized_parameters(double Theta, double Phi, double Lambda ) {
 
 /**
 @brief Call to get the final optimized parameters of the gate.
-@param parameters_in Preallocated pointer to store the parameters Theta, Phi and Lambda of the U3 gate.
+@param parameters_in Preallocated pointer to store the parameters ThetaOver2, Phi and Lambda of the U3 gate.
 */
 Matrix_real U3::get_optimized_parameters() {
 
diff --git a/gates/X.cpp b/gates/X.cpp
index 54dec843..e2d29e7d 100644
--- a/gates/X.cpp
+++ b/gates/X.cpp
@@ -138,7 +138,19 @@ X::apply_to( Matrix& input ) {
     }
 
 
-    int index_step = Power_of_2(target_qbit);
+    // the X gate of one qubit
+    Matrix x_1qbit(2,2);
+    x_1qbit[0].real = 0.0; x_1qbit[0].imag = 0.0; 
+    x_1qbit[1].real = 1.0; x_1qbit[1].imag = 0.0;
+    x_1qbit[2].real = 1.0; x_1qbit[2].imag = 0.0;
+    x_1qbit[3].real = 0.0; x_1qbit[3].imag = 0.0;
+
+    //apply_kernel_to function to X gate 
+    apply_kernel_to( x_1qbit, input );
+   
+
+
+ /*   int index_step = Power_of_2(target_qbit);
     int current_idx = 0;
     int current_idx_pair = current_idx+index_step;
 
@@ -177,7 +189,7 @@ X::apply_to( Matrix& input ) {
 
 
     }
-
+*/
 
 }
 
@@ -201,8 +213,18 @@ X::apply_from_right( Matrix& input ) {
         exit(-1);
     }
 
+    // the X gate of one qubit
+    Matrix x_1qbit(2,2);
+    x_1qbit[0].real = 0.0; x_1qbit[0].imag = 0.0; 
+    x_1qbit[1].real = 1.0; x_1qbit[1].imag = 0.0;
+    x_1qbit[2].real = 1.0; x_1qbit[2].imag = 0.0;
+    x_1qbit[3].real = 0.0; x_1qbit[3].imag = 0.0;
+   
+    //apply_kernel_from_right function to X gate 
+    apply_kernel_from_right(x_1qbit, input);
+
 
-    int index_step = Power_of_2(target_qbit);
+   /* int index_step = Power_of_2(target_qbit);
     int current_idx = 0;
     int current_idx_pair = current_idx+index_step;
 
@@ -244,7 +266,7 @@ X::apply_from_right( Matrix& input ) {
 
     }
 
-
+*/
 
 }
 
diff --git a/gates/include/CH.h b/gates/include/CH.h
index 53af4c8d..65c01351 100644
--- a/gates/include/CH.h
+++ b/gates/include/CH.h
@@ -27,13 +27,13 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "matrix.h"
 #include "Gate.h"
 #include <math.h>
-
+#include "CNOT.h"
 
 
 /**
 @brief A class representing a CH operation.
 */
-class CH: public Gate {
+class CH: public CNOT {
 
 protected:
 
diff --git a/gates/include/CNOT.h b/gates/include/CNOT.h
index 0357878e..b930377b 100644
--- a/gates/include/CNOT.h
+++ b/gates/include/CNOT.h
@@ -27,7 +27,7 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "matrix.h"
 #include "Gate.h"
 #include <math.h>
-
+#include <U3.h>
 
 
 /**
diff --git a/gates/include/CRY.h b/gates/include/CRY.h
index b969fc84..def4d500 100644
--- a/gates/include/CRY.h
+++ b/gates/include/CRY.h
@@ -25,6 +25,7 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #define CRZ_H
 
 #include "RY.h"
+#include "CNOT.h"
 #include "matrix.h"
 #include "matrix_real.h"
 #include <math.h>
@@ -72,7 +73,7 @@ CRY(int qbit_num_in, int target_qbit_in, int control_qbit_in);
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
 @param input The input array on which the gate is applied
 */
-virtual void apply_to( Matrix_real& parameters, Matrix& input, const double scale=1.0 );
+virtual void apply_to( Matrix_real& parameters, Matrix& input );
 
 
 /**
diff --git a/gates/include/CZ.h b/gates/include/CZ.h
index 328dde20..4911f02b 100644
--- a/gates/include/CZ.h
+++ b/gates/include/CZ.h
@@ -27,13 +27,13 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "matrix.h"
 #include "Gate.h"
 #include <math.h>
-
+#include "CNOT.h"
 
 
 /**
 @brief A class representing a CZ operation.
 */
-class CZ: public Gate {
+class CZ: public CNOT {
 
 protected:
 
diff --git a/gates/include/Gate.h b/gates/include/Gate.h
index 03fe8b0e..aaee698d 100644
--- a/gates/include/Gate.h
+++ b/gates/include/Gate.h
@@ -169,6 +169,19 @@ int get_qbit_num();
 */
 Gate* clone();
 
+protected:
+/**
+@brief ???????????
+*/
+void apply_kernel_to( Matrix& u3_1qbit, Matrix& input, bool deriv=false );
+
+/**
+@brief ???????????
+*/
+void apply_kernel_from_right( Matrix& u3_1qbit, Matrix& input );
+
+
+
 };
 
 
diff --git a/gates/include/Gates_block.h b/gates/include/Gates_block.h
index f867ef3c..ea0ba477 100644
--- a/gates/include/Gates_block.h
+++ b/gates/include/Gates_block.h
@@ -29,6 +29,9 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "matrix_real.h"
 #include "Gate.h"
 
+#ifdef __DFE__
+#include "common_DFE.h"
+#endif
 
 
 /**
@@ -458,7 +461,69 @@ bool contains_adaptive_gate();
 */
 bool contains_adaptive_gate(int idx);
 
+
+
+
+
+
+
+
+#ifdef __DFE__
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+DFEgate_kernel_type* convert_to_DFE_gates_with_derivates( Matrix_real& parameters_mtx, int& gatesNum, int& gateSetNum, int& redundantGateSets, bool only_derivates=false );
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+void adjust_parameters_for_derivation( DFEgate_kernel_type* DFEgates, const int  gatesNum, int& gate_idx, int& gate_set_index );
+
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+DFEgate_kernel_type* convert_to_DFE_gates( Matrix_real& parameters_mtx, int& gatesNum );
+
+/**
+@brief Method to create random initial parameters for the optimization
+@return 
+*/
+void convert_to_DFE_gates( const Matrix_real& parameters_mtx, DFEgate_kernel_type* DFEgates, int& start_index );
+#endif
 };
 
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+void export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, const std::string& filename, int verbosity=3);
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+void export_gate_list_to_binary(Matrix_real& parameters, Gates_block* gates_block, FILE* pFile, int verbosity=3);
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+Gates_block* import_gate_list_from_binary(Matrix_real& parameters, const std::string& filename, int verbosity=3);
+
+
+/**
+@brief ?????????
+@return Return with ?????????
+*/
+Gates_block* import_gate_list_from_binary(Matrix_real& parameters, FILE* pFile, int verbosity=3);
+
 #endif //GATES_BLOCK
 
diff --git a/gates/include/RX.h b/gates/include/RX.h
index efb2aae8..20d5d147 100644
--- a/gates/include/RX.h
+++ b/gates/include/RX.h
@@ -65,7 +65,7 @@ RX(int qbit_num_in, int target_qbit_in);
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
 @param input The input array on which the gate is applied
 */
-void apply_to( Matrix_real& parameters, Matrix& input, const double scale=1.0 );
+void apply_to( Matrix_real& parameters, Matrix& input );
 
 
 /**
diff --git a/gates/include/RZ.h b/gates/include/RZ.h
index b1f0a04b..a352c082 100644
--- a/gates/include/RZ.h
+++ b/gates/include/RZ.h
@@ -65,7 +65,7 @@ RZ(int qbit_num_in, int target_qbit_in);
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
 @param input The input array on which the gate is applied
 */
-void apply_to( Matrix_real& parameters, Matrix& input, const double scale=1.0 );
+void apply_to( Matrix_real& parameters, Matrix& input );
 
 
 /**
diff --git a/gates/include/SX.h b/gates/include/SX.h
index 5508fd6f..c173c926 100644
--- a/gates/include/SX.h
+++ b/gates/include/SX.h
@@ -24,15 +24,18 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #ifndef SX_H
 #define SX_H
 
-#include "Gate.h"
+
+#include "U3.h"
+//#include "Gate.h"
 #include "matrix.h"
 #include <math.h>
 
 
+
 /**
 @brief A class representing a U3 gate.
 */
-class SX: public Gate {
+class SX: public U3 {
 
 
 public:
diff --git a/gates/include/U3.h b/gates/include/U3.h
index 41f39142..aecee72d 100644
--- a/gates/include/U3.h
+++ b/gates/include/U3.h
@@ -89,7 +89,7 @@ Matrix get_matrix( Matrix_real& parameters );
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
 @param input The input array on which the gate is applied
 */
-void apply_to_list( Matrix_real& parameters, std::vector<Matrix>& input, const double scale=1.0 );
+void apply_to_list( Matrix_real& parameters, std::vector<Matrix>& input );
 
 
 /**
@@ -97,16 +97,7 @@ void apply_to_list( Matrix_real& parameters, std::vector<Matrix>& input, const d
 @param parameters An array of parameters to calculate the matrix of the U3 gate.
 @param input The input array on which the gate is applied
 */
-virtual void apply_to( Matrix_real& parameters, Matrix& input, const double scale=1.0 );
-
-protected:
-/**
-@brief ???????????
-*/
-void apply_kernel_to( Matrix& u3_1qbit, Matrix& input );
-
-
-public:
+virtual void apply_to( Matrix_real& parameters, Matrix& input );
 
 
 /**
@@ -124,15 +115,6 @@ virtual std::vector<Matrix> apply_derivate_to( Matrix_real& parameters, Matrix&
 virtual void apply_from_right( Matrix_real& parameters, Matrix& input );
 
 
-protected:
-
-/**
-@brief ???????????
-*/
-void apply_kernel_from_right( Matrix& u3_1qbit, Matrix& input );
-
-
-public:
 
 /**
 @brief Call to set the number of qubits spanning the matrix of the gate
@@ -175,7 +157,7 @@ bool is_lambda_parameter();
 @param Lambda Real parameter standing for the parameter lambda.
 @return Returns with the matrix of the one-qubit matrix.
 */
-Matrix calc_one_qubit_u3(double Theta, double Phi, double Lambda, const double scale=1.0 );
+Matrix calc_one_qubit_u3(double Theta, double Phi, double Lambda );
 
 
 /**
diff --git a/gates/include/X.h b/gates/include/X.h
index c1de4588..7e64b550 100644
--- a/gates/include/X.h
+++ b/gates/include/X.h
@@ -24,7 +24,8 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #ifndef X_H
 #define X_H
 
-#include "Gate.h"
+#include "U3.h"
+//#include "Gate.h"
 #include "matrix.h"
 #include <math.h>
 
@@ -32,7 +33,7 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 /**
 @brief A class representing a U3 gate.
 */
-class X: public Gate {
+class X: public U3 {
 
 
 public:
diff --git a/qgd_python/decomposition/CMakeLists.txt b/qgd_python/decomposition/CMakeLists.txt
index 0aea168a..e05f1f2c 100644
--- a/qgd_python/decomposition/CMakeLists.txt
+++ b/qgd_python/decomposition/CMakeLists.txt
@@ -4,6 +4,7 @@ set(EXT_DIR ${PROJECT_SOURCE_DIR}/qgd_python/decomposition)
 
 add_library( qgd_N_Qubit_Decomposition_Wrapper MODULE
     ${EXT_DIR}/qgd_N_Qubit_Decomposition_Wrapper.cpp
+    ${PROJECT_SOURCE_DIR}/common/numpy_interface.cpp        
 )
 
 
@@ -23,7 +24,7 @@ target_compile_options(qgd_N_Qubit_Decomposition_Wrapper PRIVATE
 )
 
 
-target_include_directories(qgd_N_Qubit_Decomposition_Wrapper PRIVATE
+target_include_directories(qgd_N_Qubit_Decomposition_Wrapper PRIVATE    ${PROJECT_SOURCE_DIR}/common/numpy_interface.cpp
                             ${PYTHON_INCLUDE_DIR}
                             ${NUMPY_INC_DIR}
                             ${PROJECT_SOURCE_DIR}/decomposition/include
@@ -50,6 +51,7 @@ install(TARGETS qgd_N_Qubit_Decomposition_Wrapper LIBRARY
 
 add_library( qgd_N_Qubit_Decomposition_adaptive_Wrapper MODULE
     ${EXT_DIR}/qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
+    ${PROJECT_SOURCE_DIR}/common/numpy_interface.cpp    
 )
 
 
@@ -97,6 +99,7 @@ install(TARGETS qgd_N_Qubit_Decomposition_adaptive_Wrapper LIBRARY
 
 add_library( qgd_N_Qubit_Decomposition_custom_Wrapper MODULE
     ${EXT_DIR}/qgd_N_Qubit_Decomposition_custom_Wrapper.cpp
+    ${PROJECT_SOURCE_DIR}/common/numpy_interface.cpp        
 )
 
 
diff --git a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_Wrapper.cpp b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_Wrapper.cpp
index 39158331..851fb32e 100644
--- a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_Wrapper.cpp
+++ b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_Wrapper.cpp
@@ -31,74 +31,9 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include <stdio.h>
 #include "N_Qubit_Decomposition.h"
 
-#include <numpy/arrayobject.h>
-#include "matrix.h"
-#include "matrix_real.h"
-
-
-/**
-@brief Method to cleanup the memory when the python object becomes released
-@param capsule Pointer to the memory capsule
-*/
-void capsule_cleanup(PyObject* capsule) {
-
-    void *memory = PyCapsule_GetPointer(capsule, NULL);
-    // I'm going to assume your memory needs to be freed with free().
-    // If it needs different cleanup, perform whatever that cleanup is
-    // instead of calling free().
-    scalable_aligned_free(memory);
-
-
-}
-
-
-
-
-/**
-@brief Call to make a numpy array from data stored via void pointer.
-@param ptr pointer pointing to the data
-@param dim The number of dimensions
-@param shape array containing the dimensions.
-@param np_type The data type stored in the numpy array (see possible values at https://numpy.org/doc/1.17/reference/c-api.dtype.html)
-*/
-PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type) {
-
-        if (PyArray_API == NULL) {
-            import_array();
-        }
-
-
-        // create numpy array
-        PyObject* arr = PyArray_SimpleNewFromData(dim, shape, np_type, ptr);
-
-        // set memory keeper for the numpy array
-        PyObject *capsule = PyCapsule_New(ptr, NULL, capsule_cleanup);
-        PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
-
-        return arr;
-
-}
-
-
+#include "numpy_interface.h"
 
-/**
-@brief Call to make a numpy array from an instance of matrix class.
-@param mtx a matrix instance
-*/
-PyObject* matrix_real_to_numpy( matrix_base<double> &mtx ) {
-        // initialize Numpy API
-        import_array();
-
-
-        npy_intp shape[2];
-        shape[0] = (npy_intp) mtx.rows;
-        shape[1] = (npy_intp) mtx.cols;
 
-        double* data = mtx.get_data();
-        return array_from_ptr( (void*) data, 2, shape, NPY_DOUBLE);
-
-
-}
 
 
 
@@ -155,44 +90,6 @@ release_N_Qubit_Decomposition( N_Qubit_Decomposition*  instance ) {
 
 
 
-/**
-@brief Call to create a PIC matrix representation of a numpy array
-*/
-Matrix
-numpy2matrix(PyObject *arr) {
-
-    // test C-style contiguous memory allocation of the arrays
-    if ( !PyArray_IS_C_CONTIGUOUS(arr) ) {
-        std::cout << "array is not memory contiguous" << std::endl;
-    }
-
-    // get the pointer to the data stored in the input matrices
-    QGD_Complex16* data = (QGD_Complex16*)PyArray_DATA(arr);
-
-    // get the dimensions of the array self->C
-    int dim_num = PyArray_NDIM( arr );
-    npy_intp* dims = PyArray_DIMS(arr);
-
-    // create PIC version of the input matrices
-    if (dim_num == 2) {
-        Matrix mtx = Matrix(data, dims[0], dims[1]);
-        return mtx;
-    }
-    else if (dim_num == 1) {
-        Matrix mtx = Matrix(data, dims[0], 1);
-        return mtx;
-    }
-    else {
-        std::cout << "numpy2matrix: Wrong matrix dimension was given" << std::endl;
-        return Matrix(0,0);
-    }
-
-
-
-}
-
-
-
 extern "C"
 {
 
@@ -668,12 +565,12 @@ qgd_N_Qubit_Decomposition_Wrapper_get_Optimized_Parameters( qgd_N_Qubit_Decompos
 
     int parameter_num = self->decomp->get_parameter_num();
 
-    matrix_base<double> parameters_mtx(1, parameter_num);
+    Matrix_real parameters_mtx(1, parameter_num);
     double* parameters = parameters_mtx.get_data();
     self->decomp->get_optimized_parameters(parameters);
 
     // reversing the order
-    matrix_base<double> parameters_mtx_reversed(1, parameter_num);
+    Matrix_real parameters_mtx_reversed(1, parameter_num);
     double* parameters_reversed = parameters_mtx_reversed.get_data();
     for (int idx=0; idx<parameter_num; idx++ ) {
         parameters_reversed[idx] = parameters[parameter_num-1-idx];
diff --git a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive.py b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive.py
index 56044660..02867197 100644
--- a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive.py
+++ b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive.py
@@ -242,7 +242,7 @@ def import_Qiskit_Circuit( self, qc_in ):
 
         qc = transpile(qc_in, optimization_level=0, basis_gates=['cz', 'u3'], layout_method='sabre')
         #print('Depth of imported Qiskit transpiled quantum circuit:', qc.depth())
-        #print('Gate counts in teh imported Qiskit transpiled quantum circuit:', qc.count_ops())
+        print('Gate counts in teh imported Qiskit transpiled quantum circuit:', qc.count_ops())
         #print(qc)
         
         # get the size of the register
@@ -293,6 +293,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                         params.reverse()
                         for param in params:
                             optimized_parameters = optimized_parameters + [float(param)]
+                        optimized_parameters[-1] = optimized_parameters[-1]/2                            
                         
 
                 if len(single_qubit_gates[qubit1])>0:
@@ -302,9 +303,10 @@ def import_Qiskit_Circuit( self, qc_in ):
                     if gate1['type'] == 'u3':
                         Layer.add_U3( qubit1, True, True, True )
                         params = gate1['params']
-                        params.reverse()
+                        params.reverse()                      
                         for param in params:
                             optimized_parameters = optimized_parameters + [float(param)]
+                        optimized_parameters[-1] = optimized_parameters[-1]/2
 
                 ## add cz gate to the layer  
                 #Layer.add_CZ( qubit1, qubit0 )
@@ -314,7 +316,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                 Layer.add_adaptive( qubit0, qubit1 )
                 Layer.add_RZ( qubit1 )
                 Layer.add_RX( qubit0 )
-                optimized_parameters = optimized_parameters + [np.pi/2, np.pi, -np.pi/2, -np.pi/2]
+                optimized_parameters = optimized_parameters + [np.pi/4, np.pi/2, -np.pi/2, -np.pi/4]
                 #optimized_parameters = optimized_parameters + [np.pi]
 
                 Gates_Block_ret.add_Gates_Block( Layer )
@@ -336,6 +338,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                     params.reverse()
                     for param in params:
                         optimized_parameters = optimized_parameters + [float(param)]
+                    optimized_parameters[-1] = optimized_parameters[-1]/2                        
 
         Gates_Block_ret.add_Gates_Block( Layer )
 
@@ -345,4 +348,71 @@ def import_Qiskit_Circuit( self, qc_in ):
         self.set_Gate_Structure(Gates_Block_ret)
         self.set_Optimized_Parameters( optimized_parameters )
           
+##
+# @brief Call to set custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER
+# @param filename String containing the filename
+    def set_Gate_Structure_From_Binary( self, filename ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).set_Gate_Structure_From_Binary( filename )
+
+
+##
+# @brief Call to add an adaptive layer to the gate structure previously imported gate structure
+    def add_Layer_To_Imported_Gate_Structure( self ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).add_Layer_To_Imported_Gate_Structure()
+
+
+
+##
+# @brief Call to set the radius in which randomized parameters are generated around the current minimum duting the optimization process
+    def set_Randomized_Radius( self, radius ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).set_Randomized_Radius(radius)
+
+##
+# @brief Call to append custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER
+    def add_Gate_Structure_From_Binary( self, filename ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).add_Gate_Structure_From_Binary( filename )
+        
+##
+# @brief Call to set unitary matrix from a binary file created from SQUANDER
+    def set_Unitary_From_Binary( self, filename ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).set_Unitary_From_Binary( filename )
+ 
+##
+# @brief Call to set unitary matrix from a numpy array
+    def set_Unitary( self, Umtx_arr ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).set_Unitary( Umtx_arr )
+        
+##
+# @brief Call to get unitary matrix
+    def get_Unitary( self ):
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).get_Unitary()
+
+##
+# @brief Call to get global phase
+    def get_Global_Phase( self ):
+	
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).get_Global_Phase()
+
+##
+# @brief Call to set global phase 
+    def set_Global_Phase( self, angle ):
+	
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).set_Global_Phase(angle)
+##
+# @brief Call to apply global phase on Unitary matrix
+    def apply_Global_Phase( self ):
+	
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).apply_Global_Phase()
+##
+# @brief Call to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gate structure is released.
+    def apply_Imported_Gate_Structure( self ):  
+
+        return super(qgd_N_Qubit_Decomposition_adaptive, self).apply_Imported_Gate_Structure()
 
diff --git a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
index f838c7fc..df07f0dd 100644
--- a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
+++ b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_adaptive_Wrapper.cpp
@@ -33,74 +33,8 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include "N_Qubit_Decomposition_adaptive_general.h"
 #include "Gates_block.h"
 
-#include <numpy/arrayobject.h>
-#include "matrix.h"
-#include "matrix_real.h"
-
-
-/**
-@brief Method to cleanup the memory when the python object becomes released
-@param capsule Pointer to the memory capsule
-*/
-void capsule_cleanup(PyObject* capsule) {
-
-    void *memory = PyCapsule_GetPointer(capsule, NULL);
-    // I'm going to assume your memory needs to be freed with free().
-    // If it needs different cleanup, perform whatever that cleanup is
-    // instead of calling free().
-    scalable_aligned_free(memory);
-
-
-}
-
-
-
-
-/**
-@brief Call to make a numpy array from data stored via void pointer.
-@param ptr pointer pointing to the data
-@param dim The number of dimensions
-@param shape array containing the dimensions.
-@param np_type The data type stored in the numpy array (see possible values at https://numpy.org/doc/1.17/reference/c-api.dtype.html)
-*/
-PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type) {
+#include "numpy_interface.h"
 
-        if (PyArray_API == NULL) {
-            import_array();
-        }
-
-
-        // create numpy array
-        PyObject* arr = PyArray_SimpleNewFromData(dim, shape, np_type, ptr);
-
-        // set memory keeper for the numpy array
-        PyObject *capsule = PyCapsule_New(ptr, NULL, capsule_cleanup);
-        PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
-
-        return arr;
-
-}
-
-
-
-/**
-@brief Call to make a numpy array from an instance of matrix class.
-@param mtx a matrix instance
-*/
-PyObject* matrix_real_to_numpy( matrix_base<double> &mtx ) {
-        // initialize Numpy API
-        import_array();
-
-
-        npy_intp shape[2];
-        shape[0] = (npy_intp) mtx.rows;
-        shape[1] = (npy_intp) mtx.cols;
-
-        double* data = mtx.get_data();
-        return array_from_ptr( (void*) data, 2, shape, NPY_DOUBLE);
-
-
-}
 
 
 
@@ -192,41 +126,6 @@ release_N_Qubit_Decomposition_adaptive_general( N_Qubit_Decomposition_adaptive_g
 
 
 
-/**
-@brief Call to create a PIC matrix representation of a numpy array
-*/
-Matrix
-numpy2matrix(PyObject *arr) {
-
-    // test C-style contiguous memory allocation of the arrays
-    if ( !PyArray_IS_C_CONTIGUOUS(arr) ) {
-        std::cout << "array is not memory contiguous" << std::endl;
-    }
-
-    // get the pointer to the data stored in the input matrices
-    QGD_Complex16* data = (QGD_Complex16*)PyArray_DATA(arr);
-
-    // get the dimensions of the array self->C
-    int dim_num = PyArray_NDIM( arr );
-    npy_intp* dims = PyArray_DIMS(arr);
-
-    // create PIC version of the input matrices
-    if (dim_num == 2) {
-        Matrix mtx = Matrix(data, dims[0], dims[1]);
-        return mtx;
-    }
-    else if (dim_num == 1) {
-        Matrix mtx = Matrix(data, dims[0], 1);
-        return mtx;
-    }
-    else {
-        std::cout << "numpy2matrix: Wrong matrix dimension was given" << std::endl;
-        return Matrix(0,0);
-    }
-
-
-
-}
 
 
 
@@ -321,9 +220,7 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_init(qgd_N_Qubit_Decomposition_adapti
 
 
     // create QGD version of the Umtx
-    Matrix Umtx_mtx = numpy2matrix(self->Umtx);  
-
-
+    Matrix Umtx_mtx = numpy2matrix(self->Umtx);
 
 
     // determine the optimizaton method
@@ -419,10 +316,16 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_Start_Decomposition(qgd_N_Qubit_Decom
                                      &prepare_export))
         return Py_BuildValue("i", -1);
 
-
     // starting the decomposition
     if (  self->decomp != NULL ) {
-        self->decomp->start_decomposition(prepare_export);
+        try {
+            self->decomp->start_decomposition(prepare_export);
+        }
+        catch (std::string err) {
+            PyErr_SetString(PyExc_Exception, err.c_str());
+            std::cout << err << std::endl;
+            return NULL;
+        }
     }
     else if(  self->decomp_general != NULL ) {
         self->decomp_general->start_decomposition(prepare_export);
@@ -438,6 +341,10 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_Start_Decomposition(qgd_N_Qubit_Decom
 
 
 
+
+
+
+
 /**
 @brief Wrapper function to get the number of decomposing gates.
 @param self A pointer pointing to an instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
@@ -732,6 +639,44 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_gates( qgd_N_Qubit_Decomposition_
 
 }
 
+/**
+@brief returns the angle of the global phase (the radius us always sqrt(2))
+@param self A pointer pointing to an instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Global_Phase(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self ) {
+
+    QGD_Complex16 global_phase_C = self->decomp_base->get_global_phase();
+    PyObject* global_phase = PyFloat_FromDouble( std::atan2(global_phase_C.imag,global_phase_C.real));
+    return global_phase;
+    
+}
+
+/**
+@brief sets the global phase to the new angle given
+@param self A pointer pointing to an instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
+@param arg Global_phase_new_angle the angle to be set
+*/
+static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Global_Phase(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args) {
+	double global_phase_new_angle;
+    if (!PyArg_ParseTuple(args, "|d", &global_phase_new_angle )) return Py_BuildValue("i", -1);
+    std::cout<<global_phase_new_angle<<std::endl;
+    self->decomp_base->set_global_phase(global_phase_new_angle);
+    return Py_BuildValue("i", 0);
+    
+}
+
+/**
+@brief applies the global phase to the Unitary matrix
+@param self A pointer pointing to an instance of the class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
+*/
+static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Global_Phase(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self ) {
+
+    // get the number of gates
+    self->decomp_base->apply_global_phase();
+    return Py_BuildValue("i", 0);
+    
+}
 
 /**
 @brief Lists the gates decomposing the initial unitary. (These gates are the inverse gates of the gates bringing the intial matrix into unity.)
@@ -762,7 +707,7 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Optimized_Parameters( qgd_N_Qubit
     self->decomp_base->get_optimized_parameters(parameters);
 
     // reversing the order
-    matrix_base<double> parameters_mtx_reversed(1, parameter_num);
+    Matrix_real parameters_mtx_reversed(1, parameter_num);
     double* parameters_reversed = parameters_mtx_reversed.get_data();
     for (int idx=0; idx<parameter_num; idx++ ) {
         parameters_reversed[idx] = parameters[parameter_num-1-idx];
@@ -1082,14 +1027,174 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure( qgd_N_Qubit_Decom
     else if(  self->decomp_general != NULL ) {
         self->decomp_general->set_adaptive_gate_structure( qgd_op_block->gate );
     }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+
+    return Py_BuildValue("i", 0);
+
+
+}
+
+
+
+/**
+@brief Wrapper function to append custom layers to the gate structure that are intended to be used in the decomposition.
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Gate_Structure_From_Binary( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args ) {
+
+
+
+    // initiate variables for input arguments
+    PyObject* filename_py=NULL; 
+
+    // parsing input arguments
+    if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
+
+    // determine the optimizaton method
+    PyObject* filename_string = PyObject_Str(filename_py);
+    PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
+    const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
+    std::string filename_str( filename_C );
+
+    if (  self->decomp != NULL ) {
+        self->decomp->add_adaptive_gate_structure( filename_str );
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->add_adaptive_gate_structure( filename_str );
+    }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+    return Py_BuildValue("i", 0);
+
+}
+
+
+
+/**
+@brief Wrapper function to set custom layers to the gate structure that are intended to be used in the decomposition.
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure_From_Binary( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args ) {
+
+
+
+    // initiate variables for input arguments
+    PyObject* filename_py=NULL; 
+
+    // parsing input arguments
+    if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
+
+    // determine the optimizaton method
+    PyObject* filename_string = PyObject_Str(filename_py);
+    PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
+    const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
+    std::string filename_str( filename_C );
+
+    if (  self->decomp != NULL ) {
+        self->decomp->set_adaptive_gate_structure( filename_str );
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->set_adaptive_gate_structure( filename_str );
+    }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+    return Py_BuildValue("i", 0);
 
+}
+
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary_From_Binary(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args ){
+    // initiate variables for input arguments
+    PyObject* filename_py=NULL; 
+
+    // parsing input arguments
+    if (!PyArg_ParseTuple(args, "|O", &filename_py )) return Py_BuildValue("i", -1);
+    
+    PyObject* filename_string = PyObject_Str(filename_py);
+    PyObject* filename_string_unicode = PyUnicode_AsEncodedString(filename_string, "utf-8", "~E~");
+    const char* filename_C = PyBytes_AS_STRING(filename_string_unicode);
+    std::string filename_str( filename_C );
+    
+    if (  self->decomp != NULL ) {
+        self->decomp->set_unitary_from_file( filename_str );
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->set_unitary_from_file( filename_str );
+    }
 
     return Py_BuildValue("i", 0);
+}
+
+/**
+@brief Wrapper function to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gate structure is released.
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Imported_Gate_Structure( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self ) {
 
 
+    if (  self->decomp != NULL ) {
+        self->decomp->apply_imported_gate_structure();
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->apply_imported_gate_structure();
+    }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+    return Py_BuildValue("i", 0);
+
 }
 
+/**
+@brief Extract the optimized parameters
+@param start_index The index of the first inverse gate
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Unitary( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self) {
+
+
+    Matrix&& Unitary_mtx = self->decomp->get_Umtx().copy();
+    
+    // convert to numpy array
+    Unitary_mtx.set_owner(false);
+    PyObject *Unitary_py = matrix_to_numpy( Unitary_mtx );
+
+    return Unitary_py;
+}
 
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args ) {
+           if ( self->Umtx != NULL ) {
+        // release the unitary to be decomposed
+        Py_DECREF(self->Umtx);    
+        self->Umtx = NULL;
+       }
+       PyObject *Umtx_arg = NULL;
+       //Parse arguments 
+       if (!PyArg_ParseTuple(args, "|O", &Umtx_arg )) return Py_BuildValue("i", -1);
+	   // convert python object array to numpy C API array
+		if ( Umtx_arg == NULL ) return -1;
+		self->Umtx = PyArray_FROM_OTF(Umtx_arg, NPY_COMPLEX128, NPY_ARRAY_IN_ARRAY);
+
+		// test C-style contiguous memory allocation of the array
+		if ( !PyArray_IS_C_CONTIGUOUS(self->Umtx) ) {
+		    std::cout << "Umtx is not memory contiguous" << std::endl;
+		}
+
+
+		// create QGD version of the Umtx
+		Matrix Umtx_mtx = numpy2matrix(self->Umtx);
+       self->decomp->set_unitary(Umtx_mtx);
+       return Py_BuildValue("i", 0);
+}
 
 /**
 @brief Wrapper method to reorder the qubits in the decomposition class.
@@ -1148,6 +1253,64 @@ qgd_N_Qubit_Decomposition_adaptive_Wrapper_Reorder_Qubits(qgd_N_Qubit_Decomposit
 
 
 
+
+
+/**
+@brief Wrapper method to reorder the qubits in the decomposition class.
+@param 
+*/
+static PyObject *
+qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Layer_To_Imported_Gate_Structure(qgd_N_Qubit_Decomposition_adaptive_Wrapper *self ) {
+
+
+    if (  self->decomp != NULL ) {
+        self->decomp->add_layer_to_imported_gate_structure();
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->add_layer_to_imported_gate_structure();
+    }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+    return Py_BuildValue("i", 0);
+}
+
+
+
+/**
+@brief Wrapper function to set the radius in which randomized parameters are generated around the current minimum duting the optimization process
+@param self A pointer pointing to an instance of the class qgd_N_Qubit_Decomposition_custom_Wrapper.
+@param args A tuple of the input arguments: gate_structure_dict (PyDict)
+@return Returns with zero on success.
+*/
+static PyObject * qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Randomized_Radius( qgd_N_Qubit_Decomposition_adaptive_Wrapper *self, PyObject *args ) {
+
+    // initiate variables for input arguments
+    double radius = 1.0; 
+
+    // parsing input arguments
+    if (!PyArg_ParseTuple(args, "|d", &radius )) return Py_BuildValue("i", -1);
+
+
+    if (  self->decomp != NULL ) {
+        self->decomp->set_randomized_radius( radius );
+    }
+    else if(  self->decomp_general != NULL ) {
+        self->decomp_general->set_randomized_radius( radius );
+    }
+    else {
+        return Py_BuildValue("i", 1);
+    }
+
+    return Py_BuildValue("i", 0);
+
+
+}
+
+
+
+
 /**
 @brief Structure containing metadata about the members of class qgd_N_Qubit_Decomposition_adaptive_Wrapper.
 */
@@ -1207,6 +1370,38 @@ static PyMethodDef qgd_N_Qubit_Decomposition_adaptive_Wrapper_methods[] = {
     {"set_Optimization_Blocks", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Optimization_Blocks, METH_VARARGS,
      "Wrapper method to to set the number of gate blocks to be optimized in one shot."
     },
+    {"set_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Gate_Structure_From_Binary, METH_VARARGS,
+     "Call to set custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER"
+    },
+    {"add_Gate_Structure_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Gate_Structure_From_Binary, METH_VARARGS,
+     "Call to append custom layers to the gate structure that are intended to be used in the decomposition from a binary file created from SQUANDER"
+    },
+    {"set_Unitary_From_Binary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary_From_Binary, METH_VARARGS,
+     "Call to set unitary matrix from a binary file created from SQUANDER"
+    },
+    {"set_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Unitary, METH_VARARGS,
+     "Call to set unitary matrix to a numpy matrix"
+    },
+    {"get_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Global_Phase, METH_NOARGS,
+     "Call to get global phase"
+    },
+    {"set_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Global_Phase, METH_VARARGS,
+     "Call to set global phase"
+    },
+    {"apply_Global_Phase", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Global_Phase, METH_NOARGS,
+     "Call to apply global phase on Unitary matrix"},
+    {"get_Unitary", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_get_Unitary, METH_NOARGS,
+     "Call to get Unitary Matrix"
+    },
+    {"add_Layer_To_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_add_Layer_To_Imported_Gate_Structure, METH_NOARGS,
+     "Call to add an adaptive layer to the gate structure previously imported gate structure"
+    },
+    {"apply_Imported_Gate_Structure", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_apply_Imported_Gate_Structure, METH_NOARGS,
+     "Call to apply the imported gate structure on the unitary. The transformed unitary is to be decomposed in the calculations, and the imported gate structure is released."
+    },
+    {"set_Randomized_Radius", (PyCFunction) qgd_N_Qubit_Decomposition_adaptive_Wrapper_set_Randomized_Radius, METH_VARARGS,
+     "Call to set the radius in which randomized parameters are generated around the current minimum duting the optimization process."
+    },
     {NULL}  /* Sentinel */
 };
 
diff --git a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom.py b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom.py
index 0582c423..ff93c344 100644
--- a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom.py
+++ b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom.py
@@ -271,6 +271,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                         params.reverse()
                         for param in params:
                             optimized_parameters = optimized_parameters + [float(param)]
+                        optimized_parameters[-1] = optimized_parameters[-1]/2 #SQUADER works with theta/2
                         
 
                 if len(single_qubit_gates[qubit1])>0:
@@ -283,6 +284,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                         params.reverse()
                         for param in params:
                             optimized_parameters = optimized_parameters + [float(param)]
+                        optimized_parameters[-1] = optimized_parameters[-1]/2 #SQUADER works with theta/2
 
                 ## add cz gate to the layer       
                 if name == 'cz':
@@ -309,6 +311,7 @@ def import_Qiskit_Circuit( self, qc_in ):
                     params.reverse()
                     for param in params:
                         optimized_parameters = optimized_parameters + [float(param)]
+                    optimized_parameters[-1] = optimized_parameters[-1]/2 #SQUADER works with theta/2
 
         Gates_Block_ret.add_Gates_Block( Layer )
 
diff --git a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom_Wrapper.cpp b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom_Wrapper.cpp
index 74968933..3e6377fa 100644
--- a/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom_Wrapper.cpp
+++ b/qgd_python/decomposition/qgd_N_Qubit_Decomposition_custom_Wrapper.cpp
@@ -31,74 +31,8 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 #include <stdio.h>
 #include "N_Qubit_Decomposition_custom.h"
 
-#include <numpy/arrayobject.h>
-#include "matrix.h"
-#include "matrix_real.h"
-
-
-/**
-@brief Method to cleanup the memory when the python object becomes released
-@param capsule Pointer to the memory capsule
-*/
-void capsule_cleanup(PyObject* capsule) {
-
-    void *memory = PyCapsule_GetPointer(capsule, NULL);
-    // I'm going to assume your memory needs to be freed with free().
-    // If it needs different cleanup, perform whatever that cleanup is
-    // instead of calling free().
-    scalable_aligned_free(memory);
-
-
-}
-
-
-
-
-/**
-@brief Call to make a numpy array from data stored via void pointer.
-@param ptr pointer pointing to the data
-@param dim The number of dimensions
-@param shape array containing the dimensions.
-@param np_type The data type stored in the numpy array (see possible values at https://numpy.org/doc/1.17/reference/c-api.dtype.html)
-*/
-PyObject* array_from_ptr(void * ptr, int dim, npy_intp* shape, int np_type) {
-
-        if (PyArray_API == NULL) {
-            import_array();
-        }
-
-
-        // create numpy array
-        PyObject* arr = PyArray_SimpleNewFromData(dim, shape, np_type, ptr);
-
-        // set memory keeper for the numpy array
-        PyObject *capsule = PyCapsule_New(ptr, NULL, capsule_cleanup);
-        PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
-
-        return arr;
-
-}
-
-
-
-/**
-@brief Call to make a numpy array from an instance of matrix class.
-@param mtx a matrix instance
-*/
-PyObject* matrix_real_to_numpy( matrix_base<double> &mtx ) {
-        // initialize Numpy API
-        import_array();
-
-
-        npy_intp shape[2];
-        shape[0] = (npy_intp) mtx.rows;
-        shape[1] = (npy_intp) mtx.cols;
+#include "numpy_interface.h"
 
-        double* data = mtx.get_data();
-        return array_from_ptr( (void*) data, 2, shape, NPY_DOUBLE);
-
-
-}
 
 
 
@@ -154,44 +88,6 @@ release_N_Qubit_Decomposition_custom( N_Qubit_Decomposition_custom*  instance )
 
 
 
-/**
-@brief Call to create a PIC matrix representation of a numpy array
-*/
-Matrix
-numpy2matrix(PyObject *arr) {
-
-    // test C-style contiguous memory allocation of the arrays
-    if ( !PyArray_IS_C_CONTIGUOUS(arr) ) {
-        std::cout << "array is not memory contiguous" << std::endl;
-    }
-
-    // get the pointer to the data stored in the input matrices
-    QGD_Complex16* data = (QGD_Complex16*)PyArray_DATA(arr);
-
-    // get the dimensions of the array self->C
-    int dim_num = PyArray_NDIM( arr );
-    npy_intp* dims = PyArray_DIMS(arr);
-
-    // create PIC version of the input matrices
-    if (dim_num == 2) {
-        Matrix mtx = Matrix(data, dims[0], dims[1]);
-        return mtx;
-    }
-    else if (dim_num == 1) {
-        Matrix mtx = Matrix(data, dims[0], 1);
-        return mtx;
-    }
-    else {
-        std::cout << "numpy2matrix: Wrong matrix dimension was given" << std::endl;
-        return Matrix(0,0);
-    }
-
-
-
-}
-
-
-
 extern "C"
 {
 
@@ -664,12 +560,12 @@ static PyObject *
 qgd_N_Qubit_Decomposition_custom_Wrapper_get_Optimized_Parameters( qgd_N_Qubit_Decomposition_custom_Wrapper *self ) {
 
     int parameter_num = self->decomp->get_parameter_num();
-    matrix_base<double> parameters_mtx(1, parameter_num);
+    Matrix_real parameters_mtx(1, parameter_num);
     double* parameters = parameters_mtx.get_data();
     self->decomp->get_optimized_parameters(parameters);
 
     // reversing the order
-    matrix_base<double> parameters_mtx_reversed(1, parameter_num);
+    Matrix_real parameters_mtx_reversed(1, parameter_num);
     double* parameters_reversed = parameters_mtx_reversed.get_data();
     for (int idx=0; idx<parameter_num; idx++ ) {
         parameters_reversed[idx] = parameters[parameter_num-1-idx];
diff --git a/qgd_python/gates/CMakeLists.txt b/qgd_python/gates/CMakeLists.txt
index 27f1ea43..d94f1843 100644
--- a/qgd_python/gates/CMakeLists.txt
+++ b/qgd_python/gates/CMakeLists.txt
@@ -4,6 +4,7 @@ set(EXT_DIR ${PROJECT_SOURCE_DIR}/qgd_python/gates)
 
 add_library( qgd_U3 MODULE
     ${EXT_DIR}/qgd_U3.cpp
+    ${PROJECT_SOURCE_DIR}/common/numpy_interface.cpp
 )
 
 
diff --git a/qgd_python/gates/qgd_U3.cpp b/qgd_python/gates/qgd_U3.cpp
index 9917dc99..8bf5c35e 100644
--- a/qgd_python/gates/qgd_U3.cpp
+++ b/qgd_python/gates/qgd_U3.cpp
@@ -26,8 +26,10 @@ along with this program.  If not, see http://www.gnu.org/licenses/.
 
 
 #include <Python.h>
+#include <numpy/arrayobject.h>
 #include "structmember.h"
 #include "U3.h"
+#include "numpy_interface.h"
 
 
 
@@ -70,8 +72,6 @@ release_U3( U3*  instance ) {
 
 
 
-
-
 extern "C"
 {
 
@@ -132,6 +132,50 @@ qgd_U3_init(qgd_U3 *self, PyObject *args, PyObject *kwds)
 }
 
 
+
+
+
+/**
+@brief Extract the optimized parameters
+@param start_index The index of the first inverse gate
+*/
+static PyObject *
+qgd_U3_get_Matrix( qgd_U3 *self, PyObject *args ) {
+
+    PyObject * parameters_arr = NULL;
+
+
+    // parsing input arguments
+    if (!PyArg_ParseTuple(args, "|O", &parameters_arr )) 
+        return Py_BuildValue("i", -1);
+
+    
+    if ( PyArray_IS_C_CONTIGUOUS(parameters_arr) ) {
+        Py_INCREF(parameters_arr);
+    }
+    else {
+        parameters_arr = PyArray_FROM_OTF(parameters_arr, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
+    }
+
+
+    // get the C++ wrapper around the data
+    Matrix_real&& parameters_mtx = numpy2matrix_real( parameters_arr );
+
+
+    Matrix U3_mtx = self->gate->get_matrix( parameters_mtx );
+    
+    // convert to numpy array
+    U3_mtx.set_owner(false);
+    PyObject *U3_py = matrix_to_numpy( U3_mtx );
+
+
+    Py_DECREF(parameters_arr);
+
+    return U3_py;
+}
+
+
+
 /**
 @brief Structure containing metadata about the members of class qgd_U3.
 */
@@ -144,6 +188,9 @@ static PyMemberDef qgd_U3_members[] = {
 @brief Structure containing metadata about the methods of class qgd_U3.
 */
 static PyMethodDef qgd_U3_methods[] = {
+    {"get_Matrix", (PyCFunction) qgd_U3_get_Matrix, METH_VARARGS,
+     "Method to get the matrix of the operation."
+    },
     {NULL}  /* Sentinel */
 };
 
diff --git a/setup.py b/setup.py
index e5f70abb..93e75a85 100644
--- a/setup.py
+++ b/setup.py
@@ -40,7 +40,7 @@
     maintainer_email="peter.rakyta@ttk.elte.hu",
     include_package_data=True,
     install_requires=[
-        "numpy>=1.19.4",
+        "numpy>=1.19.2",
 	"tbb-devel",
 	"qiskit",
     ],


Test patch:
diff --git a/qgd_python/decomposition/test/test_IBM.py b/qgd_python/decomposition/test/test_IBM.py
index 84cf4977..4e797465 100644
--- a/qgd_python/decomposition/test/test_IBM.py
+++ b/qgd_python/decomposition/test/test_IBM.py
@@ -28,6 +28,14 @@
 import numpy as np
 
 
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
+
+
 class Test_Decomposition:
     """This is a test class of the python iterface to the decompsition classes of the QGD package"""
 
@@ -130,7 +138,7 @@ def test_IBM_Chellenge_adaptive(self):
 
 
         # setting the verbosity of the decomposition
-        cDecompose.set_Verbose( True )
+        cDecompose.set_Verbose( 3 )
 
         # starting the decomposition
         cDecompose.Start_Decomposition()
@@ -172,3 +180,4 @@ def test_IBM_Chellenge_adaptive(self):
         assert( decomposition_error < 1e-3 )
         
 
+
diff --git a/qgd_python/decomposition/test/test_QX2.py b/qgd_python/decomposition/test/test_QX2.py
index df56e0a9..7fbc69fd 100644
--- a/qgd_python/decomposition/test/test_QX2.py
+++ b/qgd_python/decomposition/test/test_QX2.py
@@ -28,6 +28,15 @@
 import numpy as np
 
 
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
+
+
+
 class Test_Decomposition:
     """This is a test class of the python iterface to the decompsition classes of the QGD package"""
 
diff --git a/qgd_python/decomposition/test/test_Unitary.py b/qgd_python/decomposition/test/test_Unitary.py
new file mode 100644
index 00000000..e9081a42
--- /dev/null
+++ b/qgd_python/decomposition/test/test_Unitary.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Jun 26 14:42:56 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+"""
+## \file test_decomposition.py
+## \brief Functionality test cases for the qgd_N_Qubit_Decomposition class.
+
+
+
+# cerate unitary q-bit matrix
+from scipy.stats import unitary_group
+import numpy as np
+
+
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
+from qgd_python.decomposition.qgd_N_Qubit_Decomposition_adaptive import qgd_N_Qubit_Decomposition_adaptive
+from scipy.io import loadmat
+
+def test_unitary():
+	# load the unitary from file
+	data = loadmat('Umtx.mat')
+	# The unitary to be decomposed  
+	Umtx = data['Umtx']
+	# creating a class to decompose the unitary
+	cDecompose = qgd_N_Qubit_Decomposition_adaptive( Umtx.conj().T, level_limit_max=5, level_limit_min=0 )
+	Umtx_assert = cDecompose.get_Unitary()
+	assert(np.sum(np.abs(Umtx_assert-Umtx.conj().T))<0.00001)
+	Umtx_assert[0,0]=1
+	cDecompose.set_Unitary(Umtx_assert)
+	Umtx_assert_new=cDecompose.get_Unitary()
+	assert(Umtx_assert_new[0,0]==1)
diff --git a/qgd_python/decomposition/test/test_decomposition.py b/qgd_python/decomposition/test/test_decomposition.py
index e6d6c306..ae2ebe67 100644
--- a/qgd_python/decomposition/test/test_decomposition.py
+++ b/qgd_python/decomposition/test/test_decomposition.py
@@ -27,6 +27,12 @@
 from scipy.stats import unitary_group
 import numpy as np
 
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
 
 class Test_Decomposition:
     """This is a test class of the python iterface to the decompsition classes of the QGD package"""
diff --git a/qgd_python/decomposition/test/test_fmo.py b/qgd_python/decomposition/test/test_fmo.py
index 21910947..57f2a768 100644
--- a/qgd_python/decomposition/test/test_fmo.py
+++ b/qgd_python/decomposition/test/test_fmo.py
@@ -10,6 +10,11 @@
 from qiskit import assemble,Aer
 from qiskit import execute
 
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
 
 
 class Test_Decomposition:
diff --git a/qgd_python/decomposition/test/test_global_phase.py b/qgd_python/decomposition/test/test_global_phase.py
new file mode 100644
index 00000000..bac8ecb9
--- /dev/null
+++ b/qgd_python/decomposition/test/test_global_phase.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Jun 26 14:42:56 2020
+Copyright (C) 2020 Peter Rakyta, Ph.D.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see http://www.gnu.org/licenses/.
+
+@author: Peter Rakyta, Ph.D.
+"""
+## \file test_decomposition.py
+## \brief Functionality test cases for the qgd_N_Qubit_Decomposition class.
+
+
+
+# cerate unitary q-bit matrix
+from scipy.stats import unitary_group
+import numpy as np
+
+
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
+from qgd_python.decomposition.qgd_N_Qubit_Decomposition_adaptive import qgd_N_Qubit_Decomposition_adaptive
+from scipy.io import loadmat
+
+def test_global_phase():
+	# load the unitary from file
+	data = loadmat('Umtx.mat')
+	# The unitary to be decomposed  
+	Umtx = data['Umtx']
+	# creating a class to decompose the unitary
+	cDecompose = qgd_N_Qubit_Decomposition_adaptive( Umtx.conj().T, level_limit_max=5, level_limit_min=0 )
+	angl = cDecompose.get_Global_Phase()
+	Umtx_assert = cDecompose.get_Unitary()
+	assert(np.abs(angl)<2*np.pi)
+	angl_new = np.pi/3
+	cDecompose.set_Global_Phase(angl_new)
+	global_phase = np.sqrt(2)*np.cos(angl_new)+1j*np.sqrt(2)*np.sin(angl_new)
+	cDecompose.apply_Global_Phase()
+	angl_assert = cDecompose.get_Global_Phase()
+	assert(np.abs(angl_assert-np.pi/4)<1e-5)
+	Umtx_assert_new=cDecompose.get_Unitary()
+	assert(np.sum(np.abs(Umtx_assert*global_phase-Umtx_assert_new))<0.00001)
+	   
diff --git a/qgd_python/decomposition/test/test_heavy_hex.py b/qgd_python/decomposition/test/test_heavy_hex.py
index 8b0b60fd..45d7e953 100644
--- a/qgd_python/decomposition/test/test_heavy_hex.py
+++ b/qgd_python/decomposition/test/test_heavy_hex.py
@@ -28,6 +28,13 @@
 import numpy as np
 
 
+try:
+    from mpi4py import MPI
+    MPI_imported = True
+except ModuleNotFoundError:
+    MPI_imported = False
+
+
 class Test_Decomposition:
     """This is a test class of the python iterface to the decompsition classes of the QGD package"""
 
diff --git a/qgd_python/gates/test/test_U3.py b/qgd_python/gates/test/test_U3.py
new file mode 100644
index 00000000..3544be62
--- /dev/null
+++ b/qgd_python/gates/test/test_U3.py
@@ -0,0 +1,42 @@
+import numpy as np
+import random
+
+
+class Test_operations:
+    """This is a test class of the python iterface to the gates of the QGD package"""
+
+
+
+    def test_U3(self):
+        r"""
+        This method is called by pytest. 
+        Test to create an instance of U3 gate.
+
+        """
+
+        from qgd_python.gates.qgd_U3 import qgd_U3
+
+        # number of qubits
+        qbit_num = 3
+
+        # target qbit
+        target_qbit = 0
+
+        # set the free parameters
+        Theta = True
+        Phi = True
+        Lambda = True        
+
+        # creating an instance of the C++ class
+        U3 = qgd_U3( qbit_num, target_qbit, Theta, Phi, Lambda )
+        
+        parameters = np.array( [random.random(), random.random(), random.random()] )
+        
+        U3_matrix = U3.get_Matrix( parameters )
+        
+        print(U3_matrix)
+
+
+
+      
+