# cqp/app.cpp /******************************************************************************* Copyright (c) 2022-2023 Qualcomm Technologies, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Qualcomm Technologies, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @brief Program to run the FastADAS Convex Quadratic Programming solver. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "../util/util.h" uint32_t n = 0, m = 0, m_e = 0; float64_t precision = 0.0; /* FIXME remove old #define UPDATE_QPOASES_VEC_PATH(testVec, prob) \ {\ snprintf( pathG, 256, "%s/%s/G%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, n, n );\ snprintf( pathav, 256, "%s/%s/g%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, n, 1 );\ snprintf( pathC, 256, "%s/%s/A%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, m, n );\ snprintf( pathbv, 256, "%s/%s/b%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, m, 1 );\ snprintf( pathxl, 256, "%s/%s/l%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, n, 1 );\ snprintf( pathxu, 256, "%s/%s/u%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, n, 1 );\ snprintf( pathx_gt, 256, "%s/%s/x_gt%u_%u_%u.txt", CQP_VEC_PATH, testVec, prob, n, 1 );\ } */ #if defined(_WIN32) || defined(_WIN64) || defined(__x86_64__) //#define CQP_VEC_PATH "../../../tst/vectors/CQPOasis" #define CQP_VEC_PATH "../../../tmp/data/BMW_18k" #else #define CQP_VEC_PATH "../vectors/CQPS" #endif #define UPDATE_QPOASES_VEC_PATH(testVec, prob) \ {\ snprintf( pathDims, 256, "%s/%s/dims.txt", CQP_VEC_PATH, (testVec) );\ snprintf( pathG, 256, "%s/%s/G.txt", CQP_VEC_PATH, (testVec) );\ snprintf( pathav, 256, "%s/%s/p%04u/a.txt", CQP_VEC_PATH, (testVec), (prob) );\ snprintf( pathC, 256, "%s/%s/C.txt", CQP_VEC_PATH, (testVec) );\ snprintf( pathbv, 256, "%s/%s/p%04u/bv.txt", CQP_VEC_PATH, (testVec), (prob) );\ snprintf( pathxl, 256, "%s/%s/p%04u/xl.txt", CQP_VEC_PATH, (testVec), (prob) );\ snprintf( pathxu, 256, "%s/%s/p%04u/xu.txt", CQP_VEC_PATH, (testVec), (prob) );\ snprintf( pathx_gt, 256, "%s/%s/p%04u/x_gt.txt", CQP_VEC_PATH, (testVec), (prob) );\ snprintf( pathobj_gt, 256, "%s/%s/p%04u/obj_gt.txt", CQP_VEC_PATH, (testVec), (prob) );\ } float64_t dtime; using namespace std::chrono; // To be updated on consideration of new data const uint32_t gTestFolderCount = 4; const char* gTestFolders[] = { "chain80w","crane","diesel","CDU"}; const uint32_t gTestFoldersize[] = {101, 921, 600, 7201}; void CleanupBuffers(float64_t* G, float64_t* a, float64_t* C, float64_t* bv, float64_t* xl, float64_t* xu, float64_t* xv, float64_t* obj, uint32_t* iter, float64_t* x_gt, float64_t* obj_gt) { (void)FadasDeregBuf( xv ); (void)FadasDeregBuf( obj ); (void)FadasDeregBuf( iter ); (void)FadasDeregBuf( G ); (void)FadasDeregBuf( a ); (void)FadasDeregBuf( C ); (void)FadasDeregBuf( bv ); (void)FadasDeregBuf( xl ); (void)FadasDeregBuf( xu ); (void)FadasMemFree( x_gt ); (void)FadasMemFree( obj_gt ); (void)FadasMemFree( xv ); (void)FadasMemFree( obj ); (void)FadasMemFree( iter ); (void)FadasMemFree( G ); (void)FadasMemFree( a ); (void)FadasMemFree( C ); (void)FadasMemFree( bv ); (void)FadasMemFree( xl ); (void)FadasMemFree( xu ); } bool RunCQP( const char* test_id, const char* dims_data, const char* G_data, const char* av_data, const char* C_data, const char* bv_data, const char* xl_data, const char* xu_data, const char* x_gt_data, const char* obj_gt_data ) { FadasError_e status = FADAS_ERROR_NONE; bool ans = true; const uint32_t balign = 128; uint32_t n = 0, m = 0, m_e = 0; size_t memsz; float64_t varErrNorm = 0.0; float64_t xGtNorm = 0.0; float64_t maxErr = 0.0; float64_t objErr = 0.0; float64_t relObjErr = 0.0; using std_clock = std::chrono::steady_clock; uint32_t* dims = (uint32_t*)FadasMemAlloc( (size_t)3 * sizeof( uint32_t ), balign, nullptr ); if( nullptr == dims ) { UTIL_ERROR( "FadasMemAlloc failed for dims " ); } ans = UTIL_ReadUInt( dims_data, 3, dims ); // read problem size if( false == ans ) { UTIL_ERROR( "error reading file %s", dims_data ); } n = dims[0]; m = dims[1]; m_e = dims[2]; UTIL_INFO("n = %u, m = %u, m_e = %u\n", n, m, m_e); // read matrix G and register buffer memsz = ( n * n ) * sizeof( float64_t ); float64_t* G = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); // read vector a and register buffer memsz = n * sizeof( float64_t ); float64_t* a = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); // read matrix C and register buffer memsz = ( n * m ) * sizeof( float64_t ); float64_t* C = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); // read vector bv and register buffer memsz = m * sizeof( float64_t ); float64_t* bv = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); float64_t* xl; if( nullptr == xl_data ) { xl = nullptr; } else { // read vector xl and register buffer memsz = n * sizeof( float64_t ); xl = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); } for (uint32_t i = 0; i < m; ++i) //TODO:: Wrapper add a minus sign before calling the backend { bv[i] *= -1; } float64_t* xu; if( nullptr == xu_data ) { xu = nullptr; } else { // read vector xu and register buffer memsz = n * sizeof( float64_t ); xu = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); } // register buffer xv memsz = n * sizeof( float64_t ); float64_t* xv = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); // register buffer obj memsz = 1 * sizeof( float64_t ); float64_t* obj = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); // register buffer iter memsz = 1 * sizeof( uint32_t ); uint32_t* iter = (uint32_t*)FadasMemAlloc( memsz, balign, nullptr ); // cond_num memsz = 1 * sizeof( float64_t ); float64_t* cond_num = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); //GT x memsz = n * sizeof( float64_t ); float64_t* x_gt = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); //GT obj memsz = 1 * sizeof( float64_t ); float64_t* obj_gt = (float64_t*)FadasMemAlloc( memsz, balign, nullptr ); if( ( NULL == G ) || ( NULL == a ) || ( NULL == C ) || ( NULL == bv ) || ( NULL == xv ) || ( NULL == obj ) || ( NULL == iter ) || ( NULL == x_gt ) || ( NULL == obj_gt ) || (NULL == cond_num) ) { (void)fprintf( stderr, "Failed to allocate memory\n" ); // Try and free memory if it is already allocated CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( G_data, n * n, G ); if(false == ans) { UTIL_WARN( "error reading file %s", G_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = ( n * n ) * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)G, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for G [err: %u]", status ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( av_data, n, a ); if( false == ans ) { UTIL_WARN( "error reading file %s", av_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = n * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)a, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for a [err: %u]", status ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( C_data, n * m, C ); if( false == ans ) { UTIL_WARN( "error reading file %s", C_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = ( n * m ) * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)C, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for C [err: %u]", status ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( bv_data, m, bv ); if( false == ans ) { UTIL_WARN( "error reading file %s", bv_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = m * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)bv, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for bv [err: %u]", status ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } if( nullptr != xl_data) { ans = UTIL_ReadFloat64( xl_data, n, xl ); if( false == ans ) { UTIL_WARN( "error reading file %s", xl_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = n * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)xl, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for xl [err: %u]", status); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } } if( nullptr != xu_data ) { ans = UTIL_ReadFloat64( xu_data, n, xu ); if( false == ans ) { UTIL_WARN( "error reading file %s", xu_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = n * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_IN, (void*)xu, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for xu [err: %u]", status); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } } memsz = n * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_OUT, (void*)xv, size_t( memsz ) ); if (FADAS_ERROR_NONE != status) { UTIL_WARN( "Failed to register memory for xv [err: %u]", status); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = 1 * sizeof( float64_t ); status = FadasRegBuf( FADAS_BUF_TYPE_OUT, (void*)obj, size_t( memsz ) ); if (FADAS_ERROR_NONE != status) { UTIL_WARN( "Failed to register memory for obj [err: %u]", status); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = 1 * sizeof( uint32_t ); status = FadasRegBuf( FADAS_BUF_TYPE_OUT, (void*)iter, size_t( memsz ) ); if (FADAS_ERROR_NONE != status) { UTIL_WARN( "Failed to register memory for iter [err: %u]", status); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } memsz = 1 * sizeof( uint32_t ); status = FadasRegBuf( FADAS_BUF_TYPE_OUT, (void*)cond_num, size_t( memsz ) ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "Failed to register memory for iter [err: %u]", status ); CleanupBuffers( G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt ); (void)FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( x_gt_data, n, x_gt ); if( false == ans ) { UTIL_WARN( "error reading file %s", x_gt_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } ans = UTIL_ReadFloat64( obj_gt_data, 1, obj_gt ); if( false == ans ) { UTIL_WARN( "error reading file %s", obj_gt_data ); CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); return false; } //Both xl and xu should be avaialble or both are null bool boundPresent = ( nullptr != xl ) && ( nullptr != xu ); // Create internal buffers FadasCQPSolverResult_t qpResults{}; FadasHandle_t hCQP = nullptr; status = FadasCQP_Create( n, m, m_e, 0, nullptr, &hCQP, boundPresent ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "FadasCQP_Create failed! [err: %u]\n", status ); return false; } qpResults.optimization_result = xv; qpResults.obj_result = obj; qpResults.required_iterations = iter; qpResults.condition_number = *cond_num; qpResults.return_status = FADAS_CQP_INIT; auto t0 = std_clock::now(); status = FadasCQP_Run( hCQP, G, a, C, bv, xl, xu, 10000, 1e-15, 1e-30, &qpResults ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "FadasCQP_Run failed! [err: %u, qperr: %d]\n", status, qpResults.return_status ); return false; } auto t1 = std_clock::now(); long long dt = duration_cast(t1 - t0).count(); dtime = (double)dt; if( qpResults.return_status == FADAS_CQP_SUCCESS ) { float64_t err = 0.0; for( uint32_t i = 0; i < n; i++ ) { err = fabs( qpResults.optimization_result[i] - x_gt[i] ); if(maxErr < err) // compute L_inf norm { maxErr = err; } varErrNorm += (err * err); xGtNorm += (x_gt[i] * x_gt[i]); } varErrNorm = sqrt(varErrNorm)/( 1.0 + sqrt(xGtNorm)); if (maxErr > 1e-10) { ans = false; } objErr = fabs( *qpResults.obj_result - *obj_gt ); relObjErr = objErr / (1.0 + fabs(*obj_gt)); if( objErr > 1e-05 ) { ans = false; } UTIL_INFO("[%s]\nmaxErr = %E, varErrNorm = %E, objErr = %E, relObjErr = %E, solFound = 1, numIters = %u, max_violation = %E, dt = %.2f", test_id, maxErr, varErrNorm, objErr, relObjErr, *qpResults.required_iterations, qpResults.reached_tolerance, dtime); } else if( qpResults.return_status == FADAS_CQP_SOLUTION_INFEASIBLE ) { UTIL_WARN( "FadasCQP_Run Infeasable Solution!" ); UTIL_WARN("[%s]\nvarErr = 1.0, objErr = 1.0, solFound = 0, numIters = %u, dt = %.2f", test_id, *qpResults.required_iterations, dtime); } else if( qpResults.return_status == FADAS_CQP_HESSIAN_NOT_PD ) { UTIL_WARN( "FadasCQP_Run input hessian matrix is not positive definite!" ); } else { UTIL_WARN( "FadasCQP_Run unknown error!" ); } if( false == ans ) { UTIL_WARN( "FadasCQP_Run does not match GT!" ); } CleanupBuffers(G, a, C, bv, xl, xu, xv, obj, iter, x_gt, obj_gt); (void) FadasMemFree( dims ); status = FadasCQP_Destroy( hCQP ); if( FADAS_ERROR_NONE != status ) { UTIL_WARN( "FadasCQP_Destroy failed! [err: %u]\n", status ); ans = false; } return ans; } uint32_t LoadVecAndRun( const char *vec, uint32_t numProbs, uint32_t startFolder = 0 ) { uint32_t n_fails = 0; char pathDims[256] = { 0 }; char pathG[256] = { 0 }; char pathav[256] = { 0 }; char pathC[256] = { 0 }; char pathbv[256] = { 0 }; char pathxl[256] = { 0 }; char pathxu[256] = { 0 }; char pathx_gt[256] = { 0 }; char pathobj_gt[256] = { 0 }; /* FIXME n = numVariables; m = numConstraints; m_e = numEqualityConstraints; precision = precisionVal; */ for(uint32_t p = startFolder; p < numProbs; ++p) { char testId[64] = {0}; snprintf( testId, 64, "%s_%04u", vec, p); // (p + 1)); UPDATE_QPOASES_VEC_PATH(vec, p); bool ans = RunCQP( testId, pathDims, pathG, pathav, pathC, pathbv, pathxl, pathxu, pathx_gt, pathobj_gt ); if( false == ans ) { UTIL_WARN("%s_%04u: failed", vec, p); ++n_fails; } else { UTIL_INFO("%s_%04u: passed", vec, p); } } return n_fails; } int32_t CmdLineHasOption( int32_t argc, char* argv[], const char* optString ) { int32_t position = 0; for( int32_t i = 1; i < argc; ++i ) { if( !strcmp( argv[i], optString ) ) { position = i; break; } } return position; } uint32_t FindTestInTestFolders(char * word) { uint32_t size = gTestFolderCount; for (uint32_t i = 0; i < size; ++i) { if (strcmp(gTestFolders[i], word) == 0) { return i; } } return size; } int32_t main( int32_t argc, char** argv ) { int32_t retVal = 0; uint32_t startFolder = 0; uint32_t endFolder = 0; int32_t pos = 0; bool allTest = false; uint32_t posTest = 0; if( FADAS_ERROR_NONE != FadasInit( nullptr ) ) // initialize all FADAS features { UTIL_ERROR( "FadasInit failed\n" ); } if( (argc > 7) || CmdLineHasOption( argc, argv, "-h" ) ) { (void)fprintf( stderr, "USAGE: app [-h] [-a]|[-t] [-s] [-e]\n" " -h = Help information.\n" " -a = run for all test data\n" " -t = run for particular test data\n" " Vectors should be present at path : ../vectors/CQPS\n" " Available tests: chain80w, crane, diesel, CDU\n" " -s = Start folder number[0, N), default 0\n" " -e = End folder number[1, N), default max subfolders present\n" ); return -1; } //int n_fails_0 = LoadVecAndRun( "chain80w", 101 ); //int n_fails_1 = LoadVecAndRun( "crane", 921 ); //int n_fails_2 = LoadVecAndRun( "diesel", 600 ); //int n_fails_3 = LoadVecAndRun( "CDU", 7201 ); //printf( "STATS: fails_chain80w = %d / %d = %.2f%%\n", n_fails_0, 101, 100.0 * double( n_fails_0) / 101.0 ); //printf( "STATS: fails_crane = %d / %d = %.2f%%\n", n_fails_1, 921, 100.0 * double( n_fails_1 ) / 921.0 ); //printf( "STATS: fails_diesel = %d / %d = %.2f%%\n", n_fails_2, 600, 100.0 * double( n_fails_2 ) / 600.0 ); //printf( "STATS: fails_3 = %d / %d = %.2f%%\n", n_fails_3, 7201, 100.0 * double( n_fails_3 ) / 7201.0 ); pos = CmdLineHasOption( argc, argv, "-a" ); if ( pos ) { allTest = true; } else { pos = CmdLineHasOption( argc, argv, "-t" ); if ( pos && gTestFolderCount > FindTestInTestFolders(argv[pos+1]) ) { posTest = pos+1; } else { (void)fprintf(stderr, "Please provide correct test folder name!\n" "Available tests: chain80w, crane, diesel, CDU\n" ); return -1; } pos = CmdLineHasOption( argc, argv, "-s" ); if(pos && ((pos+1) < argc)) { (void)sscanf(argv[pos+1], "%u", &startFolder); } pos = CmdLineHasOption( argc, argv, "-e" ); if(pos && ((pos+1) < argc)) { (void)sscanf(argv[pos+1], "%u", &endFolder); } } UTIL_INFO( "cqp sample app is going to run with: \n" "all mode: %d\n" "particular test: %s\n" "start folder: %d\n" "end folder: %d\n\n", allTest, argv[posTest], startFolder, endFolder ); // re-arrange endFolder if not given uint32_t idx = FindTestInTestFolders(argv[posTest]); if(gTestFolderCount > idx) { if((0 == endFolder) || (gTestFoldersize[idx] < endFolder)) { endFolder = gTestFoldersize[idx]; } } if(0 == startFolder) { startFolder = 0; } if(allTest) { for(uint32_t i=0; i<4; ++i) { uint32_t n_fails = LoadVecAndRun( gTestFolders[i], gTestFoldersize[i] ); UTIL_INFO( "STATS: %s = %d / %d = %.2f%%\n", gTestFolders[i], n_fails, gTestFoldersize[i], 100.0 * double( n_fails) / gTestFoldersize[i] ); } } else { if(startFolder >= endFolder) { (void)fprintf(stderr, "Please provide proper start and end folder numbers!\n"); return -1; } uint32_t n_fails = LoadVecAndRun( argv[posTest] , endFolder, startFolder ); UTIL_INFO( "STATS: %s = %d / %d = %.2f%%\n", argv[posTest], n_fails, endFolder-startFolder, 100.0 * double( n_fails) / (endFolder-startFolder) ); } if(FADAS_ERROR_NONE != FadasDeInit()) { UTIL_ERROR( "FadasDeInit failed\n" ); } return retVal; } Copy to clipboard Last Published: Sep 30, 2024 [Previous Topic cqp\_basic/app.cpp](https://docs.qualcomm.com/bundle/publicresource/80-63309-1/topics/cqp-basic.md) [Next Topic uyvy/app.cpp](https://docs.qualcomm.com/bundle/publicresource/80-63309-1/topics/uyvy.md)