diff --git a/docs/2026.html b/docs/2026.html
index ddb6f5f918..474f8a0042 100644
--- a/docs/2026.html
+++ b/docs/2026.html
@@ -93,6 +93,7 @@
New features
C++ wrapper Simd::SynetInnerProduct32f.
C++ wrapper Simd::SynetInnerProduct16b.
C++ wrapper Simd::SynetQuantizedInnerProduct.
+ C++ wrapper Simd::SynetDeconvolution32f.
Improving
diff --git a/prj/txt/DoxygenGroups.txt b/prj/txt/DoxygenGroups.txt
index 0e27061780..fd678538d2 100644
--- a/prj/txt/DoxygenGroups.txt
+++ b/prj/txt/DoxygenGroups.txt
@@ -93,7 +93,7 @@
/*! @ingroup cpp_types
@defgroup cpp_synet Synet Wrappers
- \short Simd::SynetAdd16b, Simd::SynetQuantizedAdd, Simd::SynetQuantizedMul, Simd::SynetGatherElements, Simd::SynetPermute, Simd::SynetInnerProduct32f, Simd::SynetInnerProduct16b and Simd::SynetQuantizedInnerProduct classes (C++ wrappers of Synet operations).
+ \short Simd::SynetAdd16b, Simd::SynetQuantizedAdd, Simd::SynetQuantizedMul, Simd::SynetGatherElements, Simd::SynetPermute, Simd::SynetInnerProduct32f, Simd::SynetInnerProduct16b, Simd::SynetQuantizedInnerProduct and Simd::SynetDeconvolution32f classes (C++ wrappers of Synet operations).
*/
/*! @ingroup cpp_types
diff --git a/src/Simd/SimdLib.h b/src/Simd/SimdLib.h
index 3dc6e03f5b..5c798b5821 100644
--- a/src/Simd/SimdLib.h
+++ b/src/Simd/SimdLib.h
@@ -8119,6 +8119,8 @@ extern "C"
compatibility flags. Weights, bias and activation parameters are attached later by
::SimdSynetDeconvolution32fSetParams.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in] batch - a batch size.
\param [in] conv - a pointer to deconvolution parameters. Source and destination tensor types must be FP32.
\param [in] compatibility - calculation compatibility flags.
@@ -8138,6 +8140,8 @@ extern "C"
during initialization and can be used to allocate the \a buf argument of ::SimdSynetDeconvolution32fForward.
Some implementations return 1 when they do not need external temporary storage.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in] context - a pointer to FP32 deconvolution context. It must be created by function ::SimdSynetDeconvolution32fInit and released by function ::SimdRelease.
\return a number of FP32 elements required for external temporary buffer.
*/
@@ -8153,6 +8157,8 @@ extern "C"
the selected implementation, such as internal temporary buffers and implementation-specific reordered weights,
bias or activation parameters already allocated by the context.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in] context - a pointer to FP32 deconvolution context. It must be created by function ::SimdSynetDeconvolution32fInit and released by function ::SimdRelease.
\return a number of FP32 elements used by internal buffers.
*/
@@ -8168,6 +8174,8 @@ extern "C"
NHWC direct 2x2 variant. The returned pointer is owned by the context and remains valid until the next call
of this function for the same context or until the context is released.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in] context - a pointer to FP32 deconvolution context. It must be created by function ::SimdSynetDeconvolution32fInit and released by function ::SimdRelease.
\return a string with description of internal implementation of FP32 deconvolution algorithm.
*/
@@ -8187,6 +8195,8 @@ extern "C"
so the caller must keep it valid for later forward calls. Bias and activation parameters can also be copied
internally by some implementations; otherwise their pointers are stored in the context.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in, out] context - a pointer to FP32 deconvolution context. It must be created by function ::SimdSynetDeconvolution32fInit and released by function ::SimdRelease.
\param [in] weight - a pointer to FP32 deconvolution weights.
\param [out] internal - a pointer to a flag receiving weight ownership mode. Can be NULL.
@@ -8217,6 +8227,8 @@ extern "C"
The exact offsets depend on tensor format, padding, dilation, stride and group. The input and output tensors
use the shape and format from the context created by ::SimdSynetDeconvolution32fInit.
+ \note This function has a C++ wrapper: Simd::SynetDeconvolution32f.
+
\param [in] context - a pointer to FP32 deconvolution context. It must be created by function ::SimdSynetDeconvolution32fInit and released by function ::SimdRelease.
\param [in] src - a pointer to FP32 input tensor.
\param [out] buf - a pointer to external temporary FP32 buffer. The required number of elements is determined by
diff --git a/src/Simd/SimdSynet.hpp b/src/Simd/SimdSynet.hpp
index 4e8cd880bd..6e2786ba92 100644
--- a/src/Simd/SimdSynet.hpp
+++ b/src/Simd/SimdSynet.hpp
@@ -1483,6 +1483,271 @@ namespace Simd
SimdTensorDataType _typeA, _typeB, _typeC;
SimdBool _transB, _constB, _bias;
};
+
+ //-------------------------------------------------------------------------------------------------
+
+ /*! @ingroup cpp_synet
+
+ \short The SynetDeconvolution32f class is a C++ wrapper of FP32 deconvolution (transposed convolution).
+
+ The class wraps C API functions ::SimdSynetDeconvolution32fInit, ::SimdSynetDeconvolution32fExternalBufferSize,
+ ::SimdSynetDeconvolution32fInternalBufferSize, ::SimdSynetDeconvolution32fInfo, ::SimdSynetDeconvolution32fSetParams and
+ ::SimdSynetDeconvolution32fForward. It applies transposed convolution to each image in the batch, optionally adds bias
+ and applies activation:
+ \verbatim
+ dst[:] = 0;
+ for(sc = 0; sc < srcC/group; ++sc)
+ for(sy = 0; sy < srcH; ++sy)
+ for(sx = 0; sx < srcW; ++sx)
+ for(ky = 0; ky < kernelY; ++ky)
+ for(kx = 0; kx < kernelX; ++kx)
+ dst[outputOffset] += src[inputOffset] * weight[weightOffset];
+ dst[outputOffset] = Activate(dst[outputOffset] + bias[dc], activation, params);
+ \endverbatim
+
+ The exact offsets depend on tensor format, padding, dilation, stride and group. The current implementation
+ supports FP32 source and destination tensors with matching NCHW format, or matching NHWC format when group is 1.
+ The destination spatial size must match deconvolution parameters:
+ \verbatim
+ dstH = strideY*(srcH - 1) + dilationY*(kernelY - 1) + 1 - padY - padH
+ dstW = strideX*(srcW - 1) + dilationX*(kernelX - 1) + 1 - padX - padW
+ \endverbatim
+
+ Call Init() and SetParams() before Forward(). Use Enable() to check that a context was created.
+ The context is released by Clear() or by the destructor.
+
+ Using example:
+ \verbatim
+ #include "Simd/SimdSynet.hpp"
+
+ int main()
+ {
+ const size_t batch = 1, srcC = 4, srcH = 3, srcW = 3, dstC = 4;
+ SimdConvolutionParameters conv = {};
+ conv.srcC = srcC;
+ conv.srcH = srcH;
+ conv.srcW = srcW;
+ conv.srcT = SimdTensorData32f;
+ conv.srcF = SimdTensorFormatNhwc;
+ conv.dstC = dstC;
+ conv.kernelY = 2;
+ conv.kernelX = 2;
+ conv.dilationY = 1;
+ conv.dilationX = 1;
+ conv.strideY = 2;
+ conv.strideX = 2;
+ conv.padY = 0;
+ conv.padX = 0;
+ conv.padH = 0;
+ conv.padW = 0;
+ conv.group = 1;
+ conv.activation = SimdConvolutionActivationIdentity;
+ conv.dstH = conv.strideY * (conv.srcH - 1) + conv.dilationY * (conv.kernelY - 1) + 1 - conv.padY - conv.padH;
+ conv.dstW = conv.strideX * (conv.srcW - 1) + conv.dilationX * (conv.kernelX - 1) + 1 - conv.padX - conv.padW;
+ conv.dstT = SimdTensorData32f;
+ conv.dstF = SimdTensorFormatNhwc;
+
+ std::vector src(batch * srcH * srcW * srcC);
+ std::vector weight(conv.kernelY * conv.kernelX * srcC * dstC / conv.group);
+ std::vector bias(dstC, 0.0f);
+ std::vector dst(batch * conv.dstH * conv.dstW * dstC, 0.0f);
+ for (size_t i = 0; i < src.size(); ++i)
+ src[i] = float(i) * 0.01f;
+ for (size_t i = 0; i < weight.size(); ++i)
+ weight[i] = float(i) * 0.02f;
+
+ Simd::SynetDeconvolution32f deconvolution;
+ deconvolution.Init(batch, &conv);
+ if (deconvolution.Enable())
+ {
+ deconvolution.SetParams(weight.data(), NULL, bias.data(), NULL);
+ deconvolution.Forward(src.data(), NULL, dst.data());
+ }
+
+ return 0;
+ }
+ \endverbatim
+ */
+ class SynetDeconvolution32f
+ {
+ public:
+ /*!
+ Creates a new empty SynetDeconvolution32f class.
+ */
+ SynetDeconvolution32f()
+ : _context(NULL)
+ , _batch(0)
+ , _compatibility(SimdSynetCompatibilityDefault)
+ {
+ SimdConvolutionParameters conv = {};
+ _conv = conv;
+ }
+
+ /*!
+ SynetDeconvolution32f class destructor. Releases internal context.
+ */
+ virtual ~SynetDeconvolution32f()
+ {
+ Clear();
+ }
+
+ /*!
+ Initializes (or re-initializes) an FP32 deconvolution context.
+
+ Creates an internal context with using of function ::SimdSynetDeconvolution32fInit.
+ The context is recreated only if batch size, deconvolution parameters or compatibility flags were changed.
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fInit.
+
+ \param [in] batch - a batch size.
+ \param [in] conv - a pointer to deconvolution parameters. Source and destination tensor types must be FP32.
+ \param [in] compatibility - calculation compatibility flags.
+ */
+ SIMD_INLINE void Init(size_t batch, const SimdConvolutionParameters * conv, SimdSynetCompatibilityType compatibility = SimdSynetCompatibilityDefault)
+ {
+ if (conv == NULL)
+ return;
+ if (_batch != batch || Changed(*conv) || _compatibility != compatibility)
+ {
+ Clear();
+ _batch = batch;
+ _conv = *conv;
+ _compatibility = compatibility;
+ _context = SimdSynetDeconvolution32fInit(_batch, &_conv, _compatibility);
+ }
+ }
+
+ /*!
+ Checks that the internal deconvolution context was created.
+
+ \return true if the context exists and Forward() can be called.
+ */
+ SIMD_INLINE bool Enable() const
+ {
+ return _context != NULL;
+ }
+
+ /*!
+ Gets the size of caller-provided temporary buffer for FP32 deconvolution.
+
+ The returned value is a number of FP32 elements. It depends on the implementation selected
+ during initialization and can be used when allocating the \a buf argument of Forward().
+ Some implementations return 1 when they do not need external temporary storage.
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fExternalBufferSize.
+
+ \return a number of FP32 elements required for external temporary buffer.
+ */
+ SIMD_INLINE size_t ExternalBufferSize() const
+ {
+ return _context ? SimdSynetDeconvolution32fExternalBufferSize(_context) : 0;
+ }
+
+ /*!
+ Gets the size of internal storage used by the deconvolution context.
+
+ The returned value is a number of FP32 elements. It reports internal temporary buffers and
+ implementation-specific reordered weights, bias or activation parameters already allocated by the context.
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fInternalBufferSize.
+
+ \return a number of FP32 elements used by internal buffers.
+ */
+ SIMD_INLINE size_t InternalBufferSize() const
+ {
+ return _context ? SimdSynetDeconvolution32fInternalBufferSize(_context) : 0;
+ }
+
+ /*!
+ Gets a short description of the selected FP32 deconvolution implementation.
+
+ The returned string contains the implementation extension and algorithm name, for example a GEMM-based or
+ NHWC direct 2x2 variant. The returned pointer is owned by the context and remains valid until the next call
+ of this function or until the context is released.
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fInfo.
+
+ \return a string with description of internal implementation. NULL if the context was not created.
+ */
+ SIMD_INLINE const char * Info() const
+ {
+ return _context ? SimdSynetDeconvolution32fInfo(_context) : NULL;
+ }
+
+ /*!
+ Sets weights, bias and activation parameters for FP32 deconvolution.
+
+ This function must be called before Forward(). The \a weight array contains FP32 deconvolution weights
+ with kernelY*kernelX*srcC*dstC/group elements. Depending on the selected implementation, weights can be
+ used directly or transformed and stored inside the context. If \a internal is not NULL, ::SimdTrue means
+ the weights were copied/reordered into the context; ::SimdFalse means the original \a weight pointer can
+ be used by later forward calls and must remain valid.
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fSetParams.
+
+ \param [in] weight - a pointer to FP32 deconvolution weights.
+ \param [out] internal - a pointer to a flag receiving weight storage mode. Can be NULL.
+ \param [in] bias - a pointer to FP32 bias array with dstC elements. Can be NULL.
+ \param [in] params - a pointer to FP32 parameters of activation function (see ::SimdConvolutionActivationType). Can be NULL when activation does not require parameters.
+ */
+ SIMD_INLINE void SetParams(const float * weight, SimdBool * internal, const float * bias, const float * params)
+ {
+ if (_context)
+ SimdSynetDeconvolution32fSetParams(_context, weight, internal, bias, params);
+ }
+
+ /*!
+ Performs FP32 deconvolution forward propagation.
+
+ The function applies transposed convolution to each image in the batch, adds bias when it was set,
+ and applies the activation stored in the context created by Init() and SetParams().
+ The \a buf argument can be NULL (it causes usage of internal buffer).
+
+ \note This function is a C++ wrapper for function ::SimdSynetDeconvolution32fForward.
+
+ \param [in] src - a pointer to FP32 input tensor.
+ \param [out] buf - a pointer to external temporary FP32 buffer. Can be NULL.
+ \param [out] dst - a pointer to FP32 output tensor.
+ */
+ SIMD_INLINE void Forward(const float * src, float * buf, float * dst)
+ {
+ if (_context)
+ SimdSynetDeconvolution32fForward(_context, src, buf, dst);
+ }
+
+ /*!
+ Releases internal context and clears stored deconvolution parameters.
+ */
+ SIMD_INLINE void Clear()
+ {
+ if (_context)
+ SimdRelease(_context), _context = NULL;
+ _batch = 0;
+ SimdConvolutionParameters conv = {};
+ _conv = conv;
+ _compatibility = SimdSynetCompatibilityDefault;
+ }
+
+ private:
+ SIMD_INLINE bool Changed(const SimdConvolutionParameters & conv) const
+ {
+ return _conv.srcC != conv.srcC || _conv.srcH != conv.srcH || _conv.srcW != conv.srcW ||
+ _conv.srcT != conv.srcT || _conv.srcF != conv.srcF ||
+ _conv.dstC != conv.dstC || _conv.dstH != conv.dstH || _conv.dstW != conv.dstW ||
+ _conv.dstT != conv.dstT || _conv.dstF != conv.dstF ||
+ _conv.kernelY != conv.kernelY || _conv.kernelX != conv.kernelX ||
+ _conv.dilationY != conv.dilationY || _conv.dilationX != conv.dilationX ||
+ _conv.strideY != conv.strideY || _conv.strideX != conv.strideX ||
+ _conv.padY != conv.padY || _conv.padX != conv.padX ||
+ _conv.padH != conv.padH || _conv.padW != conv.padW ||
+ _conv.group != conv.group || _conv.activation != conv.activation;
+ }
+
+ void * _context;
+ size_t _batch;
+ SimdConvolutionParameters _conv;
+ SimdSynetCompatibilityType _compatibility;
+ };
}
#endif
diff --git a/src/Test/TestCheckCpp.cpp b/src/Test/TestCheckCpp.cpp
index f1ac0a40e5..ba7d31f93f 100644
--- a/src/Test/TestCheckCpp.cpp
+++ b/src/Test/TestCheckCpp.cpp
@@ -485,6 +485,74 @@ namespace Test
std::cout << "TestSynetQuantizedInnerProduct is failed at " << i << " : " << (int)C1[i] << " != " << (int)C2[i] << std::endl;
}
}
+
+ static void TestSynetDeconvolution32f()
+ {
+ const size_t batch = 1, srcC = 4, srcH = 3, srcW = 3, dstC = 4;
+ SimdConvolutionParameters conv = {};
+ conv.srcC = srcC;
+ conv.srcH = srcH;
+ conv.srcW = srcW;
+ conv.srcT = SimdTensorData32f;
+ conv.srcF = SimdTensorFormatNhwc;
+ conv.dstC = dstC;
+ conv.kernelY = 2;
+ conv.kernelX = 2;
+ conv.dilationY = 1;
+ conv.dilationX = 1;
+ conv.strideY = 2;
+ conv.strideX = 2;
+ conv.padY = 0;
+ conv.padX = 0;
+ conv.padH = 0;
+ conv.padW = 0;
+ conv.group = 1;
+ conv.activation = SimdConvolutionActivationIdentity;
+ conv.dstH = conv.strideY * (conv.srcH - 1) + conv.dilationY * (conv.kernelY - 1) + 1 - conv.padY - conv.padH;
+ conv.dstW = conv.strideX * (conv.srcW - 1) + conv.dilationX * (conv.kernelX - 1) + 1 - conv.padX - conv.padW;
+ conv.dstT = SimdTensorData32f;
+ conv.dstF = SimdTensorFormatNhwc;
+
+ const size_t srcSize = batch * srcH * srcW * srcC;
+ const size_t weightSize = conv.kernelY * conv.kernelX * srcC * dstC / conv.group;
+ const size_t dstSize = batch * conv.dstH * conv.dstW * dstC;
+ std::vector src(srcSize), weight(weightSize), bias(dstC, 0.1f);
+ std::vector dst1(dstSize, 0.0f), dst2(dstSize, 0.0f);
+ for (size_t i = 0; i < src.size(); ++i)
+ src[i] = float(i) * 0.01f;
+ for (size_t i = 0; i < weight.size(); ++i)
+ weight[i] = float(i) * 0.02f;
+
+ Simd::SynetDeconvolution32f deconvolution;
+ deconvolution.Init(batch, &conv);
+ if (deconvolution.Enable())
+ {
+ deconvolution.SetParams(weight.data(), NULL, bias.data(), NULL);
+ deconvolution.Forward(src.data(), NULL, dst1.data());
+ }
+
+ void* context = SimdSynetDeconvolution32fInit(batch, &conv, SimdSynetCompatibilityDefault);
+ if (context)
+ {
+ SimdSynetDeconvolution32fSetParams(context, weight.data(), NULL, bias.data(), NULL);
+ SimdSynetDeconvolution32fForward(context, src.data(), NULL, dst2.data());
+ if (deconvolution.InternalBufferSize() != SimdSynetDeconvolution32fInternalBufferSize(context))
+ std::cout << "TestSynetDeconvolution32f is failed : InternalBufferSize mismatch" << std::endl;
+ if (deconvolution.ExternalBufferSize() != SimdSynetDeconvolution32fExternalBufferSize(context))
+ std::cout << "TestSynetDeconvolution32f is failed : ExternalBufferSize mismatch" << std::endl;
+ const char* info1 = deconvolution.Info();
+ const char* info2 = SimdSynetDeconvolution32fInfo(context);
+ if ((info1 == NULL) != (info2 == NULL) || (info1 && info2 && std::strcmp(info1, info2) != 0))
+ std::cout << "TestSynetDeconvolution32f is failed : Info mismatch" << std::endl;
+ SimdRelease(context);
+ }
+
+ for (size_t i = 0; i < dst1.size(); ++i)
+ {
+ if (dst1[i] != dst2[i])
+ std::cout << "TestSynetDeconvolution32f is failed at " << i << " : " << dst1[i] << " != " << dst2[i] << std::endl;
+ }
+ }
#endif
void CheckCpp()
@@ -526,6 +594,7 @@ namespace Test
TestSynetInnerProduct32f();
TestSynetInnerProduct16b();
TestSynetQuantizedInnerProduct();
+ TestSynetDeconvolution32f();
#endif
}
}