[Applied Algebra] C++ symbol calculation method for a large number of polynomials (compilation error problem when the symbol table is too large)

The method of calculating a large number of polynomials with c++ symbols (the compilation error problem when the symbol table is too large)


When using the C++ symbolic calculation library GiNaC for symbolic calculation, because there are too many polynomials to be processed, they are defined in the .cpp/.h file, so the g++ compilation always fails after running for a long time; we conceived a The method of reading text from the outside and then parsing it into corresponding symbols solves this problem;


Problem Description

When using the C++ symbolic computing library GiNaC for symbolic computing, because there are too many polynomials to be processed, they are defined in the .cpp/.h file:

std::vector<GiNaC::ex> ITEMS_ZTT {
    
    t1*z664, t2*z664,...}; // 这有超过2000个单项式;
std::vector<GiNaC::ex> EQUATIONS_420 {
    
    t39*z979 + t40*z979 + z749...,...}; // 这有超过2000个多项式;

When compiling at this time, an error will be reported due to insufficient memory (the symbol table is too large):

[hanss@Tenda cpp]$ g++ app_gauss.cpp -o app -lginac -lcln
g++: internal compiler error: Segmentation fault (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-7/README.Bugs> for instructions.

Solution

A direct thought is, since the operation of "writing the formula into it" defines too many symbols, can it not be defined at compile time, but can be defined at runtime?

How does C++ dynamically declare a variable with a string?

In fact, no; because the symbol table is generated at the beginning of the program ;
then the compromise idea is: I only create basic symbols, such as variables:

GiNaC::symbol x1("x1"), x2("x2"), x3("x3"), x4("x4"), x5("x5"), x6("x6"), x7("x7"), x8("x8"), x9("x9");

Then store the formula to be calculated in a static .dat file, and then only need to read it and parse it:

z1*c1 + z2*c2 + z3*c3 + z4*c4 + z5*c5 + z6*c6 + z7 + z8
z1*c2 + z3*c4 + z1 + z3
...

Therefore, the required functions include file reading/symbol parsing/formula generation, and the whole program is as follows:

#include <vector>
#include <string>
#include <iostream>
#include <cstring>
#include <fstream>
#include <ginac/ginac.h>
#include <map>
// g++ formula_resolution.cpp -o hello -lginac -lcln
#define MAX_LEN_LINE 200

using namespace std;

std::vector<string> read_formulas(const string& FILE_NAME)
{
    
    
    char THIS_LINE[MAX_LEN_LINE];
    string FORMULA_STRING;
    string TMP_VAR;
    std::ifstream FILEIN(FILE_NAME);
    std::vector<string> FORMULA_STRING_SET;
    for (;;)
    {
    
    
        FILEIN.getline( THIS_LINE, sizeof(THIS_LINE));
        if ( FILEIN.eof()){
    
    break;}
        std::vector<int> COEFF;
        for (int j = 0; j < MAX_LEN_LINE; ++j)
        {
    
    
            if ( THIS_LINE[j]=='\0'){
    
    break;}
            TMP_VAR = THIS_LINE[j];
            FORMULA_STRING += TMP_VAR;
        }
        FORMULA_STRING_SET.push_back(FORMULA_STRING);
        FORMULA_STRING.clear();
    }
    FILEIN.close();
    return FORMULA_STRING_SET;
}

vector<string> split(const string& PROCESS_STRING, const string& DELIM) {
    
      
    vector<string> res;  
    if("" == PROCESS_STRING) return res;  
    char * strs = new char[PROCESS_STRING.length() + 1] ;
    strcpy(strs, PROCESS_STRING.c_str());   
   
    char * d = new char[DELIM.length() + 1];  
    strcpy(d, DELIM.c_str());  
   
    char *p = strtok(strs, d);  
    while(p) {
    
      
      string s = p; 
      res.push_back(s);  
      p = strtok(NULL, d);  
    }  
   
    return res;  
}

GiNaC::ex make_poly_from_string(const string& PROCESS_STRING,std::map<string,GiNaC::symbol> MAP_STR_SYMBOL)
{
    
    
    std::vector<string> STR_MONOMIALS = split(PROCESS_STRING, " + ");
    GiNaC::ex POLYNOMIAL = 0;
    for (int INDEX_i = 0; INDEX_i < STR_MONOMIALS.size(); ++INDEX_i)
    {
    
    
        std::vector<string> STR_SYMBOLS = split(STR_MONOMIALS[INDEX_i], "*");
        if (STR_SYMBOLS.size()==1)
        {
    
    
            POLYNOMIAL+=MAP_STR_SYMBOL[STR_SYMBOLS[0] ];
            continue;
        }
        GiNaC::ex MONOMIAL = 1;
        for (int INDEX_j = 0; INDEX_j < STR_SYMBOLS.size(); ++INDEX_j)
  {
    
    
            MONOMIAL *= MAP_STR_SYMBOL[STR_SYMBOLS[INDEX_j] ];
        }
        POLYNOMIAL += MONOMIAL;
    }
    return POLYNOMIAL;
}

std::map<string,GiNaC::symbol> construct_map(const std::vector<GiNaC::symbol>& ITEMS_X)
{
    
    
    std::map<string,GiNaC::symbol> MAP_STR_SYMBOL;
    for (int INDEX_i = 0; INDEX_i < ITEMS_X.size(); ++INDEX_i)
    {
    
    
        MAP_STR_SYMBOL[ITEMS_X[INDEX_i].get_name()] = ITEMS_X[INDEX_i];
    }
    return MAP_STR_SYMBOL;
}

std::vector<GiNaC::ex> ppsh_read_formulas_from_file(const string& FILE_NAME, std::map<string,GiNaC::symbol> MAP_STR_SYMBOL)
{
    
    
    std::vector<GiNaC::ex> EQUATIONS_192;
    std::vector<string> FORMULA_STRING_SET = read_formulas("poly.dat");
    for (int INDEX_i = 0; INDEX_i < FORMULA_STRING_SET.size(); ++INDEX_i)
    {
    
    
        EQUATIONS_192.push_back(make_poly_from_string(FORMULA_STRING_SET[INDEX_i],MAP_STR_SYMBOL));
    }
    return EQUATIONS_192;
}

int main()
{
    
    
    GiNaC::symbol x1("x1"), x2("x2"), x3("x3"), x4("x4"), x5("x5"), x6("x6"), x7("x7"), x8("x8"), x9("x9");
    std::vector<GiNaC::symbol> ITEMS_X {
    
    x1, x2, x3, x4, x5, x6, x7, x8, x9};
    std::map<string,GiNaC::symbol> MAP_STR_SYMBOL = construct_map(ITEMS_X);
    std::vector<GiNaC::ex> EQUATIONS_192 = ppsh_read_formulas_from_file("poly.dat",MAP_STR_SYMBOL);
    for (int i = 0; i < EQUATIONS_192.size(); ++i)
    {
    
    
        cout << EQUATIONS_192[i] <<endl;
    }
}

The entire project can refer to the symbolic calculation program of polynomials on finite fields written by our group:

https://github.com/Luomin1993/PPSH-41

Another small question: how to compile the source code on a server without root privileges and quote

On a server without root privileges, you need to compile a library (CLN), a library GiNac that depends on CLN, and finally use g++ to compile your c++ program that depends on GiNac, all of which can be done as follows:

[hanss@Tenda cpp]$ ./configure --prefix=$HOME/usr // 编译CLN之前
[hanss@Tenda cpp]$ make // 编译CLN
[hanss@Tenda cpp]$ make install // 安装CLN
[hanss@Tenda cpp]$ export PKG_CONFIG_PATH=$HOME/usr/lib/pkgconfig // 指明CLN库
[hanss@Tenda cpp]$ ./configure --prefix=$HOME/usr // 编译GiNaC之前
[hanss@Tenda cpp]$ make // 编译GiNaC
[hanss@Tenda cpp]$ make install // 安装GiNaC
[hanss@Tenda cpp]$ g++ hello.cc -o hello -I$HOME/usr/include -L$HOME/usr/lib -lginac -lcln // 编译你的程序
[hanss@Tenda cpp]$ export LD_LIBRARY_PATH=$HOME/usr/lib // 运行程序之前
[hanss@Tenda cpp]$ ./hello // 运行程序

Guess you like

Origin blog.csdn.net/hanss2/article/details/120691511