//+------------------------------------------------------------------+ //| RCL.mqh | //| Copyright 2026, Leo. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Leo." #property link "https://www.mql5.com/en/users/nique_372" #property strict #ifndef SCALERBYLEO_SRC_SCALER_RSCALER_RCL_MQH #define SCALERBYLEO_SRC_SCALER_RSCALER_RCL_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "RBase.mqh" #include "..\\..\\CL\\Main.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ //--- kernel transform / inverse (mismo layout de args en ambos kernels) #define SCALERBYLEO_RCL_TRANSFORM_IDX_MEDIAN (0) #define SCALERBYLEO_RCL_TRANSFORM_IDX_IQRINV (1) #define SCALERBYLEO_RCL_TRANSFORM_IDX_IQR (1) #define SCALERBYLEO_RCL_TRANSFORM_IDX_MATRIX (2) #define SCALERBYLEO_RCL_TRANSFORM_IDX_COLS (3) #define SCALERBYLEO_RCL_TRANSFORM_IDX_ISTART (4) //--- #define SCALERBYLEO_RCL_KNAME_TRANSFORM ("robust_scaler_tranform_matrix") #define SCALERBYLEO_RCL_KNAME_INV_TRANSFORM ("robust_scaler_inverse_tranform_matrix") //+------------------------------------------------------------------+ //| Robust Scaler (OpenCL) | //+------------------------------------------------------------------+ template class RobustScalerCL : public RobustScalerBase { private: bool OnLoad() override final; //--- int m_buffer_h_medians; int m_buffer_h_iqrs; int m_buffer_h_iqrs_inv; //--- int m_kernel_transform; int m_kernel_inv_transform; //--- bool m_is_init; public: RobustScalerCL(void); ~RobustScalerCL(void) {} //--- __forceinline bool FitTransform(matrix &X, matrix &out); bool Transform(matrix &X, matrix &out); bool InverseTransform(matrix &X_scaled, matrix &out); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template RobustScalerCL::RobustScalerCL() : m_buffer_h_medians(INVALID_HANDLE), m_buffer_h_iqrs(INVALID_HANDLE), m_buffer_h_iqrs_inv(INVALID_HANDLE), m_kernel_transform(INVALID_HANDLE), m_kernel_inv_transform(INVALID_HANDLE), m_is_init(false) { m_type = SCALERBYLEO_SCALER_TYPE_ROBUST_CL; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool RobustScalerCL::OnLoad() { //--- if(!m_is_init) { //--- MQLART_TRY_INI //--- if(sizeof(TFlt) == sizeof(float)) { MQLART_TRY(COpenCLCtxManager::ProgramInit(g_cl_scaler_program_flt), true) } else { MQLART_TRY(COpenCLCtxManager::ProgramInit(g_cl_scaler_program_dbl), true) } //--- MQLART_TRY(COpenCLCtxManager::KernelGet(SCALERBYLEO_RCL_KNAME_TRANSFORM, m_kernel_transform), true) MQLART_TRY(COpenCLCtxManager::KernelGet(SCALERBYLEO_RCL_KNAME_INV_TRANSFORM, m_kernel_inv_transform), true) //--- m_is_init = true; //--- MQLART_TRY_END //--- if(MQLART_TRY_IS_ERR) { m_last_err = SCALERBYLEO_ERR_IN_CTX_MANAGER_CL; return false; } } //--- actulizamos los 3 buffers OPENCLMANAGER_MAKE_VECTOR(m_buffer_h_medians, m_medians, CL_MEM_READ_ONLY) OPENCLMANAGER_MAKE_VECTOR(m_buffer_h_iqrs, m_iqrs, CL_MEM_READ_ONLY) OPENCLMANAGER_MAKE_VECTOR(m_buffer_h_iqrs_inv, m_iqrs_inv, CL_MEM_READ_ONLY) return true; // exito } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template __forceinline bool RobustScalerCL::FitTransform(matrix &X, matrix &out) { //--- if(!Fit(X)) return false; if(!Transform(X, out)) return false; //--- return true; } //+------------------------------------------------------------------+ //| Transform | //+------------------------------------------------------------------+ template bool RobustScalerCL::Transform(matrix &X, matrix &out) { //--- if(!m_loaded_scaler) { m_last_err = SCALERBYLEO_ERR_NOT_TRAINED; return false; } //--- chekeo de rows const ulong rows = X.Rows(); if(rows == 0) { m_last_err = SCALERBYLEO_ERR_EMPTY_INPUT; return false; } //--- chekeo de cols const ulong total_cols = X.Cols(); const ulong fs = m_medians.Size(); if(m_i_start + fs > total_cols) { m_last_err = SCALERBYLEO_ERR_SIZE_MISMATCH; return false; } //--- init int matrix_h = INVALID_HANDLE; ::ResetLastError(); //--- MQLART_TRY_INI //--- Decidimos que subimos a la GPU segun el caso (patron validado en Bench/CLPattern.mq5) ulong istart, cl_cols; if(m_reconstruir && fs != total_cols) { //--- Caso ambiguo: subimos TODO + offset (Estrategia A, gano el benchmark) out.Resize(rows, total_cols); istart = m_i_start; cl_cols = total_cols; const uint write = uint(sizeof(TFlt) * total_cols * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, X), true) } else { istart = 0; if(fs == total_cols) { //--- Caso trivial: se escala todo, no hay nada que reconstruir out.Resize(rows, total_cols); cl_cols = total_cols; const uint write = uint(sizeof(TFlt) * total_cols * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, X), true) } else { //--- Caso !reconstruir: solo interesa el rango, subimos angosto out.Resize(rows, fs); cl_cols = fs; matrix in(rows, fs); for(ulong c = 0; c < fs; c++) for(ulong r = 0; r < rows; r++) in[r][c] = X[r][m_i_start + c]; const uint write = uint(sizeof(TFlt) * fs * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, in), true) } } //--- MQLART_TRY(CLSetKernelArgMem(m_kernel_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_MEDIAN, m_buffer_h_medians), true) MQLART_TRY(CLSetKernelArgMem(m_kernel_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_IQRINV, m_buffer_h_iqrs_inv), true) MQLART_TRY(CLSetKernelArgMem(m_kernel_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_MATRIX, matrix_h), true) MQLART_TRY(CLSetKernelArg(m_kernel_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_COLS, uint(cl_cols)), true) MQLART_TRY(CLSetKernelArg(m_kernel_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_ISTART, uint(istart)), true) //--- const uint work_off_set[2] = {0, 0}; uint work_size_tranfors[2] = { (uint)rows, (uint)fs }; MQLART_TRY(CLExecute(m_kernel_transform, 2, work_off_set, work_size_tranfors), true) CLBufferRead(matrix_h, 0, out); //--- MQLART_TRY_END //--- if(matrix_h != INVALID_HANDLE) CLBufferFree(matrix_h); //--- if(MQLART_TRY_IS_ERR) { m_last_err = SCALERBYLEO_ERR_IN_OPENCL; return false; } //--- return true; } //+------------------------------------------------------------------+ //| InverseTransform | //+------------------------------------------------------------------+ template bool RobustScalerCL::InverseTransform(matrix &X_scaled, matrix &out) { //--- if(!m_loaded_scaler) { m_last_err = SCALERBYLEO_ERR_NOT_TRAINED; return false; } //--- chekeo de rows const ulong rows = X_scaled.Rows(); if(rows == 0) { m_last_err = SCALERBYLEO_ERR_EMPTY_INPUT; return false; } //--- chekeo de cols const ulong total_cols = X_scaled.Cols(); const ulong fs = m_medians.Size(); if(m_i_start + fs > total_cols) { m_last_err = SCALERBYLEO_ERR_SIZE_MISMATCH; return false; } //--- init int matrix_h = INVALID_HANDLE; ::ResetLastError(); //--- MQLART_TRY_INI //--- ulong istart, cl_cols; if(m_reconstruir && fs != total_cols) { out.Resize(rows, total_cols); istart = m_i_start; cl_cols = total_cols; const uint write = uint(sizeof(TFlt) * total_cols * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, X_scaled), true) } else { istart = 0; if(fs == total_cols) { out.Resize(rows, total_cols); cl_cols = total_cols; const uint write = uint(sizeof(TFlt) * total_cols * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, X_scaled), true) } else { out.Resize(rows, fs); cl_cols = fs; matrix in(rows, fs); for(ulong c = 0; c < fs; c++) for(ulong r = 0; r < rows; r++) in[r][c] = X_scaled[r][m_i_start + c]; const uint write = uint(sizeof(TFlt) * fs * rows); matrix_h = CLBufferCreate(CCLCtxShared::s_context, write, CL_MEM_READ_WRITE); MQLART_TRY_NOT(matrix_h, INVALID_HANDLE) MQLART_TRY(CLBufferWrite(matrix_h, 0, in), true) } } //--- MQLART_TRY(CLSetKernelArgMem(m_kernel_inv_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_MEDIAN, m_buffer_h_medians), true) MQLART_TRY(CLSetKernelArgMem(m_kernel_inv_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_IQR, m_buffer_h_iqrs), true) MQLART_TRY(CLSetKernelArgMem(m_kernel_inv_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_MATRIX, matrix_h), true) MQLART_TRY(CLSetKernelArg(m_kernel_inv_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_COLS, uint(cl_cols)), true) MQLART_TRY(CLSetKernelArg(m_kernel_inv_transform, SCALERBYLEO_RCL_TRANSFORM_IDX_ISTART, uint(istart)), true) //--- const uint work_off_set[2] = {0, 0}; uint work_size_tranfors[2] = { (uint)rows, (uint)fs }; MQLART_TRY(CLExecute(m_kernel_inv_transform, 2, work_off_set, work_size_tranfors), true) CLBufferRead(matrix_h, 0, out); //--- MQLART_TRY_END //--- if(matrix_h != INVALID_HANDLE) CLBufferFree(matrix_h); //--- if(MQLART_TRY_IS_ERR) { m_last_err = SCALERBYLEO_ERR_IN_OPENCL; return false; } //--- return true; } } #endif // SCALERBYLEO_SRC_SCALER_RSCALER_RCL_MQH