<commit_before>/* Portions copyright (c) 2006 Stanford University and <NAME>.
 * Contributors: <NAME>, <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "CFSQPOptimizer.h"
#include <string>

// ----------------------------------------------------------------------
// Code to manage the library loading
// ----------------------------------------------------------------------

#ifndef _WIN32
#define SIMTK_PORTABLE_HMODULE void *
#define SIMTK_PORTABLE_HINSTANCE void *
#define WINAPI
#include <dlfcn.h>
#define GetProcAddress(handle, proc) dlsym(handle, proc)
#define LoadLibrary(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
const char *LoadLibraryErrorMessage() { return dlerror(); }
#else
#include <windows.h>
#define SIMTK_PORTABLE_HMODULE HMODULE
#define SIMTK_PORTABLE_HINSTANCE HINSTANCE
const char *LoadLibraryErrorMessage() { return 0; }
#endif

#ifndef _WIN32
static const char *CFSQP_LIBRARY_NAME = "libosimCFSQP.so";
#else
static const char *CFSQP_LIBRARY_NAME = "osimCFSQP.dll";
#endif

typedef void* (*CFSQP_FUNCTION)(int,int,int,int,int,int,int,int,int,int *,int,int,
                                int,int *,double,double,double,double,double *,
                                double *,double *,double *,double *,double *,
                                void (*)(int,int,double *,double *,void *),
                                void (*)(int,int,double *,double *,void *),
                                void (*)(int,int,double *,double *,
                                void (*)(int,int,double *,double *,void *),void *),
                                void (*)(int,int,double *,double *,
                                void (*)(int,int,double *,double *,void *),void *),
                                void *);
CFSQP_FUNCTION cfsqp_fptr = 0;

// ----------------------------------------------------------------------

#define USE_CONSTRAINT_CACHE

using namespace SimTK;

//=============================================================================
// CONSTRUCTION
//=============================================================================
//_____________________________________________________________________________
/**
 * Destructor.
 */
CFSQPOptimizer::
~CFSQPOptimizer()
{
    delete[] _mesh;
    delete[] _p;
    delete[] _c;
    delete[] _lambda;
}
//_____________________________________________________________________________
CFSQPOptimizer::CFSQPOptimizer(OptimizerSystem& sys)
    : OptimizerRep(sys)
{
    bindToCFSQPLibrary();

	_infinity = 1.0e10;
	_mode = 100;
	_epseqn = 1.0e-8;
	_inform = 0;
	_udelta = 0.0;

    int nx = sys.getNumParameters();

	// NEW MAX ITERATIONS
	maxIterations = 4 * nx;

	// SQP STUFF- NOT CURRENTLY SUPPORTED
	int i;
	_ncsrl = 0;
	_ncsrn = 0;
	_nfsr = 0;
	int lenmesh = _nfsr+_ncsrn+_ncsrl;  if(lenmesh<1) lenmesh=1;
	_mesh = new int[lenmesh];
	for(i=0;i<lenmesh;i++)  _mesh[i] = 0;

	// PERFORMANCE
	int lenp = 1 - _nfsr; // _target->getNumContacts() - _nfsr;
	for(int i=0;i<_nfsr;i++)  lenp += _mesh[i];
	if(lenp<1) lenp = 1;
    _p = new double[lenp];

	// CONSTRAINTS
	int lenc = sys.getNumConstraints() - _ncsrl - _ncsrn;
	for(int i=0;i<_ncsrn;i++)  lenc += _mesh[i+_nfsr];
	for(int i=0;i<_ncsrl;i++)  lenc += _mesh[i+_nfsr+_ncsrn];
	if(lenc<1) lenc = 1;
	_c = new double[lenc];

	// LAGRANGE MULTIPLIERS
	_lambda = new double[nx+lenp+lenc];
}

bool CFSQPOptimizer::isAvailable()
{
    try {
        bindToCFSQPLibrary();
    } catch (const SimTK::Exception::Base &ex) {
       std::cout << ex.getMessageText() << std::endl;
       return false;
    }
    return true;
}

void CFSQPOptimizer::bindToCFSQPLibrary()
{
    if(cfsqp_fptr) return;

    SIMTK_PORTABLE_HINSTANCE libraryHandle = LoadLibrary(CFSQP_LIBRARY_NAME);
    if(!libraryHandle) {
        const char *msg=LoadLibraryErrorMessage();
        if(msg)
            SimTK_THROW1(SimTK::Exception::Cant, std::string("Could not load CFSQP library '") + CFSQP_LIBRARY_NAME + "': " + msg); 
        else
            SimTK_THROW1(SimTK::Exception::Cant, std::string("Could not load CFSQP library '") + CFSQP_LIBRARY_NAME + "'"); 
    }

    cfsqp_fptr = (CFSQP_FUNCTION)GetProcAddress(libraryHandle, "cfsqp");
    if(!cfsqp_fptr) {
        SimTK_THROW1(SimTK::Exception::Cant, std::string("CFSQP library '") + CFSQP_LIBRARY_NAME + "' not valid (could not find 'cfsqp' symbol)"); 
    }

    std::cout << "Successfully linked to CFSQP library '" << CFSQP_LIBRARY_NAME << "'" << std::endl;
}

//=============================================================================
// COMPUTE OPTIMAL CONTROLS
//=============================================================================
//_____________________________________________________________________________
/**
 * Compute a set of optimal controls, given the input controls (xin) and
 * the current state of the optimization target.
 *
 * Whether or not the optimization terminates normally, the latest value of
 * the controls are copied to xout.  It is safe to use the same pointer
 * for xin and xout.  However, in all cases the calling routine must
 * allocate enough space for xin and xout.
 *
 * @param xin Values of the controls.
 * @param xout Optimal values of the controls.
 *
 * @return Parameter inform of cfsqp. -1 means a fatal error.
 */
Real CFSQPOptimizer::
optimize(Vector &results)
{
    const OptimizerSystem &sys = getOptimizerSystem();

    int nx = sys.getNumParameters();

	double *bl, *bu;
	if(sys.getHasLimits()) {
		sys.getParameterLimits(&bl,&bu);
	} else {
		bl = new double[nx];
		bu = new double[nx];
		for(int i=0; i<nx; i++) {
			bl[i] = -_infinity;
			bu[i] =  _infinity;
		}
	}

    double *x = &results[0];
    int numObjectiveFunctions = 1;

	// Clear cache before starting optimization (used to speed up constraint computations)
	clearCache();

	// OPTIMIZE
	cfsqp_fptr(getOptimizerSystem().getNumParameters(),numObjectiveFunctions,_nfsr,
        sys.getNumNonlinearInequalityConstraints(), sys.getNumInequalityConstraints(),
        sys.getNumNonlinearEqualityConstraints(), sys.getNumEqualityConstraints(),
		_ncsrl,_ncsrn,_mesh,
		_mode,diagnosticsLevel,maxIterations,&_inform,_infinity,convergenceTolerance,_epseqn,_udelta,
		bl,bu,x,_p,_c,_lambda,pFunc,cFunc,dpdxFunc,dcdxFunc,(void *)this);

	if(!getOptimizerSystem().getHasLimits()) {
		delete[] bl;
		delete[] bu;
	}

    if(diagnosticsLevel > 0) PrintInform(_inform,std::cout);
    
    if (_inform != 0 && _inform != 3 && _inform != 4 && _inform != 8) {
        char buf[1024];
        sprintf(buf, "CFSQP failed with status = %d",_inform);
        SimTK_THROW1(SimTK::Exception::OptimizerFailed, SimTK::String(buf));
    }

    return *_p;
}

//=============================================================================
// STATIC PERFORMANCE AND CONSTRAINT EVALUATIONS
//=============================================================================
//______________________________________________________________________________
/**
 * Compute the performance criterion.
 */
void CFSQPOptimizer::
pFunc(int nparam,int j,double *x,double *p,void *cd)
{
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
    int nx=cfsqp->getOptimizerSystem().getNumParameters();
    cfsqp->objectiveFunc(cfsqp->getOptimizerSystem(),Vector(nx,x,true),true,*p);
}
//______________________________________________________________________________
/**
 * Compute the derivatives of the performance criterion.
 */
void CFSQPOptimizer::
dpdxFunc(int nparam,int j,double *x,double *dpdx,
	void (*dummy)(int,int,double *,double *,void *),void *cd)
{
    // TODO; support numerical gradients
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
    int nx=cfsqp->getOptimizerSystem().getNumParameters();
    cfsqp->gradientFunc(cfsqp->getOptimizerSystem(),Vector(nx,x,true),true,Vector(nx,dpdx,true));
}

//______________________________________________________________________________
/**
 * Compute the constraints.
 */
void CFSQPOptimizer::
cFunc(int nparam,int j,double *x,double *c,void *cd)
{
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
	int nx=cfsqp->getOptimizerSystem().getNumParameters();
    // special wrapper to deal with caching
	cfsqp->computeConstraint(Vector(nx,x,true),true,*c,j-1);
}
//______________________________________________________________________________
/**
 * Compute the gradient of the constraints.
 */
void CFSQPOptimizer::
dcdxFunc(int nparam,int j,double *x,double *dcdx,
	void (*dummy)(int,int,double *,double *,void *),
	void *cd)
{
    // TODO: support numerical gradients
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
	int nx=cfsqp->getOptimizerSystem().getNumParameters();
	int nc=cfsqp->getOptimizerSystem().getNumConstraints();
    // special wrapper to deal with caching
	cfsqp->computeConstraintGradient(Vector(nx,x,true),true,Vector(nc,dcdx,true),j-1);
}

//=============================================================================
// PRINT
//=============================================================================
//______________________________________________________________________________
/**
 * Print the meaning of the value returned by computeOptimalControls().
 */
void CFSQPOptimizer::
PrintInform(int aInform,std::ostream &aOStream)
{
	switch(aInform) {
		case(0):
			aOStream<<"CFSQP(0): Normal termination.\n";
			break;
		case(1):
			aOStream<<"CFSQP(1): User-provided initial guess is infeasible ";
			aOStream<<"for linear constraints\n";
			aOStream<<"and CFSQP is unable to generate a point satisfying these ";
			aOStream<<"conditions.\n";
			break;
		case(2):
			aOStream<<"CFSQP(2): The user-provided initial guess is infeasible ";
			aOStream<<"for non-linear inequality constraints\n";
			aOStream<<"and linear constraints, and CFSQP is unable to generate ";
			aOStream<<"a point satisfying these constraints.\n";
			aOStream<<"This may be due to insucient accuracy of the QP solver.\n";
			break;<commit_msg>Fixed bug in my CFSQP wrapping.<commit_after>/* Portions copyright (c) 2006 Stanford University and <NAME>.
 * Contributors: <NAME>, <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "CFSQPOptimizer.h"
#include <string>

// ----------------------------------------------------------------------
// Code to manage the library loading
// ----------------------------------------------------------------------

#ifndef _WIN32
#define SIMTK_PORTABLE_HMODULE void *
#define SIMTK_PORTABLE_HINSTANCE void *
#define WINAPI
#include <dlfcn.h>
#define GetProcAddress(handle, proc) dlsym(handle, proc)
#define LoadLibrary(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
const char *LoadLibraryErrorMessage() { return dlerror(); }
#else
#include <windows.h>
#define SIMTK_PORTABLE_HMODULE HMODULE
#define SIMTK_PORTABLE_HINSTANCE HINSTANCE
const char *LoadLibraryErrorMessage() { return 0; }
#endif

#ifndef _WIN32
static const char *CFSQP_LIBRARY_NAME = "libosimCFSQP.so";
#else
static const char *CFSQP_LIBRARY_NAME = "osimCFSQP.dll";
#endif

typedef void* (*CFSQP_FUNCTION)(int,int,int,int,int,int,int,int,int,int *,int,int,
                                int,int *,double,double,double,double,double *,
                                double *,double *,double *,double *,double *,
                                void (*)(int,int,double *,double *,void *),
                                void (*)(int,int,double *,double *,void *),
                                void (*)(int,int,double *,double *,
                                void (*)(int,int,double *,double *,void *),void *),
                                void (*)(int,int,double *,double *,
                                void (*)(int,int,double *,double *,void *),void *),
                                void *);
CFSQP_FUNCTION cfsqp_fptr = 0;

// ----------------------------------------------------------------------

#define USE_CONSTRAINT_CACHE

using namespace SimTK;

//=============================================================================
// CONSTRUCTION
//=============================================================================
//_____________________________________________________________________________
/**
 * Destructor.
 */
CFSQPOptimizer::
~CFSQPOptimizer()
{
    delete[] _mesh;
    delete[] _p;
    delete[] _c;
    delete[] _lambda;
}
//_____________________________________________________________________________
CFSQPOptimizer::CFSQPOptimizer(OptimizerSystem& sys)
    : OptimizerRep(sys)
{
    bindToCFSQPLibrary();

	_infinity = 1.0e10;
	_mode = 100;
	_epseqn = 1.0e-8;
	_inform = 0;
	_udelta = 0.0;

    int nx = sys.getNumParameters();

	// NEW MAX ITERATIONS
	maxIterations = 4 * nx;

	// SQP STUFF- NOT CURRENTLY SUPPORTED
	int i;
	_ncsrl = 0;
	_ncsrn = 0;
	_nfsr = 0;
	int lenmesh = _nfsr+_ncsrn+_ncsrl;  if(lenmesh<1) lenmesh=1;
	_mesh = new int[lenmesh];
	for(i=0;i<lenmesh;i++)  _mesh[i] = 0;

	// PERFORMANCE
	int lenp = 1 - _nfsr; // _target->getNumContacts() - _nfsr;
	for(int i=0;i<_nfsr;i++)  lenp += _mesh[i];
	if(lenp<1) lenp = 1;
    _p = new double[lenp];

	// CONSTRAINTS
	int lenc = sys.getNumConstraints() - _ncsrl - _ncsrn;
	for(int i=0;i<_ncsrn;i++)  lenc += _mesh[i+_nfsr];
	for(int i=0;i<_ncsrl;i++)  lenc += _mesh[i+_nfsr+_ncsrn];
	if(lenc<1) lenc = 1;
	_c = new double[lenc];

	// LAGRANGE MULTIPLIERS
	_lambda = new double[nx+lenp+lenc];
}

bool CFSQPOptimizer::isAvailable()
{
    try {
        bindToCFSQPLibrary();
    } catch (const SimTK::Exception::Base &ex) {
       std::cout << ex.getMessageText() << std::endl;
       return false;
    }
    return true;
}

void CFSQPOptimizer::bindToCFSQPLibrary()
{
    if(cfsqp_fptr) return;

    SIMTK_PORTABLE_HINSTANCE libraryHandle = LoadLibrary(CFSQP_LIBRARY_NAME);
    if(!libraryHandle) {
        const char *msg=LoadLibraryErrorMessage();
        if(msg)
            SimTK_THROW1(SimTK::Exception::Cant, std::string("Could not load CFSQP library '") + CFSQP_LIBRARY_NAME + "': " + msg); 
        else
            SimTK_THROW1(SimTK::Exception::Cant, std::string("Could not load CFSQP library '") + CFSQP_LIBRARY_NAME + "'"); 
    }

    cfsqp_fptr = (CFSQP_FUNCTION)GetProcAddress(libraryHandle, "cfsqp");
    if(!cfsqp_fptr) {
        SimTK_THROW1(SimTK::Exception::Cant, std::string("CFSQP library '") + CFSQP_LIBRARY_NAME + "' not valid (could not find 'cfsqp' symbol)"); 
    }

    std::cout << "Successfully linked to CFSQP library '" << CFSQP_LIBRARY_NAME << "'" << std::endl;
}

//=============================================================================
// COMPUTE OPTIMAL CONTROLS
//=============================================================================
//_____________________________________________________________________________
/**
 * Compute a set of optimal controls, given the input controls (xin) and
 * the current state of the optimization target.
 *
 * Whether or not the optimization terminates normally, the latest value of
 * the controls are copied to xout.  It is safe to use the same pointer
 * for xin and xout.  However, in all cases the calling routine must
 * allocate enough space for xin and xout.
 *
 * @param xin Values of the controls.
 * @param xout Optimal values of the controls.
 *
 * @return Parameter inform of cfsqp. -1 means a fatal error.
 */
Real CFSQPOptimizer::
optimize(Vector &results)
{
    const OptimizerSystem &sys = getOptimizerSystem();

    int nx = sys.getNumParameters();

	double *bl, *bu;
	if(sys.getHasLimits()) {
		sys.getParameterLimits(&bl,&bu);
	} else {
		bl = new double[nx];
		bu = new double[nx];
		for(int i=0; i<nx; i++) {
			bl[i] = -_infinity;
			bu[i] =  _infinity;
		}
	}

    double *x = &results[0];
    int numObjectiveFunctions = 1;

	// Clear cache before starting optimization (used to speed up constraint computations)
	clearCache();

	// OPTIMIZE
	cfsqp_fptr(getOptimizerSystem().getNumParameters(),numObjectiveFunctions,_nfsr,
        sys.getNumNonlinearInequalityConstraints(), sys.getNumInequalityConstraints(),
        sys.getNumNonlinearEqualityConstraints(), sys.getNumEqualityConstraints(),
		_ncsrl,_ncsrn,_mesh,
		_mode,diagnosticsLevel,maxIterations,&_inform,_infinity,convergenceTolerance,_epseqn,_udelta,
		bl,bu,x,_p,_c,_lambda,pFunc,cFunc,dpdxFunc,dcdxFunc,(void *)this);

	if(!getOptimizerSystem().getHasLimits()) {
		delete[] bl;
		delete[] bu;
	}

    if(diagnosticsLevel > 0) PrintInform(_inform,std::cout);
    
    if (_inform != 0 && _inform != 3 && _inform != 4 && _inform != 8) {
        char buf[1024];
        sprintf(buf, "CFSQP failed with status = %d",_inform);
        SimTK_THROW1(SimTK::Exception::OptimizerFailed, SimTK::String(buf));
    }

    return *_p;
}

//=============================================================================
// STATIC PERFORMANCE AND CONSTRAINT EVALUATIONS
//=============================================================================
//______________________________________________________________________________
/**
 * Compute the performance criterion.
 */
void CFSQPOptimizer::
pFunc(int nparam,int j,double *x,double *p,void *cd)
{
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
    int nx=cfsqp->getOptimizerSystem().getNumParameters();
    cfsqp->objectiveFunc(cfsqp->getOptimizerSystem(),Vector(nx,x,true),true,*p);
}
//______________________________________________________________________________
/**
 * Compute the derivatives of the performance criterion.
 */
void CFSQPOptimizer::
dpdxFunc(int nparam,int j,double *x,double *dpdx,
	void (*dummy)(int,int,double *,double *,void *),void *cd)
{
    // TODO; support numerical gradients
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
    int nx=cfsqp->getOptimizerSystem().getNumParameters();
    cfsqp->gradientFunc(cfsqp->getOptimizerSystem(),Vector(nx,x,true),true,Vector(nx,dpdx,true));
}

//______________________________________________________________________________
/**
 * Compute the constraints.
 */
void CFSQPOptimizer::
cFunc(int nparam,int j,double *x,double *c,void *cd)
{
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
	int nx=cfsqp->getOptimizerSystem().getNumParameters();
    // special wrapper to deal with caching
	cfsqp->computeConstraint(Vector(nx,x,true),true,*c,j-1);
}
//______________________________________________________________________________
/**
 * Compute the gradient of the constraints.
 */
void CFSQPOptimizer::
dcdxFunc(int nparam,int j,double *x,double *dcdx,
	void (*dummy)(int,int,double *,double *,void *),
	void *cd)
{
    // TODO: support numerical gradients
    CFSQPOptimizer *cfsqp = (CFSQPOptimizer *)cd;
	int nx=cfsqp->getOptimizerSystem().getNumParameters();
	int nc=cfsqp->getOptimizerSystem().getNumConstraints();
    // special wrapper to deal with caching
	cfsqp->computeConstraintGradient(Vector(nx,x,true),true,Vector(nx,dcdx,true),j-1);
}

//=============================================================================
// PRINT
//=============================================================================
//______________________________________________________________________________
/**
 * Print the meaning of the value returned by computeOptimalControls().
 */
void CFSQPOptimizer::
PrintInform(int aInform,std::ostream &aOStream)
{
	switch(aInform) {
		case(0):
			aOStream<<"CFSQP(0): Normal termination.\n";
			break;
		case(1):
			aOStream<<"CFSQP(1): User-provided initial guess is infeasible ";
			aOStream<<"for linear constraints\n";
			aOStream<<"and CFSQP is unable to generate a point satisfying these ";
			aOStream<<"conditions.\n";
			break;
		case(2):
			aOStream<<"CFSQP(2): The user-provided initial guess is infeasible ";
			aOStream<<"for non-linear inequality constraints\n";
			aOStream<<"and linear constraints, and CFSQP is unable to generate ";
			aOStream<<"a point satisfying these constraints.\n";
			aOStream<<"This may be due to insucient accuracy of the QP solver.\n";
			break;
{'fraction_non_alphanumeric': 0.19010345143275656, 'fraction_numerical': 0.0058591961915224755, 'mean_word_length': 3.9416421624066955, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5368791', 'n_tokens_mistral': 7054, 'n_tokens_neox': 6518, 'n_words': 1656}