diff --git a/Core/Col_basis.cc b/Core/Col_basis.cc --- a/Core/Col_basis.cc +++ b/Core/Col_basis.cc @@ -1,1336 +1,1336 @@ // -*- C++ -*- /* * Col_basis.cc * Contains definition of the base class Col_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #include "Col_basis.h" namespace ColorFull { // Define the operator << for col_basis std::ostream& operator<<(std::ostream& out, const col_basis & cb) { int max = cb.size(); for (int i = 0; i < max; i++) { out <<", " << cb.at(i); } return out; } void Col_basis::scalar_product_matrix(){ if(n_g+n_q>6){ std::cout << "Col_basis::scalar_product_matrix: n_q+n_g0=" << n_q+n_g << " is large, consider using numerical and/or memory version. " << std::endl; std::cout.flush(); } return scalar_product_matrix( true, true, false ); } void Col_basis::scalar_product_matrix_mem(){ //return Col_basis::scalar_product_matrix( true, true, true ); return scalar_product_matrix( true, true, true ); } void Col_basis::scalar_product_matrix_num(){ return scalar_product_matrix( false, true, false ); } void Col_basis::scalar_product_matrix_mem_num(){ return scalar_product_matrix( false, true, true ); } void Col_basis::scalar_product_matrix_Polynomial(){ return scalar_product_matrix( true, false, false ); } const char* Col_basis::basis_file_name() const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // ColorFull ss << "CF_"; // Basis Type if( trace_basis) ss << "TB_"; else if ( tree_level_gluon_basis ) ss << "TGB_"; else if ( orthogonal_basis ) ss << "OB_"; else ss << "CB_"; //ss << "Basis_q"; ss << "q_"; ss << n_q; ss << "_g_"; ss << n_g; // If Nc is not 3, append Nc info - if( CF->Nc != 3 ){ + if( CF->N_c() != 3 ){ ss << "_Nc_"; - ss << CF->Nc; + ss << CF->N_c(); } // If TR is not 1/2, append TR info - if( CF->TR != 0.5 ){ + if( CF->T_R() != 0.5 ){ ss << "_TR_"; - ss << CF->TR; + ss << CF->T_R(); } //ss << ".dat"; filename=ss.str(); //return filename; return filename.c_str(); } std::string Col_basis::spm_file_name(const bool leading, const bool poly ) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // CF as in ColorFull ss << "CF_"; // Prefix according to basis type if( trace_basis ) ss << "TB_"; else if ( tree_level_gluon_basis ) ss << "TGB_"; else if ( orthogonal_basis ) ss << "OB_"; else ss << "CB_"; // Polynomial or numerical matrix? if( poly )ss << "P_"; else ss << "d_"; ss << "spm_q"; ss << n_q; ss << "_g"; ss << n_g; if ( leading ) ss << "_l"; - if ( CF->leading_cf ) ss << "_cfl"; - if(CF->Nc != 3 ){ + if ( CF-> is_leading_cf() ) ss << "_cfl"; + if(CF->N_c() != 3 ){ ss << "_Nc_"; - ss << CF->Nc; + ss << CF->N_c(); } - if(CF->TR != 0.5 ){ + if(CF->T_R() != 0.5 ){ ss << "_TR_"; - ss << CF->TR; + ss << CF->T_R(); } filename=ss.str(); return filename; } void Col_basis::write_out_basis() const{ write_out_basis( basis_file_name() ); } void Col_basis::write_out_basis(const char* filename) const { if ((cb.size() == 0)) { std::cout << "Col_basis::write_out_basis(string): There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); return; } std::ofstream outfile(filename); outfile << std::setprecision(16); for (uint m = 0; m < cb.size(); m++) { outfile << m << " " << cb.at(m) << std::endl; } outfile.flush(); } void Col_basis::write_out_basis_to_cout() const{ if( (cb.size()==0 ) ) { std::cout << "Col_basis::write_out_basis(): There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); return ; } for (uint m = 0; m < cb.size(); m++) { std::cout << m << " "<< cb.at(m) << std::endl; } } void Col_basis::write_out_dvec( const dvec & dv, const char* filename ) const { std::ofstream outfile(filename); outfile << dv; } void Col_basis::write_out_poly_vec( const poly_vec & pv, const char* filename ) const { std::ofstream outfile(filename); outfile << pv; } void Col_basis::write_out_spm(const dmatr & matr, const char* filename) const { std::ofstream outfile(filename); outfile << matr; } void Col_basis::write_out_spm(const poly_matr & matr, const char* filename) const { //std::cout << "write_out_spm(dmatr, string): Incoming matrix \n" << matr; std::ofstream outfile(filename); outfile << matr; } void Col_basis::write_out_spm( const dmatr & matr, const bool leading ) const{ //std::cout << "write_out_spm(dmatr, bool): Incoming matrix \n" << matr; // Creating file name (part) std::string filename = spm_file_name( leading, false ); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << matr; } void Col_basis::write_out_spm(const poly_matr & matr, const bool leading) const { //std::cout << "write_out_spm(dmatr, bool): Incoming matrix \n" << matr; // Creating file name std::string filename = spm_file_name(leading, true); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << matr; } void Col_basis::read_in_basis(const char* filename) { // Read in file std::ifstream fin(filename); // Check that file exists if( !fin ){ std::cerr << "Col_basis::read_in_basis: The file " << filename << " could not be opened." << std::endl; throw filename; } // Copy info from file to string std::string str((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()); Col_basis_of_str( str ); } void Col_basis::read_in_d_spm( const char* filename ){ d_spm=read_in_dmatr( filename ); } void Col_basis::read_in_d_spm( ){ d_spm=read_in_dmatr( spm_file_name( false, false ).c_str() ); } void Col_basis::read_in_leading_d_spm(const char* filename){ leading_d_spm=read_in_dmatr( filename ); } void Col_basis::read_in_leading_d_spm( ){ leading_d_spm=read_in_dmatr( spm_file_name( true, false ).c_str() ); } void Col_basis::read_in_P_spm(const char* filename){ P_spm=read_in_poly_matr( filename ); } void Col_basis::read_in_P_spm( ){ P_spm=read_in_poly_matr( spm_file_name( false, true).c_str() ); } void Col_basis::read_in_leading_P_spm(const char* filename){ leading_P_spm=read_in_poly_matr( filename ); } void Col_basis::read_in_leading_P_spm( ){ leading_P_spm=read_in_poly_matr( spm_file_name( true, true).c_str() ); } //poly_vec Col_basis::decompose( const Col_amp & Ca ) const { //poly_vec Col_basis::decompose( const Col_amp & Ca ){ poly_vec Col_basis::decompose( Col_amp & Ca ){ std::cerr << "Col_basis::decompose: This function is not implemented for the Col_basis class. Try using a derived class (such as Trace_basis). " << std::endl; poly_vec Pv; throw Pv; return Pv; } Col_amp Col_basis::exchange_gluon( uint vec, int p1, int p2 ) { if( (cb.size()==0 ) ) { std::cout << "Col_basis::exchange_gluon: There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); throw cb; } // Check that the basis vector exists if ( vec >= cb.size() ) { std::cerr << "Col_basis::exchange_gluon: Basis vector number "<< vec << " does not exist, as the basis only have " << cb.size() << " basis vectors." << std::endl; throw cb; } return CF->exchange_gluon( cb.at(vec), p1, p2); }; poly_matr Col_basis::Col_gamma( int p1, int p2 ) { if( (cb.size()==0 ) ) { std::cerr << "Col_basis::Col_gamma: There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cerr.flush(); throw cb; } // Function not available for Tree_level gluon matrix if( tree_level_gluon_basis ) { std::cerr << "Col_basis::Col_gamma: This function is not available for tree level gluon bases " << "as the vector resulting after gluon exchange contains vectors which are not in the basis. " << "Consider using Trace_basis."<< std::endl; std::cerr.flush(); throw cb; } // Function should only be used for Orthogonal_basis and Tree_level_gluon_basis if( ( !trace_basis ) and (! orthogonal_basis) ) { std::cerr << "Col_basis::Col_gamma: This function is only implemented for Trace_basis and bases that are orthogonal. " << "If your basis is not orthogonal, the result will not be correct. " << "If your basis is orthogonal, consider using Orthogonal_basis." << std::endl; std::cerr.flush(); } // To contain the resulting matrix poly_matr gamma_res; // Fill gamma_res with 0s Polynomial Zero; Zero=Zero*0; for( uint i=0; i < cb.size(); i++ ){ poly_vec rowi; for( uint j=0; j < cb.size(); j++ ){ rowi.push_back(Zero); } gamma_res.push_back(rowi); } // To contain the Col_amp after gluon exchange Col_amp Ca_ae; // First exchange a gluon between partons p1 and p2 in the vector v1 for ( uint vi=0; vi < cb.size(); vi++ ){ //std::cerr << "Col_basis::Col_gamma: Now looping over vector i."<< vi << std::endl; Ca_ae=exchange_gluon( vi, p1, p2 ); // Then decompose the result into the basis, poly_vec col_vi = decompose( Ca_ae ); // This gives the vi:th column in Col_gamma // Loop over rows in that column for ( uint vj=0; vj < cb.size(); vj++ ){ gamma_res.at(vj).at(vi) = col_vi.at(vj); } } return gamma_res; }; void Col_basis::Col_basis_of_str( std::string str ){ //std::cout << "Col_basis::Col_basis_of_str: entering function with string size: " << str.size()<< std::endl; if (str.size() == 0) { std::cerr << "Col_basis::Col_basis_of_str: The basis string is empty. This should not happen." << std::endl; throw str; } // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase( str.begin() ); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in string should be '0' if (str.at(0) != '0') { std::cerr << "Col_basis::Col_basis_of_str: First char in basis data file after comments should be '0', as in basis vector 0 as in vector number 0, but it was: " << str.at(0) << std::endl; throw -1; } //std::cout << "Col_basis::Col_basis_of_str: string at 0: " << str.at(0)<< std::endl; // Read the string, starting from 0th element unsigned int i = 0; while (i < str.size() - 2) { i += 1; // We may have some white spaces while (i< str.size()-2 &&(str.at(i) == ' ')) i++; // After white spaces there should be a number, the vector number while (i< str.size()-2 &&(str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9') ) i++; // We may have some more white spaces while (i< str.size()-2 &&(str.at(i) == ' ')) i++; // String to make a Col_amp of std::string Ca_str; Ca_str.clear(); // Keep reading the Ca_str while not '\n which marks new vector //while ( i< str.size()-1 && (str.at(i) != '\n') ) while ( i< str.size() && (str.at(i) != '\n') ) { Ca_str.push_back(str.at(i)); i++; } //std::cout << "Col_basis::Col_basis_of_str: The Ca_str is " << Ca_str << std::endl; // Make the Col_amp (basis vector) Col_amp Ca( Ca_str ); //std::cout <<Col_basis::Col_basis_of_str: Ca << std::endl; cb.push_back(Ca); } // Check that the basis is not empty if(cb.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The basis is empty. " << std::endl; throw cb; } // Check that first vector is not empty if(cb.at(0).ca.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Col_amp (vector) in the basis is empty. " << std::endl; throw cb; } // Check that first col_str in first vector is not empty if(cb.at(0).ca.at(0).cs.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Col_str in the first basis vector is empty. " << std::endl; throw cb; } // Check that first quark_line in the first col_str in first vector is not empty if(cb.at(0).ca.at(0).cs.at(0).ql.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Quark_line in the first Col_str in the first basis vector is empty. " << std::endl; throw cb; } // Find the number of quarks n_q = cb.at(0).ca.at(0).n_quark(); // Find the number of gluons n_g = cb.at(0).ca.at(0).n_gluon(); // Test if it's a trace basis if( trace_basis ){ //std::cout << "Col_basis::Col_basis_of_str: Checking if Trace_basis" << std::endl; bool is_trace_basis=true; for ( uint j=0; j< cb.size(); j++ ){ if( cb.at(j).size() !=1 ) is_trace_basis=false; if(! is_trace_basis ){ std::cerr << "Col_basis::Col_basis_of_str: You are trying to read in a non-trace basis to a trace basis object. " <<"The length of the Col_amp " << cb.at(j) << " is not 1." << std::endl; throw cb; } } } //else std::cout << "Col_basis::Col_basis_of_str: This is not a Trace_basis" << std::endl; // Test if trace basis, other types of bases cannot easily be tested for bool is_trace_basis=true; for ( uint j=0; j< cb.size(); j++ ){ if( cb.at(j).size() != 1 ) is_trace_basis=false; } if( is_trace_basis ) trace_basis=true; //if ( trace_basis ) std::cout << "Col_basis::Col_basis_of_str: Is trace basis " << std::endl; //else std::cout << "Col_basis::Col_basis_of_str: Is NOT trace basis " << std::endl; } void Col_basis::read_in_basis( ) { read_in_basis( basis_file_name() ); } poly_matr Col_basis::read_in_poly_matr( const char* filename ) { //std::cout << "Col_basis::read_in_poly_matr: Entering with file " << filename << std::endl; // Read in file std::ifstream fin(filename); // Check that file exists if( !fin ){ std::cerr << "Col_basis::read_in_poly_matr: The file " << filename << " could not be opened." << std::endl; throw filename; } // Copy info from file to string std::string str((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()); // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "Col_basis::read_in_poly_matr: First char in matrix data after comments file should be '{', it was: " << str.at(0) << std::endl; throw -1; } // Row to contain numbers poly_vec row; // To contain matrix of scalar products poly_matr matr; //std::cout << "Col_basis::read_in_poly_matr: starting to read string " << filename << std::endl; // Read the string, starting from 0th element unsigned int i = 0; while (i < str.size() - 2) { //i++; //Skip { //std::cout << "Col_basis::read_in_poly_matr: now at " // << i << " " << str.at(i) << std::endl; // To contain the Polynomial string std::string Poly_str; Poly_str.clear(); // We may have to skip some spaces, end-lines and { while (i < str.size() - 2 && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '{')) i++; // Read next Polynomial, until , or { // Keep reading the number while not ',' or '}' while (i < str.size() - 2 && (str.at(i) != ',' && str.at(i) != '}')) { Poly_str.push_back(str.at(i)); i++; } //std::cout << "Col_basis::read_in_poly_matr: Found Poly_str " << Poly_str << std::endl; Polynomial Poly( Poly_str ); row.push_back( Poly ); //std::cout << "Col_basis::read_in_poly_matr: Found polynomial " << Poly << std::endl; // If we have a new row if (i < str.size() - 2 && str.at(i) == '}') { // Save row in matrix, and empty row matr.push_back(row); row.clear(); //std::cout << "Col_basis::read_in_poly_matr: Intermediate matr : " << matr << std::endl; //std::cout << str.at(i); // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' ) ) i++; //std::cout << str.at(i); } i++; } //std::cout << "Col_basis::read_in_poly_matr: Found matrix " << matr << std::endl; return matr; } dmatr Col_basis::read_in_dmatr( const char* filename ) { // Read in file std::ifstream fin(filename); // Check that file exists if( !fin ){ std::cerr << "Col_basis::read_in_dmatr: The file " << filename << " could not be opened." << std::endl; throw filename; } // Copy info from file to string std::string str((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()); // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } //std::cout << "Col_basis::read_in_dmatr: erased comment, now sting is " << str << std::endl; } // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "Col_basis::read_in_dmatr: First char in matrix data file after comments should be '{', it was: " << str.at(0) << std::endl; throw -1; } // Check that only allowed signs uint j = 0; while (j < str.size()) { if (!(str.at(j) == '+' or str.at(j) == '-' or str.at(j) == '.' or str.at(j) == '{' or str.at(j) == '}' or str.at(j) == '\n' or str.at(j) == ',' or str.at(j) == ' ' or str.at(j) == '0' or str.at(j) == '1' or str.at(j) == '2' or str.at(j) == '3' or str.at(j) == '4' or str.at(j) == '5' or str.at(j) == '6' or str.at(j) == '7' or str.at(j) == '8' or str.at(j) == '9')) { std::cerr << "Col_basis::read_in_dmatr: A disallowed sign encountered in string for dmatr: " << str.at(j) << std::endl; std::cerr << "Col_basis::read_in_dmatr expects a numerical matrix." << std::endl; throw str; } j++; }; // Row to contain numbers dvec row; // To contain matrix of scalar products dmatr matr; // Read the string, starting from 0th element unsigned int i = 0; while (i < str.size() - 2) { i += 1; // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == ' ' or str.at(i) == '{') ) i++; // String to make a number of, and double to contain number std::string num_str; num_str.clear(); double num; // Keep reading the number while not ',' or '}' while ( i< str.size()-2 && (str.at(i) != ',' && str.at(i) != '}') ) { num_str.push_back(str.at(i)); i++; //std::cout << "Col_basis::read_in_dmatr at " << i << " " // << num_str << std::endl; } // now, at(i), there is either , or } //std::cout << str.at(i); // num_str contains the string to make a number of std::istringstream parton_str_st( num_str ); parton_str_st >> num; //std::cout << "Col_basis::read_in_dmatr num at " << i << " " // << num << std::endl; // Add number to vector row.push_back(num); // If we have a new row if( i< str.size()-2 && str.at(i)=='}'){ // Save row in matrix, and empty row matr.push_back(row); row.clear(); //std::cout << "Intermediate matr : " << matr << std::endl; //std::cout << str.at(i); // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' ) ) { i++; } } // Otherwise just keep on reading the next number in row }; //std::cout << "read_in_dmatr: Read from file: " << str << std::endl; //std::cout << "read_in_dmatr: Constructed: " << matr << std::endl; return matr; } void Col_basis::scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ) { if( (cb.size()==0 ) ) { std::cout << "Col_basis::scalar_product_matrix: There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); return ; } // If the P_spm and d_spm have already been calculated, erase them if( !P_spm.empty() ) P_spm.clear(); if( !d_spm.empty() ) d_spm.clear(); // Check that all Polynomials are real // (check doesn't cover the case of complex Poly in a Quark_line) // but if an entry is not real this will be discovered as the d_spm is calculated for ( uint cbi=0; cbi < cb.size(); cbi++){ for ( uint j=0; j< cb.at(cbi).size(); j++){ - if( imag (CF->c_num( cb.at(cbi).at(j).Poly )) > accuracy ){ + if( imag (CF->cnum_num( cb.at(cbi).at(j).Poly )) > accuracy ){ std::cerr << "Col_basis::scalar_product_matrix: ColorFull expects real Polynomial multiplying the color structure, but the Polynomial\n" << cb.at(cbi).at(j).Poly << std::endl << " appearing in front of the Col_str " << cb.at(cbi).at(j).cs << " in basis vector number " << cbi << " is not real."<< std::endl << std::endl; std::cerr.flush(); throw cb.at(cbi).at(j).Poly; } } } // For remembering already calculated topologies std::map<std::string, std::tr1::shared_ptr<Polynomial> > mem_map; // If Polynomial version is not needed convert all algebraic information // to numerical in the vectors // Loop over basis vectors in Basis for( uint i=0; i < cb.size(); i++){ //vector< Polynomial > rowi; //std::cout << "Col_basis::scalar_product_matrix: Now looping over i " << i << std::endl; // To contain a row of the resulting scalar product matrix poly_vec rowi; // Loop over basis vectors in Basis, normally over all vectors, // but for orthogonal_basis only for i==j uint minj=0, maxj=cb.size(); for( uint j=minj; j< maxj; j++){ Polynomial ijRes; ijRes=ijRes*0; if(use_mem){ // Loop over Col_strs in first Ca for( uint Ca1i=0; Ca1i< cb.at(i).size(); Ca1i++){ // Loop over Col_strs in second Ca for( uint Ca2i=0; Ca2i< cb.at(j).size(); Ca2i++){ // To contain the contribution to the ij-th entry if memoization is used std::tr1::shared_ptr<Polynomial> ijEntry_contr; // Rename indices, and make string of new col_strs, to use in map // strip off Polynomial information to make the map minimal Col_str Cs1_cs, Cs2_cs; Cs1_cs.append(cb.at(i).at(Ca1i).cs); Cs2_cs.append(cb.at(j).at(Ca2i).cs); std::pair<Col_str, Col_str> Css_new = rename_indices( Cs1_cs, Cs2_cs ); //std::pair<Col_str, Col_str> Css_new = rename_indices( cb.at(i).at(Ca1i),cb.at(j).at(Ca2i) ); Col_str Cs1 = Css_new.first; Col_str Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Calculate element contribution to the ijth element in scalar product matrix // If this has scalar product has occurred before, reuse old value if (mem_map.count( Cs_string.str() ) > 0) { ijEntry_contr = mem_map[Cs_string.str()]; //std::cout << "Reusing old res " << Cs_string.str()<< std::endl; } // Otherwise, calculate value and save topology else { if( !tree_level_gluon_basis ){ Polynomial p = CF->scalar_product(Cs1, Cs2); ijEntry_contr = shared_ptr<Polynomial> (new Polynomial(p)); mem_map[Cs_string.str()] = ijEntry_contr; //std::cout << " Col_basis::scalar_product_matrix: Found new mem_map entry general case " << Cs_string.str()<< std::endl; //std::cout << "Col_basis::scalar_product_matrix: The map now has seize " << mem_map.size(); } else if( tree_level_gluon_basis ){ int sign = (n_g % 2 ? -1 : 1); Polynomial p = 2* CF-> scalar_product( Cs1, Cs2 ) + sign*2*CF-> scalar_product( Cs1, CF-> conjugate(Cs2) ); //Polynomial p= ijEntry(i,j); No should not have Polys ijEntry_contr = shared_ptr<Polynomial> (new Polynomial(p)); //std::cout << "Found new mem_map entry, TGB case " << Cs_string.str()<< std::endl; mem_map[Cs_string.str()] = ijEntry_contr; } } // Sum up all the contributions to one Polynomial, recall to multiply with Polynomials Polynomial ijEntry_contr_poly=(*ijEntry_contr)* cb.at(i).at(Ca1i).Poly*cb.at(j).at(Ca2i).Poly; //Polynomial ijEntry_contr_poly=*ijEntry_contr; //ijRes= ijRes + ijEntry_contr_poly; //ijRes += &ijEntry; if( !save_P_spm ) { - double ijEntry_contr_d= CF-> d_num( ijEntry_contr_poly ); + double ijEntry_contr_d= CF-> double_num( ijEntry_contr_poly ); Monomial Mon( ijEntry_contr_d ); //ijRes=ijRes + Mon; ijRes+= Mon; } else ijRes += ijEntry_contr_poly; } // end looping over Cs in first Ca } // end looping over Cs in 2nd Ca // If Polynomial result is wanted, simplify if ( save_P_spm ) ijRes.simplify(); // Otherwise convert to numerical else { - double num_ijRes= CF-> d_num(ijRes); + double num_ijRes= CF-> double_num(ijRes); ijRes.clear(); Monomial Mon( num_ijRes ); ijRes.push_back(Mon); } rowi.push_back( ijRes ); /* std::cout << "Col_basis::scalar_product_matrix: Result using memoization for " << i << " j " << j << " " - << CF-> d_num(ijRes) << std::endl; + << CF-> double_num(ijRes) << std::endl; std::cout << "Col_basis::scalar_product_matrix: Result using calculation for " << i << " j " << j << " " - << CF-> d_num(ijEntry( i, j )) << std::endl; + << CF-> double_num(ijEntry( i, j )) << std::endl; - if( std::abs( CF-> d_num(ijRes)- (CF-> d_num(ijEntry( i, j ))) ) > accuracy ){ + if( std::abs( CF-> double_num(ijRes)- (CF-> double_num(ijEntry( i, j ))) ) > accuracy ){ std::cout << "Col_basis::scalar_product_matrix: RESULTS DISAGREE!! " - << CF-> d_num(ijRes)- (CF-> d_num(ijEntry( i, j ))) << std::endl; + << CF-> double_num(ijRes)- (CF-> double_num(ijEntry( i, j ))) << std::endl; } */ } // end if ( use_mem ) else{ //std::cout << "Col_basis::scalar_product_matrix: Now looping over i " << i << " j " << j <<std::endl; // Calculate element ij in scalar product matrix ijRes=ijEntry( i, j ); //ijEntry=CF->scalar_product(cb.at(i), cb.at(j)); //ijEntry=CF->scalar_product2(cb.at(i), cb.at(j)); ijRes.simplify(); rowi.push_back( ijRes ); } //end if (not mem) } P_spm.push_back(rowi); //std::cout << "Col_basis::scalar_product_matrix: mem_map size after row " << i << " " // << mem_map.size() << " " << std::endl; } // For saving and checking symmetry of numerical version - d_spm= CF->d_num(P_spm); + d_spm= CF->double_num(P_spm); // Leading terms version, // this can only be calculated if the Polynomial information was kept, // but if we don't intend to save Polynomial info, and if memoization // is used this info is disregarded. if( use_mem and (!save_P_spm) ){ leading_P_spm= CF->leading(P_spm); - leading_d_spm= CF->d_num( leading_P_spm ); + leading_d_spm= CF->double_num( leading_P_spm ); } // Making consistency checks check_spm(); // Writing out or erasing the double spm if( save_d_spm ) { std::cout << "Col_basis::scalar_product_matrix: Writing out numerical versions (standard and leading) of scalar product matrix." << std::endl; std::cout.flush(); write_out_spm( d_spm, false ); write_out_spm( leading_d_spm , true ); } else{ d_spm.clear(); } // Writing out or erasing the Polynomial spm if( save_P_spm ) { //std::cout << "Col_basis::scalar_product_matrix: About to write out numerical." << std::endl; //std::cout.flush(); std::cout << "Col_basis::scalar_product_matrix: Writing out Polynomial versions (standard and leading) of scalar product matrix." << std::endl; std::cout.flush(); write_out_spm( P_spm, false ); write_out_spm( leading_P_spm , true ); } else{ P_spm.clear(); } return; }; Polynomial Col_basis::ijEntry(const int i, const int j) const{ Polynomial ijEntry; ijEntry=CF->scalar_product(cb.at(i), cb.at(j)); ijEntry.simplify(); return ijEntry; } bool Col_basis::check_symmetry(const dmatr & matr) const { if( matr.size()==0 ){ std::cout << "Col_basis::check_symmetry( dmatr ): The numerical matrix is empty..." << std::endl; } else std::cout << "Col_basis::check_symmetry( dmatr ): Numerically verifying symmetry of matrix..."; // Verifying that the matrix is symmetric to accuracy "accuracy", // if not sym is put to false bool sym=true; // Loop over basis vectors in Basis for (uint i = 0; i < d_spm.size(); i++) { poly_vec rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { if ((abs(d_spm.at(i).at(j) / d_spm.at(j).at(i) - 1.0) > accuracy) && (d_spm.at(i).at(j) > accuracy) && (d_spm.at(j).at(i) > accuracy)) { sym=false; std::cerr << "Col_basis::check_symmetry( dmatr ): Error, the resulting scalar product matrix is not symmetric. \n " << "Element " << i << "," << j << ": " << d_spm.at(i).at(j) << ", Element " << j << "," << i << ": " << d_spm.at(j).at(i) << std::endl << "This indicates an error in calculation of scalar products. " << std::endl; } } } if( matr.size() !=0 and sym )std::cout << " done." << std::endl; return sym; } bool Col_basis::check_diagonal(const dmatr & matr) const{ if( matr.size()==0 ){ std::cout << "Col_basis::check_diagonal( dmatr ): The numerical matrix is empty..."; std::cout.flush(); } else{ std::cout << "Col_basis::check_diagonal( dmatr ): Numerically checking if the matrix is diagonal..."; std::cout.flush(); } // Verifying that the matrix is diagonal // if not sym is put to false bool diag=true; // Loop over basis vectors in Basis for (uint i = 0; i < matr.size(); i++) { //vector<Polynomial> rowi; poly_vec rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { // If non-diagonal elements are sufficiently large write warning if ( std::abs( matr.at(i).at(j) ) > accuracy and i!=j ) { diag=false; if( !(trace_basis or tree_level_gluon_basis) ){ std::cout << "Col_basis::check_diagonal( matr ): Warning, the matrix is not diagonal. \n " << "Element " << i << "," << j << ": " << matr.at(i).at(j) << std::endl; //<< "This could indicate an error in calculation of scalar products. " << std::endl; } } } } if (! diag ){ std::cout << " the matrix is not diagonal" << std::endl; } else if (diag) std::cout << " done." << std::endl; return diag; } void Col_basis::check_spm() const{ //std::cout << "Col_basis::check_spm(): Entering" << std::endl; // Verifying that the matrix is symmetric to accuracy bool sym=check_symmetry(d_spm); if( !sym ) throw -1; if( !sym ) { std::cerr <<"Col_basis::check_spm(): scalar product matrix not symmetric. Please report bug." << std::endl; std::cerr.flush(); throw -1; } //std::cout << "Col_basis::check_spm(): Numerically checking if leading version of the scalar product matrix is diagonal..." << std::endl; // Verifying that the leading terms only sit on the diagonal bool diagonal=check_diagonal(leading_d_spm); if(!diagonal and ( trace_basis or tree_level_gluon_basis ) ) { std::cout <<"Col_basis::check_spm(): Leading terms appear of the diagonal. This should not happen in a trace type basis." << " For numerical bases it can appear to happen as powers of Nc may hide in numerical constants." << std::endl; std::cout.flush(); } } std::pair<Col_str,Col_str> Col_basis::rename_indices(const Col_str & Cs1, const Col_str & Cs2) const{ //cout << "rename_indices: incoming Col_strs" << endl << Cs1 << Cs2; // To contain the new Cs1 Col_str Cs1_new(Cs1); // To contain the new Cs2 Col_str Cs2_new(Cs2); // Loop over Quark_lines in Cs1 int new_ind=0; // The new index uint Nql= Cs1.cs.size(); for(uint i=0; i< Nql; i++ ){ //cout << i << endl; //cout.flush(); uint Nind=Cs1.cs.at(i).ql.size(); // Loop over indices in the Quark_line for(uint j=0; j< Nind; j++ ){ new_ind++; // The old index in Cs1, to be replaced also in Cs2 int old_ind=Cs1.cs.at(i).ql.at(j); // Replace index in Cs1 Cs1_new.cs.at(i).ql.at(j)=new_ind; // Locate and replace index in Cs2 std::vector<int> place_in_2= Cs2.find_parton( old_ind ); Cs2_new.cs.at( place_in_2.at(0) ).ql.at( place_in_2.at(1) )=new_ind; } } //cout << "rename_indices: outgoing Col_strs" << endl << Cs1 << Cs2_new; return std::make_pair(Cs1_new,Cs2_new); } std::pair<Col_amp,Col_amp> Col_basis::rename_indices(const Col_amp & Ca1, const Col_amp & Ca2 ) { std::cout << "Col_basis::rename_indices: Incoming Col_strs" << std::endl << Ca1 << Ca2; // To contain the new Ca1 Col_amp Ca1_new(Ca1); // To contain the new Ca2 Col_amp Ca2_new(Ca2); // The Col_str to define the ordering Col_str Cs1=Ca1_new.ca.at(0); // Loop over Quark_lines in Cs1 int new_ind=0; // The new index for(uint i=0; i< Cs1.cs.size(); i++ ){ //cout << i << endl; //cout.flush(); // Loop over indices in the Quark_line for(uint j=0; j< Cs1.cs.at(i).ql.size(); j++ ){ new_ind++; // The old index in Cs1, to be replaced also in Cs2 int old_ind=Cs1.cs.at(i).ql.at(j); // Replace index in Cs1 Cs1.cs.at(i).ql.at(j)=new_ind; // Locate and replace index in all other Col_strs // Do the replacement in all Col_strs in Ca1 for(uint csi=0; csi < Ca1_new.ca.size(); csi++){ std::vector<int> place= Ca1_new.ca.at(csi).find_parton( old_ind ); Ca1_new.ca.at(csi).cs.at( place.at(0) ).ql.at( place.at(1) )=new_ind; } // Do the replacement in all Col_str in Ca2 for(uint csi=0; csi < Ca2.ca.size(); csi++){ std::vector<int> place= Ca2.ca.at(csi).find_parton( old_ind ); Ca2_new.ca.at(csi).cs.at( place.at(0) ).ql.at( place.at(1) )=new_ind; } } } //cout << "rename_indices: outgoing Col_strs" << endl << Cs1 << Cs2_new; return std::make_pair(Ca1_new,Ca2_new); } // Function for calculating scalar products // given the information about the basis and the scalar product matrix in the basis Polynomial Col_basis::scalar_product( Col_amp & Ca1, Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Col_basis::scalar_product: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; throw cb; } // Check that the Polynomial scalar product matrix is calculated, if not calculate it if( P_spm.size() != cb.size() ) { std::cerr << "Col_basis::scalar_product: This function uses the scalar product matrix which has not yet been calculated." << std::endl; throw P_spm; } // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps poly_vec Polyv1=CF->conjugate( decompose(Ca1) ); poly_vec Polyv2=decompose( Ca2 ); //std::cout << "scalar_product(Ca1,Ca2,Basis,sp_matr): Decomposition done."<< std::endl; // Then add contributions for (uint m1=0; m1< cb.size(); m1++){ // Diagonal terms Poly_res=Poly_res+Polyv1.at(m1) *Polyv2.at(m1) *P_spm.at(m1).at(m1); for (uint m2=0; m2< m1; m2++){ // Other terms, use symmetry of scalar product matrix Poly_res=Poly_res+ ( Polyv1.at(m1) *Polyv2.at(m2)+Polyv1.at(m2) *Polyv2.at(m1) ) *P_spm.at(m1).at(m2); } } return Poly_res; } // Function for calculating scalar products numerically // given the information about the basis and the scalar product matrix in the basis cnum Col_basis::scalar_product_num( Col_amp & Ca1, Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ - std::cerr << "Col_basis::scalar_product_c_num: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; + std::cerr << "Col_basis::scalar_product_cnum_num: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; throw cb; } // Check that the Polynomial scalar product matrix is calculated, if not calculate it if( d_spm.size() != cb.size() ) { - std::cerr << "Col_basis::scalar_product_c_num: This function uses the numerical scalar product matrix which has not yet been calculated." << std::endl; + std::cerr << "Col_basis::scalar_product_cnum_num: This function uses the numerical scalar product matrix which has not yet been calculated." << std::endl; throw d_spm; } // To contain the resulting Polynomial cnum res=0; uint basis_size=cb.size(); //time_t t1, t2, t3; //t1=clock( ); // Decompose the Col_amps poly_vec Polyv1, Polyv2; Polyv1=decompose(Ca1); if( &Ca1==&Ca2 ) Polyv2=Polyv1; else Polyv2=decompose(Ca2); - //cout << "scalar_product_c_num(Ca1, Ca2, Basis, sp_matr)" << "\n Polyv1: " << Polyv1 << endl; + //cout << "scalar_product_cnum_num(Ca1, Ca2, Basis, sp_matr)" << "\n Polyv1: " << Polyv1 << endl; // Conjugate first arg, added 130525 Polyv1=CF->conjugate(Polyv1); // Make ordinary vectors to speed up multiplication cvec v1, v2; v1.reserve( basis_size ); for (uint i=0; i< basis_size; i++){ - v1.push_back( CF->c_num( Polyv1.at(i) ) ); + v1.push_back( CF->cnum_num( Polyv1.at(i) ) ); }; //if( &Ca1==&Ca2 ) v2=v1; if (false) v2=v1; else{ v2.reserve(basis_size); - for (uint i=0; i< basis_size; i++) v2.push_back( CF->c_num( Polyv2.at(i) ) ); + for (uint i=0; i< basis_size; i++) v2.push_back( CF->cnum_num( Polyv2.at(i) ) ); } - //cout << "scalar_product_c_num(Ca1, Ca2, Basis, sp_matr) " << "\n v1: " << v1 << endl; + //cout << "scalar_product_cnum_num(Ca1, Ca2, Basis, sp_matr) " << "\n v1: " << v1 << endl; //t2=clock(); - //cout << "scalar_product_c_num(Ca1,Ca2,Basis, cmatr): Decomposition done at time: " << t2-t1 << endl; + //cout << "scalar_product_cnum_num(Ca1,Ca2,Basis, cmatr): Decomposition done at time: " << t2-t1 << endl; // Then add contributions cnum v1m1, v2m1; dvec row; for (uint m1=0; m1< basis_size; m1++){ v1m1=v1.at(m1); v2m1=v2.at(m1); row=d_spm.at( m1 ); // Diagonal terms res=res+ v1m1 *v2.at(m1) *row.at(m1); for (uint m2=0; m2< m1; m2++){ //res=res+ v1.at(m1) *v2.at(m2) *sp_matr.at(m1).at(m2); //res=res+ 2.0*ind1*v2.at(m2) *row.at(m2);// how could this be true res += (v1m1*v2.at(m2) +v1.at(m2)*v2m1 ) *row.at(m2); // changed to this 130524 } } //t3=clock(); - //cout << "scalar_product_c_num(Ca1,Ca2,Basis, cmatr): Matrix multiplication done at time: " << t3-t2 << endl; + //cout << "scalar_product_cnum_num(Ca1,Ca2,Basis, cmatr): Matrix multiplication done at time: " << t3-t2 << endl; return res; } // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Warning: ordering of basis tensors must be the same cnum Col_basis::scalar_product_num_fast( const cvec & v1, const cvec & v2) { if(v1.size()!= v2.size()){ std::cerr << "Col_basis::scalar_product_num_fast: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; throw v2; } if(v1.size()!= d_spm.size()){ std::cerr << "Col_basis::scalar_product_num_fast: Size of vectors " << v1.size() << " does not agree with size of d_spm matrix " << d_spm.size() << std::endl; throw d_spm; } uint basis_size=v1.size(); // To contain the result cnum res=0; cnum tmp=0, tmp2=0; //time_t t1, t2; ///t1=clock( ); // Add contributions cnum v1m1, v2m1; for (uint m1=0; m1< basis_size; m1++){ v1m1= conj( v1.at(m1) ); // Conjugation added 130529 v2m1=v2.at(m1); const dvec &row = d_spm.at(m1); // Diagonal parts res += v1m1 *v2.at(m1) *row.at(m1); tmp = 0; tmp2=0; for (uint m2=0; m2< m1; m2++){ //res=res+ v1.at(m1) *v2.at(m2) *sp_matr.at(m1).at(m2); //res=res+ 2.0*ind1 *v2.at(m2) *row.at(m2); //tmp += v2[m2] *row[m2]; // 40% quicker tmp += (v1m1*v2[m2]+v2m1*conj(v1[m2])) *row[m2]; //alt //tmp+=v2[m2]*row[m2]; //tmp2+=v1[m2]*row[m2]; } //res += 2.0*v1m1*tmp; res+=tmp; // alt //res+= v1m1*tmp+v2m1*tmp2; } //t2=clock(); //cout << "scalar_product_num_fast(v1, v2, spm_matr): scalar prod calculated at time " // << t2-t1 << endl; return res; } // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Warning: ordering of basis tensors must be the same // Function for calculating scalar products, assuming that only diagonal parts contribute // This is useful for calculating the leading Nc result // This function assumes that the Col_amps are normal ordered and that the full polynomial is // contained in the numerical factor // It also assumes that the BASIS in which sp_matr is calculated is the SAME as that of the Col_amp // and that the Col_amp is a linear combination of ALL states // The coefficients of the basis vectors can then simply be read off in the Polynomials multiplying the Col_strs // This is an attempt to speed up // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Assumes that there are only diagonal contributions. This is useful for calculations in leading Nc limit // Warning: ordering of basis tensors must be the same cnum Col_basis::scalar_product_num_diagonal( const cvec & v1, const cvec & v2 ) { if(v1.size()!= v2.size()){ std::cerr << "Col_basis::scalar_product_num_diagonal: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; throw v2; } if(v1.size()!= d_spm.size()){ std::cerr << "Col_basis::scalar_product_num_diagonal: Size of vectors " << v1.size() << " do not agree with size of d_spm matrix " << d_spm.size() << std::endl; throw d_spm; } // To contain the result cnum res=0; // Check dimension uint basis_size=v1.size(); //time_t t1, t2; //t1=clock( ); // Then add contributions for (uint m1=0; m1< basis_size; m1++){ // added conjugation of first arg 130524 res=res+ conj( v1.at(m1) ) *v2.at(m1) *d_spm.at(m1).at(m1); } //t2=clock(); //cout << "scalar_product_num_diagonal(v1, v2, spm_matr): scalar prod calculated at time " // << t2-t1 << endl; return res; } } diff --git a/Core/Col_functions.cc b/Core/Col_functions.cc --- a/Core/Col_functions.cc +++ b/Core/Col_functions.cc @@ -1,5773 +1,5774 @@ /* * Col_functions.cc * Contains definitions of functions and operators used for treating the color structure * Created on: Jul 8, 2010 * Author: malin */ #include "Col_functions.h" namespace ColorFull { //using namespace std; using std::tr1::shared_ptr; /////////////////////// DEFINING OPERATORS ///////////////////// // Defining + operator to be able to erase elements at place std::list<int>::iterator operator+(std::list<int>::iterator x, int n) { while(n>0){ x++; n--; } while(n<0){ x--; n++; } return x; } std::list<int>::iterator operator-(std::list<int>::iterator x, int n) { while(n>0){ x--; n--; } while(n<0){ x++; n++; } return x; } // Defining + operator to be able to erase elements at place std::list< Quark_line >::iterator operator+( std::list < Quark_line >::iterator x, int n){ while(n>0){ x++; n--; } while(n<0){ x--; n++; } return x; } col_str::iterator operator-( col_str::iterator x, int n){ while(n>0){ x--; n--; } while(n<0){ x++; n++; } return x; } // Define the operator << for vector of int std::ostream& operator<<(std::ostream& out, std::vector<int> vec){ int max=vec.size(); if(max==0) out <<"{}"; else{ out <<"{"; for (int i=0; i<max-1; i++){ out << vec.at(i) << ","; } out << vec.at(max-1) <<"}"; } return out; } // Define the operator << for cvec std::ostream& operator<<( std::ostream& out, const cvec & cv ) { out << "{"; // Loop over entries for (uint i = 0; i < cv.size(); i++) { // Print element std::cout.width(6); std::ostringstream outstr; outstr << cv.at(i); out << outstr.str(); // If not last element print "," if (i < (cv.size() -1 )) out << ", "; } out << "}"; return out; } // Define the operator << for dvec std::ostream& operator<<(std::ostream& out, const dvec & dv) { out << "{"; // Loop over entries for (uint i = 0; i < dv.size(); i++) { // Print element std::cout.width(6); std::ostringstream outstr; outstr << dv.at(i); out << outstr.str(); // If not last element print "," if (i < (dv.size() -1 )) out << ", "; } out << "}"; return out; } // Define the operator << for cmatr std::ostream& operator<<(std::ostream& out, const cmatr & cm){ out <<"{" << std::endl; // Loop over rows for(uint i=0; i< cm.size(); i++ ){ out <<"{"; // Loop over columns for(uint j=0; j< cm.at(i).size(); j++ ){ // Print element std::cout.width( 6 ); std::ostringstream outstr; outstr << cm.at(i).at(j); // If not last element print "," if (j<cm.at(i).size()-1 ) outstr << ","; out << outstr.str(); //out << Poly_m.at(i).at(j) << ""; } out <<"}"; // If not last row, print "," if (i<cm.at(i).size()-1 ) out << ","; out << std::endl; } out <<"}" << std::endl; return out; } // Define the operator << for dmatr std::ostream& operator<<(std::ostream& out, const dmatr & matr){ //std::cout.precision(10); out <<"{" << std::endl; // Loop over rows for(uint i=0; i< matr.size(); i++ ){ out <<"{"; // Loop over columns for(uint j=0; j< matr.at(i).size(); j++ ){ // Print element std::cout.width( 16 ); std::ostringstream outstr; outstr.width( 20 ); outstr.precision(16); // If the result is larger than accuracy print it out if( std::abs( matr.at(i).at(j) )> accuracy ){ outstr << std::fixed << matr.at(i).at(j); } // otherwise is should probably be 0 else outstr << std::fixed << 0; // If not last element print "," if (j<matr.at(i).size()-1 ) outstr << ","; out << outstr.str(); //out << Poly_m.at(i).at(j) << ""; } out <<"}"; // If not last row, print "," if (i<matr.at(i).size()-1 ) out << ","; out << std::endl; } out <<"}" <<std::endl; return out; } // Define the operator << for dvec std::ostream& operator<<(std::ostream& out, std::pair<int, int> pair) { out << "("; out << pair.first; out << ", "; out << pair.second; out << ")"; return out; } /////////////////////// DEFINING FUNCKTIONS //////////////////// // Function returning the conjugate of a Monomial Monomial Col_functions::conjugate( const Monomial Mon ) const{ // Monomial to return Monomial out_Monomial=Mon; out_Monomial.conjugate(); return out_Monomial; } // Function returning the conjugate of a Polynomial Polynomial Col_functions::conjugate( const Polynomial Poly ) const{ // Polynomial to return Polynomial out_Polynomial=Poly; out_Polynomial.conjugate(); return out_Polynomial; } // Function returning the conjugate of a poly_vec poly_vec Col_functions::conjugate( const poly_vec Pv ) const{ // poly_vec to return poly_vec out_Pv=Pv; for ( uint i=0; i < Pv.size(); i++ ){ out_Pv.at(i).conjugate(); } return out_Pv; } // Function returning the conjugate of a poly_matr poly_matr Col_functions::conjugate( const poly_matr Pm ) const{ // poly_vec to return poly_matr out_Pm=Pm; for ( uint i=0; i < Pm.size(); i++ ){ out_Pm.at(i)= conjugate(out_Pm.at(i)); } return out_Pm; } // Function returning the conjugate of a Quark_line Quark_line Col_functions::conjugate(const Quark_line Ql) const{ //Quark_line to return Quark_line out_Quark_line; // Copying info if the Quark_line is closed or open if( Ql.open ) out_Quark_line.open=true; else out_Quark_line.open=false; // Copying info of (conjugated) Polynomial // fixed conjugation bug, ~ 12 08 Polynomial new_Poly=conjugate( Ql.Poly ); out_Quark_line.Poly =new_Poly; // Take conjugate by reversing order of all partons in the Quark_line for (int i=Ql.ql.size()-1; i>=0;){ out_Quark_line.ql.push_back( Ql.ql.at(i) ); i--; } return out_Quark_line; } // Function for taking the conjugate of a Col_str Col_str Col_functions::conjugate( const Col_str Cs ) const{ // Col_str to return Col_str out_Col_str; // Copying info of conjugated Polynomial Polynomial new_Poly=conjugate( Cs.Poly ); out_Col_str.Poly =new_Poly; //Cs.Poly.conjugate(); //out_Col_str.Poly = Cs.Poly; // Take conjugate by reversing order of all partons in each Quark_line // Loop over Quark_lines for (uint i=0; i < Cs.cs.size(); i++ ){ // Append conjugate of i:th Quark_line at i:th place in out_Col_str out_Col_str.cs.push_back( conjugate(Cs.cs.at(i)) ); } return out_Col_str; } // Function for taking the conjugate of a Col_amp Col_amp Col_functions::conjugate( const Col_amp Ca ) const{ // Col_str to return Col_amp out_Col_amp; // Copying info of conjugated Polynomial out_Col_amp.Scalar = Ca.Scalar; out_Col_amp.Scalar.conjugate(); // Take conjugate by reversing order of all partons in each Quark_line // Loop over Col_strs for (uint i=0; i < Ca.ca.size(); i++ ){ // Append conjugate of i:th Quark_line at i:th place in out_Col_str out_Col_amp.ca.push_back( conjugate(Ca.ca.at(i)) ); } return out_Col_amp; } // Function for emitting a gluon from a Col_str Col_amp Col_functions::emit_gluon(const Col_str & in_Col_str, int emitter, int g_new) const{ // Locate the emitter in the Col_str std::vector <int> place=in_Col_str.find_parton(emitter); // Find what kind, q qbar or g, the emitter is std::string kind=in_Col_str.find_kind(emitter); // Defining Col_str to return (two needed in case of g) Col_str out_Col_str1=in_Col_str; Col_str out_Col_str2=in_Col_str; // Defining Col_amp to return Col_amp out_Col_amp; // If the emitter is a quark if( kind =="q" ){ // Add new parton index at second first place in relevant quark_line out_Col_str1.insert2( place.at(0), place.at(1)+1, g_new ); out_Col_amp.ca.push_back( out_Col_str1 ); } // If the emitter is an anti-quark else if( kind =="qbar" ){ // Add new parton after the qbar out_Col_str1.insert2( place.at(0), place.at(1), g_new ); // Change sign // TODO check change of sign out_Col_str1.Poly=out_Col_str1.Poly*(-1); // Making a Col_amp to return out_Col_amp.ca.push_back( out_Col_str1 ); } // If the emitter is a g else if( kind =="g" ){ // If the emitter is a gluon a new gluon line should be inserted in two // different places, before and after the emitter // TODO check sign, currently -sign when inserting the gluon before // Change sign Poly for first term Monomial Mon_tmp; Mon_tmp.n_times=-1; out_Col_str1.Poly=out_Col_str1.Poly*Mon_tmp; // Inserting the gluon before out_Col_str1.insert2( place.at(0), place.at(1), g_new ); // Inserting the gluon after out_Col_str2.insert2( place.at(0), place.at(1)+1, g_new ); // Appending result to out_Col_amp out_Col_amp.ca.push_back( out_Col_str1 ); out_Col_amp.ca.push_back( out_Col_str2 ); // Normal order out_Col_amp.normal_order(); }; out_Col_amp.simplify(); return out_Col_amp; } // Function for emitting a gluon from a Col_amp Col_amp Col_functions::emit_gluon(const Col_amp & Ca_in, int emitter, int g_new) const{ Col_amp Ca_out; // Special case of gluons only, and only 2 gluons // As the anti-cyclic order is implicit, the third gluon should // only be added in one place. // For >2 gluons this will be automatic as the starting state contains // only one of the orders, for example (1,2,3) but not (1,3,2) // If there is only one Col_str, and that Cs has only one Ql, // and that Ql has only 2 partons and there are only gluons if(Ca_in.ca.size()==1 && Ca_in.ca.at(0).cs.size()==1 && Ca_in.ca.at(0).cs.at(0).ql.size()==2 && Ca_in.gluons_only()){ // Ca_in.ca.size()==1 && Ca_in.gluons_only()Ca_in.ca.at(0).cs.size()==1 && Ca_in.ca.at(0).cs.at(0).ql.size()==2 && // The new Col_amp has only one Col_str, which is typically (1,2,3) // This should give the old Col_str Ca_out=Ca_in; // Then the third gluon should be appended to the only ql in the only cs Ca_out.ca.at(0).cs.at(0).append(g_new); //cout << "emit_gluon(Ca..): Special case of 2 gluons, to return " << Ca_out.ca.at(0).cs.at(0); // TODO check sign } else{ // Emit from each Col_str, and append to new Col_amp for( uint m=0; m< Ca_in.ca.size(); m++ ){ // Emit from the Col_str Col_amp part_m=emit_gluon(Ca_in.ca.at(m), emitter, g_new); // Append result Ca_out.append(part_m.ca); } } //cout << "emit_gluon(Ca..): after emission" << endl << Ca_out << endl; return Ca_out; } // Function for splitting an open Quark_line into two Quark_lines // j is the locations of the gluons to be removed in the split Col_str Col_functions::split_after( Quark_line& Ql, int j ) const{ if(!Ql.open){ std::cerr <<"split_after: expects an open quark_line" << std::endl; std::cerr.flush(); throw -1; } if( j==0 or j== static_cast<int>(Ql.ql.size()-1) ){ std::cerr <<"split_after: the argument must be a gluon place, was " << j << std::endl; std::cerr.flush(); throw -1; } Quark_line Ql1=Ql; Quark_line Ql2=Ql; // In first part erase everything after j quark_line::iterator it1=Ql1.ql.begin()+j+1; quark_line::iterator it2=Ql1.ql.end(); Ql1.ql.erase(it1, it2); // In second part, erase everyting before j it1=Ql2.ql.begin(); it2=Ql2.ql.begin()+j+1; Ql2.ql.erase( it1, it2 ); Col_str Cs; // Construct Col_str to return Cs.cs.push_back(Ql1); Cs.cs.push_back(Ql2); return Cs; } // Returns a Quark_line with the the elements before j in a Quark_line Quark_line Col_functions::before( Quark_line Ql, int j ) const{ //cout << "before( Quark_line Ql, int j ): the size was " << static_cast<int>(Ql.ql.size()); //cout << "for the Quark_line " << Ql << endl; if( j<0 or j> static_cast<int>(Ql.ql.size()-1) ){ std::cerr << "before( Quark_line Ql, int j ): the size was " << static_cast<int>(Ql.ql.size()); std::cerr << "for the Quark_line " << Ql << std::endl; std::cerr.flush(); std::cerr <<"before: the argument must be >=0 and inside vector, was " << j << std::endl; std::cerr.flush(); throw -1; } // In first part erase everything after and includingj quark_line::iterator it1=Ql.ql.begin()+j; quark_line::iterator it2=Ql.ql.end(); Ql.ql.erase(it1, it2); return Ql; } // Returns the elements after j in a Quark_line Quark_line Col_functions::after( Quark_line & Ql, int j ) const{ if(j<0 or j> static_cast<int>(Ql.ql.size()-1)){ - std::cerr <<"before: the argument must be >=0 and inside vector, was " << j << std::endl; + std::cerr <<"after: the argument must be >=0 and inside vector, was " << j << std::endl; std::cerr.flush(); throw -1; } // In first part erase everything after and including j quark_line::iterator it1=Ql.ql.begin(); quark_line::iterator it2=Ql.ql.begin()+j+1; Ql.ql.erase(it1, it2); return Ql; } // Function for exchanging a gluon between the partons p1 and p2 // See my general color structure paper Col_amp Col_functions::exchange_gluon( const Col_str & Cs, int p1, int p2) const{ Col_str Cs_copy=Cs; // Find out kind and location of partons std::string kind1 = Cs_copy.find_kind(p1); std::string kind2 = Cs_copy.find_kind(p2); std::vector<int> place1 = Cs_copy.find_parton(p1); std::vector<int> place2 = Cs_copy.find_parton(p2); // To contain result Col_amp Ca; // Make sure the col_f in multiplying participating Ql's are 0 Polynomial Poly1; // For comparing Poly is 1, all powers are 0 if (!(Poly1 == Cs_copy.cs.at(place1.at(0)).Poly)) { // Move factor of Quark_line to Col_str Cs_copy.Poly = Cs_copy.Poly * Cs_copy.cs.at(place1.at(0)).Poly; Cs_copy.cs.at(place1.at(0)).Poly = Poly1; } if (!(Cs_copy.cs.at(place2.at(0)).Poly == Poly1)) { // Move factor of Quark_line to Col_str Cs_copy.Poly = Cs_copy.Poly * Cs_copy.cs.at(place2.at(0)).Poly; Cs_copy.cs.at(place2.at(0)).Poly = Poly1; } // If the qs are the same, the result is just cf*old for q and qbar, or (1/2) // TODO sign if ( place1 == place2 ) { Monomial mon; // For qq or qbar qbar if (kind1 == "q" or kind1 == "qbar") mon.pow_cf = 1; // for gluons else mon.pow_Nc = 1; // There is a factor TR from the contracted gluon, // and we have 2 terms which survives in the large Nc limit // the factor -1 can be seen by noting that once the gluon // is inserted to the left and once to the right in the surviving terms // TODO check again! mon.pow_TR+=1; mon.n_times*=-2; Cs_copy.Poly = Cs_copy.Poly * mon; Ca.ca.push_back( Cs_copy ); return Ca; } // Exchange between qq or qbar qbar if ((kind1 == "q" && kind2 == "q") or (kind1 == "qbar" && kind2 == "qbar")) { Col_str Cs2 = Cs_copy; // The q's normally sit on different quark_lines // The leading N-tems has the q's exchanged Cs_copy.cs.at(place1.at(0)).ql.at(place1.at((1))) = p2; Cs_copy.cs.at(place2.at(0)).ql.at(place2.at((1))) = p1; // Multiply with TR 1/2 Monomial Mon; //Mon.pow_2 = -1; changed 121218 Mon.pow_TR = 1; Cs_copy.Poly = Cs_copy.Poly * Mon; // The suppressed term is a copy of the old term, // but multiplied with -TR /Nc //Mon.pow_2 = -1; changed 121218 Mon.pow_TR = 1; Mon.n_times = -1; Mon.pow_Nc = -1; Cs2.Poly = Cs2.Poly * Mon; Ca.ca.push_back(Cs_copy); Ca.ca.push_back(Cs2); } // Exchange between q qbar if ((kind1 == "q" && kind2 == "qbar") or (kind1 == "qbar" && kind2 == "q")) { std::vector<int> q_place; std::vector<int> qbar_place; int the_q; int the_qbar; if ((kind1 == "q" && kind2 == "qbar")) { q_place = place1; qbar_place = place2; the_q = p1; the_qbar = p2; } else if ((kind1 == "qbar" && kind2 == "q")) { q_place = place2; qbar_place = place1; the_q = p2; the_qbar = p1; } Col_str Cs2 = Cs_copy; // If q and qbar are not part of same ql if (q_place.at(0) != qbar_place.at(0)) { // The non-suppressed term, obtained by erasing q qbar and joining rest Cs_copy.erase(q_place); Cs_copy.erase(qbar_place); // In the ql of the qbar, append the ql of the q, then erase the ql of the q //cout << "Before append " << Cs_copy << endl; Cs_copy.cs.at(qbar_place.at(0)).append(Cs_copy.cs.at(q_place.at(0)).ql); //cout << "After append " << Cs_copy << endl; // and erase the ql of the q Cs_copy.erase(q_place.at(0)); } // If q and qbar sit on same ql else { //std::cout << "q qbar part of same ql " << std::endl; // The non-suppressed term, obtained by erasing q qbar and joining rest to a ring Cs_copy.erase(qbar_place); Cs_copy.erase(q_place); Cs_copy.cs.at(q_place.at(0)).open = false; // If only one quark remains the term should be 0 //if ( Cs_copy.cs.at( q_place.at(0) ).ql.size()==1 ) Cs_copy.clear(); } // Construct Quark_line={q,qbar}, and append it Quark_line Ql; Ql.open = true; Ql.ql.push_back(the_q); Ql.ql.push_back(the_qbar); Cs_copy.cs.push_back(Ql); // Multiply with 1/2 Monomial Mon; // Mon.pow_2 = -1; // Changed 121218 Mon.pow_TR = 1; Cs_copy.Poly = Cs_copy.Poly * Mon; //std::cout << "Non-suppressed term " << Cs_copy; // The suppressed term is a copy of the old term, // but multiplied with -TR / Nc //Mon.pow_2 = -1;// Changed 121218 Mon.pow_TR=1; Mon.n_times = -1; Mon.pow_Nc = -1; Cs2.Poly = Cs2.Poly * Mon; // Construct the resulting color_amplitude and return // Make sure there is not only one or 0 gluons in a closed ql // Append Cs to ca if >=2 gluons // if open, append if (Cs_copy.cs.at(q_place.at(0)).open) Ca.ca.push_back(Cs_copy); // if closed but >= 2 gluons, append else if (Cs_copy.cs.at(q_place.at(0)).ql.size() >= 2 && !Cs_copy.cs.at( q_place.at(0)).open) Ca.ca.push_back(Cs_copy); // If 1 gluon, the term is 0 and is not appended // If 0 gluons the empty colsed ql is equivalent to multiplying with Nc, obtained after simplification else if (Cs_copy.cs.at(q_place.at(0)).ql.empty() && !Cs_copy.cs.at(q_place.at(0)).open) { // return 2nd term * (1-Nc^2) //Monomial Mon_tmp; // old incorrect code //Mon_tmp.pow_Nc = 2; //Cs2.Poly = Cs2.Poly + Mon_tmp * Cs2.Poly; Ca.ca.push_back(Cs_copy); Ca.ca.push_back(Cs2); Ca.simplify(); //cout << "Ca in exchange_gluon" << Ca; return Ca; } Ca.ca.push_back(Cs2); } // Exchange between q g if ((kind1 == "q" && kind2 == "g") or (kind1 == "g" && kind2 == "q")) { std::vector<int> q_place; std::vector<int> g_place; int the_q=0; int the_g=0; if (kind1 == "q" && kind2 == "g") { q_place = place1; g_place = place2; the_q = p1; the_g = p2; } else if (kind1 == "g" && kind2 == "q") { q_place = place2; g_place = place1; the_q = p2; the_g = p1; } // For containing Quark_lines Quark_line Ql11; Quark_line Ql12; Quark_line Ql21; Quark_line Ql22; Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; // If the q and the g are not part of same ql if (g_place.at(0) != q_place.at(0)) { // Take split the Ql with the gluon after the gluon Quark_line Qlg_start = before(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Quark_line Qlg_end = after(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Quark_line Qlq_end = after(Cs_copy.cs.at(q_place.at(0)), q_place.at(1)); // First part, + sign // First part, first Ql Ql11 = Qlg_end; Ql11.prepend(the_g); Ql11.prepend(the_q); // First part, second Ql Ql12 = Qlg_start; Ql12.append(Qlq_end.ql); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql11.append(Ql12.ql); Ql11.open = true; }; // Second part, first Ql Ql21 = Qlg_end; Ql21.prepend(the_q); // Second part, second Ql Ql22 = Qlg_start; Ql22.append(the_g); Ql22.append(Qlq_end.ql); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql21.append(Ql22.ql); Ql21.open = true; }; // Replace Ql's in the Col_str Cs1.cs.at(q_place.at(0)) = Ql11; Cs2.cs.at(q_place.at(0)) = Ql21; // There is only one ql if the ql of the gluon was closed if (Qlg_start.open) { Cs1.cs.at(g_place.at(0)) = Ql12; Cs2.cs.at(g_place.at(0)) = Ql22; } else { // Erase 2nd ql Cs1.erase(g_place.at(0)); Cs2.erase(g_place.at(0)); } } // If q and g are part of same ql else { Quark_line Ql1 = before(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Ql1.ql.erase(Ql1.ql.begin()); Quark_line Ql2 = after(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); // cout << "Ql1 " << Ql1 << endl; // cout << "Ql2 " << Ql2 << endl; // First term, First ql Ql11 = Ql1; Ql11.open = false; // First term, 2nd ql Ql12 = Ql2; Ql12.prepend(the_g); Ql12.prepend(the_q); // 2nd term, 1st ql Ql21 = Ql1; Ql21.append(the_g); Ql21.open = false; // 2nd term 2nd ql Ql22 = Ql2; Ql22.prepend(the_q); // Replace Ql's in the Col_str Cs1.cs.at(q_place.at(0)) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + q_place.at(0) + 1, Ql12); // Replace Ql's in the Col_str Cs2.cs.at(q_place.at(0)) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + q_place.at(0) + 1, Ql22); } // Multiply first part with 1/2 Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; // Changed 121218 Mon_tmp.pow_TR = 1; Cs1.Poly = Cs1.Poly * Mon_tmp; // Multiply second part with -1/2 Monomial Mon_tmp2; //Mon_tmp2.pow_2 = -1;// Changed 121218 Mon_tmp2.pow_TR = 1; Mon_tmp2.n_times = -1; Cs2.Poly = Cs2.Poly * Mon_tmp2; // Add to result Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); }// end echange between q g // Exchange between qbar g (q and g in my paper) if ((kind1 == "qbar" && kind2 == "g") or (kind1 == "g" && kind2 == "qbar")) { //cout <<"qbar g \n"; std::vector<int> qbar_place; std::vector<int> g_place; int the_qbar=0; int the_g=0; if (kind1 == "qbar" && kind2 == "g") { qbar_place = place1; g_place = place2; the_qbar = p1; the_g = p2; } else if (kind1 == "g" && kind2 == "qbar") { qbar_place = place2; g_place = place1; the_qbar = p2; the_g = p1; } // For containing Quark_lines Quark_line Ql11, Ql12, Ql21, Ql22; // For containing resulting Col_str's Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; // If ql and g on different quark_lines if (g_place.at(0) != qbar_place.at(0)) { // Split ql's into parts Quark_line Qlg_start = before(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Quark_line Qlg_end = after(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Quark_line Qlqbar_start = before(Cs_copy.cs.at(qbar_place.at(0)), qbar_place.at(1)); // First part, + sign, first Ql Ql11 = Qlqbar_start; Ql11.append(the_g); Ql11.append(Qlg_end.ql); // First part, + sign, second Ql Ql12 = Qlg_start; Ql12.append(the_qbar); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql11.append(Ql12.ql); Ql11.open = true; //cout << "Ql11" << Ql11 << endl; std::cout.flush(); }; // Second part, - sign, first Ql Ql21 = Qlqbar_start; Ql21.append(Qlg_end.ql); // Second part, - sign, second Ql Ql22 = Qlg_start; Ql22.append(the_g); Ql22.append(the_qbar); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql21.append(Ql22.ql); Ql21.open = true; //cout << "Ql21" << Ql21 << endl; std::cout.flush(); }; // Replace Ql's in the Col_str Cs1 = Cs_copy; Cs2 = Cs_copy; if (Qlg_start.open) { Cs1.cs.at(qbar_place.at(0)) = Ql11; Cs1.cs.at(g_place.at(0)) = Ql12; Cs2.cs.at(qbar_place.at(0)) = Ql21; Cs2.cs.at(g_place.at(0)) = Ql22; } else { // If the ql of the g was closed, there is only one ql, erase 2nd ql Cs1.cs.at(qbar_place.at(0)) = Ql11; //cout << "Cs1 " << Cs1 << endl; Cs1.erase(g_place.at(0)); Cs2.cs.at(qbar_place.at(0)) = Ql21; Cs2.erase(g_place.at(0)); //cout << "Cs1 " << Cs1 << endl; //cout << "Cs2 " << Cs2 << endl; } } // If qbar and g are part of same quark_line else { Quark_line Ql1 = before(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Quark_line Ql2 = after(Cs_copy.cs.at(g_place.at(0)), g_place.at(1)); Ql2.ql.erase(Ql2.ql.end() - 1); // First term, First ql Ql11 = Ql1; Ql11.append(the_qbar); // First term, 2nd ql Ql12 = Ql2; Ql12.prepend(the_g); Ql12.open = false; // 2nd term, 1st ql Ql21 = Ql1; Ql21.append(the_g); Ql21.append(the_qbar); // 2nd term 2nd ql Ql22 = Ql2; Ql22.open = false; // Replace Ql's in the Col_str Cs1 = Cs_copy; Cs1.cs.at(qbar_place.at(0)) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + qbar_place.at(0) + 1, Ql12); // Replace Ql's in the Col_str Cs2 = Cs_copy; Cs2.cs.at(qbar_place.at(0)) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + qbar_place.at(0) + 1, Ql22); } // Multiply with 1/2 Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; changed 121218 Mon_tmp.pow_TR = 1; Cs1.Poly = Cs1.Poly * Mon_tmp; // Multiply with -1/2 Monomial Mon_tmp2; //Mon_tmp2.pow_2 = -1; changed 121218 Mon_tmp2.pow_TR = 1; Mon_tmp2.n_times = -1; Cs2.Poly = Cs2.Poly * Mon_tmp2; Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); } // Exchange between g g if (kind1 == "g" && kind2 == "g") { // For containing resulting Col_str's Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; Col_str Cs3 = Cs_copy; Col_str Cs4 = Cs_copy; // For containing Quark_lines Quark_line Ql11, Ql12, Ql21, Ql22, Ql31, Ql32, Ql41, Ql42; // If g's on different Ql's if (place1.at(0) != place2.at(0)) { // Split both ql's into parts Quark_line Ql1_start = before(Cs_copy.cs.at(place1.at(0)), place1.at(1)); Quark_line Ql1_end = after(Cs_copy.cs.at(place1.at(0)), place1.at(1)); Quark_line Ql2_start = before(Cs_copy.cs.at(place2.at(0)), place2.at(1)); Quark_line Ql2_end = after(Cs_copy.cs.at(place2.at(0)), place2.at(1)); // First term, both quarks on 2nd ql, First ql Ql11 = Ql1_start; Ql11.append(Ql2_end.ql); // First term, 2nd ql Ql12 = Ql2_start; Ql12.append(p2); Ql12.append(p1); Ql12.append(Ql1_end.ql); // 2nd term, g1 on ql1, g2 on ql2, 1st ql Ql21 = Ql1_start; Ql21.append(p1); Ql21.append(Ql2_end.ql); // 2nd term, 2nd ql Ql22 = Ql2_start; Ql22.append(p2); Ql22.append(Ql1_end.ql); // 3rd term, g2 on ql1, g1 on ql2,1st ql Ql31 = Ql1_start; Ql31.append(p2); Ql31.append(Ql2_end.ql); // 3rd term, 2nd ql Ql32 = Ql2_start; Ql32.append(p1); Ql32.append(Ql1_end.ql); // last term, both quarks on 1st ql,1st ql Ql41 = Ql1_start; Ql41.append(p1); Ql41.append(p2); Ql41.append(Ql2_end.ql); // last term, 2nd ql Ql42 = Ql2_start; Ql42.append(Ql1_end.ql); // Replace Ql's in the Col_str // If both Ql's are open if (Cs_copy.cs.at(place1.at(0)).open && Cs_copy.cs.at(place2.at(0)).open) { Cs1.cs.at(place1.at(0)) = Ql11; Cs1.cs.at(place2.at(0)) = Ql12; Cs2.cs.at(place1.at(0)) = Ql21; Cs2.cs.at(place2.at(0)) = Ql22; Cs3.cs.at(place1.at(0)) = Ql31; Cs3.cs.at(place2.at(0)) = Ql32; Cs4.cs.at(place1.at(0)) = Ql41; Cs4.cs.at(place2.at(0)) = Ql42; } // If string where closed glue together ends // If first ql closed else if (!Cs_copy.cs.at(place1.at(0)).open) { Ql12.append(Ql11.ql); Ql22.append(Ql21.ql); Ql32.append(Ql31.ql); Ql42.append(Ql41.ql); //The result should be open/closed if the ql2 was open/closed Ql12.open = Cs_copy.cs.at(place2.at(0)).open; Ql22.open = Cs_copy.cs.at(place2.at(0)).open; Ql32.open = Cs_copy.cs.at(place2.at(0)).open; Ql42.open = Cs_copy.cs.at(place2.at(0)).open; //The reslut contaied int Qlx2 sould be used for the Color structure // Replace Ql's in the Col_str Cs1.cs.at(place2.at(0)) = Ql12; Cs2.cs.at(place2.at(0)) = Ql22; Cs3.cs.at(place2.at(0)) = Ql32; Cs4.cs.at(place2.at(0)) = Ql42; Cs1.erase(place1.at(0)); Cs2.erase(place1.at(0)); Cs3.erase(place1.at(0)); Cs4.erase(place1.at(0)); } // if only 2nd ql closed if (!Cs_copy.cs.at(place2.at(0)).open && Cs_copy.cs.at(place1.at(0)).open) { Ql11.append(Ql12.ql); Ql21.append(Ql22.ql); Ql31.append(Ql32.ql); Ql41.append(Ql42.ql); // The result should be open Ql11.open = true; Ql21.open = true; Ql21.open = true; Ql21.open = true; //The result contained int Qlx1 should be used for the Color structure // Replace Ql's in the Col_str Cs1.cs.at(place2.at(0)) = Ql11; Cs2.cs.at(place2.at(0)) = Ql21; Cs3.cs.at(place2.at(0)) = Ql31; Cs4.cs.at(place2.at(0)) = Ql41; Cs1.erase(place1.at(0)); Cs2.erase(place1.at(0)); Cs3.erase(place1.at(0)); Cs4.erase(place1.at(0)); } } // If g's on same ql's if (place1.at(0) == place2.at(0)) { //cout << "g's on same ql \n"; // Split ql into parts Quark_line Ql_start = before(Cs_copy.cs.at(place1.at(0)), std::min(place2.at( 1), place1.at(1))); // the part between the g's Quark_line Ql_between = before(Cs_copy.cs.at(place1.at(0)), std::max( place2.at(1), place1.at(1))); Ql_between = after(Ql_between, std::min(place2.at(1), place1.at(1))); // the end after 2nd g Quark_line Ql_end = after(Cs_copy.cs.at(place1.at(0)), std::max(place2.at(1), place1.at(1))); //std::cout << "QL_start " << Ql_start << std::endl; //std::cout << "Ql_between " << Ql_between << std::endl; //std::cout << "Ql_end " << Ql_end << std::endl; // added 130710 // we have to know what g is first to put them back in right order int first_g, last_g; if( place1.at(1) < place2.at(1) ) { first_g=p1; last_g=p2; } else { first_g=p2; last_g=p1; } // First term, both gluons inbetween Ql11 = Ql_start; Ql11.append(Ql_end.ql); // First term, part between quarks Ql12 = Ql_between; //Ql12.append(p2); //Ql12.append(p1); changed 130710 Ql12.append( last_g ); Ql12.append( first_g ); Ql12.open = false; //std::cout << "Ql11 " << Ql11 << std::endl; //std::cout << "Ql12 " << Ql12 << std::endl; // 2nd term, g1 after first part, g2 after 2nd Ql21 = Ql_start; //Ql21.append(p1); changed 130710 Ql21.append( first_g ); Ql21.append(Ql_end.ql); // First term, part between quarks Ql22 = Ql_between; //Ql22.append(p2); changed 130710 Ql22.append( last_g ); Ql22.open = false; //std::cout << "Ql21 " << Ql21 << std::endl; //std::cout << "Ql22 " << Ql22 << std::endl; // 3rd term, g2 after first part, g1 after 2nd Ql31 = Ql_start; //Ql31.append(p2); changed 130710 Ql31.append( last_g); Ql31.append(Ql_end.ql); // First term, part between quarks Ql32 = Ql_between; //Ql32.append(p1);changed 130710 Ql32.append( first_g ); Ql32.open = false; //std::cout << "Ql31 " << Ql31 << std::endl; //std::cout << "Ql32 " << Ql32 << std::endl; // 4th term, g2 after first part, g1 after 2nd Ql41 = Ql_start; Ql41.append( first_g ); Ql41.append( last_g ); Ql41.append(Ql_end.ql); // First term, part between quarks Ql42 = Ql_between; Ql42.open = false; //std::cout << "Ql41 " << Ql41 << std::endl; //std::cout << "Ql42 " << Ql42 << std::endl; Cs1.cs.at(place1.at(0)) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + place1.at(0) + 1, Ql12); Cs2.cs.at(place1.at(0)) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + place1.at(0) + 1, Ql22); Cs3.cs.at(place1.at(0)) = Ql31; Cs3.cs.insert(Cs3.cs.begin() + place1.at(0) + 1, Ql32); Cs4.cs.at(place1.at(0)) = Ql41; Cs4.cs.insert(Cs4.cs.begin() + place1.at(0) + 1, Ql42); } // Multiplying with 1/2 where appropriate Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; Mon_tmp.pow_TR = 1; Cs2.Poly = Cs2.Poly * Mon_tmp; Cs3.Poly = Cs3.Poly * Mon_tmp; // Multiplying with -1/2 where appropriate //Mon_tmp.pow_2 = -1; changed 121218 Mon_tmp.pow_TR = 1; Mon_tmp.n_times = -1; Cs1.Poly = Cs1.Poly * Mon_tmp; Cs4.Poly = Cs4.Poly * Mon_tmp; Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); Ca.ca.push_back(Cs3); Ca.ca.push_back(Cs4); } // Simplify Col_amp Ca.simplify(); return Ca; } // Function for exchanging a gluon between two partons p1 and p2 Col_amp Col_functions::exchange_gluon( const Col_amp & Ca, int p1, int p2) const{ // Final col_amp to return Col_amp Ca_out; // Exchange in each Col_str, and append to new Col_amp for(uint m=0; m< Ca.ca.size(); m++ ){ // Exchange in Col_str m Col_amp part_m=exchange_gluon( Ca.ca.at(m), p1, p2 ); // Append result Ca_out.append(part_m.ca); } return Ca_out; } // Exchange gluons between all possible parton pairs Col_amp Col_functions::exchange_gluon(Col_amp & Ca, int n_p) const{ Col_amp Ca_out; // Loop over partons for(int p1=1; p1<= n_p; p1++){ for(int p2=1; p2<= n_p; p2++){ Ca_out=Ca_out+ exchange_gluon(Ca, p1, p2); } } Ca_out.simplify(); return Ca_out; } // Function simulating virtual gluon exchane, evolving the color structure // Evolution performed n_steps time with size alpha Col_amp Col_functions::step_evolv(Col_amp & Ca, int n_p, int n_steps, double alpha) const{ // Let A->A+alpha*ANew, repeat this N_step times for(int i=1; i<=n_steps; i++){ // The amplitude resulting after exchange, part to add Col_amp Ca_new=exchange_gluon(Ca, n_p); // The evolved amplitude, A->A+alpha*ANew Ca=Ca+Ca_new*alpha; Ca.simplify(); } return Ca; } // Function for contracting quarks // between two color structures Cs1 and Cs2 Col_str Col_functions::contract_quarks(Col_str Cs1, Col_str Cs2) const{ //cout << "contract_quarks(Cs1,Cs2): has the arguments " << Cs1 << Cs2 << endl; // A vector for containing place of next quark to remove // in Cs1 and in conjugate( Col_str Cs2) std::vector<int> q_place; std::vector<int> q_place2; // The conjugate of Cs2 //Col_str conj_Cs2 = conjugate(Cs2); // The conjugate of Cs1 Col_str conj_Cs1 = conjugate(Cs1); // The total color structure //Col_str Cs = Cs1*conj_Cs2; // Changed to conjugate first arg, 130524 Col_str Cs = conj_Cs1*Cs2; //cout << "contract_quarks(Cs1, Cs2): Cs1 Cs2^* " << Cs << endl; // Count how many quarks should be contracted int n_q = Cs.n_quark(); // As long as there are quark_lines left to contract while (n_q > 0) { // Find first quark in Cs1 by looping over Quark_lines for (int i = 0; (n_q>0 && i < static_cast<int>(Cs.cs.size()) ); i++) { //cout << "contract_quarks(Cs1, Cs2): Looking at place " << i << " in " <<Cs << endl; std::cout.flush(); // Check if the quark-line is open, in which case it has a q if (Cs.cs.at(i).open) { //cout << "contract_quarks(Cs1, Cs2): Found open ql at " << i // << " " << Cs.cs.at(i) << endl; // The first quark is located and has position q_place.clear(); q_place.push_back(i); q_place.push_back(0); // and number int q = Cs.at2(q_place.at(0), q_place.at(1)); //cout << "contract_quarks(Cs1, Cs2): Found q to remove " << q << " at place " << q_place << endl; // Locate same quark a second time // Loop over Quark_lines q_place2.clear(); int i2 = i + 1; // Quark_line of second occurrence while (q_place2.empty()) { // As long as quark not found a second time if (Cs.cs.at(i2).at(Cs.cs.at(i2).ql.size() - 1) == q) {// If quark found, store place q_place2.push_back(i2); q_place2.push_back(Cs.cs.at(i2).ql.size() - 1); } i2++; } if (q_place2.empty()) { std::cerr << "contract_quarks(Cs1, Cs2): Found q " << q << " only once in " << Cs << std::endl; } //cout << "contract_quarks(Cs1, Cs2): Also found at place " // << q_place2 << endl; // Prepare new Quark_line // to be inserted at the place of found open Quark_line Quark_line new_Quark_line; Quark_line part2_new_Quark_line; // The first part of the new Quark_line should be the Quark_line // containing q in the conjugate new_Quark_line = Cs.cs.at(q_place2.at(0)); // Erasing q in the end new_Quark_line.ql.erase(--new_Quark_line.ql.end()); //cout << "contract_quarks(Cs1, Cs2): First part after removing contracted quark, new_Quark_line, from conj_Cs2 " << new_Quark_line <<"\n"; part2_new_Quark_line = Cs.cs.at(q_place.at(0)); // Erasing the q in the beginning part2_new_Quark_line.ql.erase(part2_new_Quark_line.ql.begin()); //cout << "contract_quarks(Cs1, Cs2): Second part " << part2_new_Quark_line <<"\n"; new_Quark_line.append(part2_new_Quark_line.ql); //cout << "contract_quarks(Cs1, Cs2): The new quark_line is " << new_Quark_line << "\n"; // If the first q index and the last q(bar) index in the new // Quark_line is the same (and the Quark_line is "open"), the indices // should be removed and the Quark_line should be closed if (new_Quark_line.ql.at(0) == new_Quark_line.ql.at( new_Quark_line.ql.size() - 1) && new_Quark_line.open) { //cout <<"First = Last \n"; // The string is closed new_Quark_line.open = false; // Remove last and first index new_Quark_line.ql.erase(--new_Quark_line.ql.end(), new_Quark_line.ql.end()); // cout <<"The new quark_line is " << new_Quark_line <<"\n"; new_Quark_line.ql.erase(new_Quark_line.ql.begin()); //cout <<"contract_quarks(Cs1, Cs2): The new quark_line is " << new_Quark_line <<"\n"; } // Inserting new Quark_line in the place of the old Cs.cs.at(i) = new_Quark_line; //cout << "contract_quarks(Cs1, Cs2): The Cs after insertion is " << Cs << "\n"; // Remove quark_line with q in Cs Cs.cs.erase((Cs.cs.begin() + q_place2.at(0))); //cout << "contract_quarks(Cs1, Cs2): after removal " << Cs << "\n"; i=-1; // reset to keep looking from the beginning in the new Cs (i will be increased to 0) }// end of if (open) n_q = Cs.n_quark(); //cout << "contract_quarks(Cs1, Cs2): The new number of quarks is " << n_q << endl; } // end of for, loop over quark_lines } //cout << "contract_quarks(Cs1, Cs2): To return " << Cs << endl; std::cout.flush(); return Cs; } // Function for contracting quarks // between two Col_amps. Col_amp Col_functions::contract_quarks( Col_amp Ca1, Col_amp Ca2 ) const{ Col_amp Ca_res; // Make sure the Col_strs are not empty "[]"=1, as all indices contracted Ca1.remove_empty_Col_strs(); Ca2.remove_empty_Col_strs(); // Loop over Col_strs, and contract quarks between all possible combinations // Loop over Col_str's in Ca1 for(uint m1=0; m1 < Ca1.ca.size(); m1++ ){ // Loop over Col_strs in Ca2 for(uint m2=0; m2 < Ca2.ca.size(); m2++ ){ Ca_res.ca.push_back( contract_quarks( Ca1.ca.at(m1), Ca2.ca.at(m2)) ); } } /* // Col_amps should not have uncontracted quarks and a Polynomial at the same time if( !Ca1.Scalar.size() && !Ca2.Scalar.empty() ){ cerr << "contract_quarks(Ca1, Ca1): Col_amp with uncontracted quarks had Polynomial " << Ca1.Scalar << " and " << Ca2.Scalar << endl; throw -1; } // Add Polynomials Ca_res.Scalar=Ca1.Scalar+Ca2.Scalar; */ //cout << "contract_quarks(Ca1, Ca2): To return " << Ca_res << endl; return Ca_res; } // Function to contract neighboring gluons in a Quark_line starting at j Quark_line Col_functions::contract_neighboring_gluons(Quark_line & Ql, int j ) const { //cout << "contract_neighboring_gluons( Ql, j ): incoming " << Ql <<" j " << j << endl; // If asked for -1st element, check last if(j==-1) j=Ql.ql.size()-1; // If asked for last+1 element, check first if( j== static_cast<int>(Ql.ql.size()-1) ) j=0; // Keep contracting gluons as long as: // there are at least two gluons to contract // neighbors are equal // and j is not the last parton in the ql while( j< static_cast<int>(Ql.ql.size()-1) && (Ql.ql.size()) >=2 && (Ql.ql.at(j))==Ql.ql.at(j+1) ){ quark_line::iterator it1=Ql.ql.begin()+j; quark_line::iterator it2=Ql.ql.begin()+j+2; // Removing neighboring gluons Ql.ql.erase(it1,it2); // This should increase the power of Cf Monomial Mon_tmp; Mon_tmp.pow_cf=1; Ql.Poly=Ql.Poly*Mon_tmp; if(j>2) j=j-2; // cout << "contract_neighboring_gluons: removed one pair, new Ql" << Ql << "and new j is" << j << endl; } // If j is the last parton and the Quark_line is closed and first=last // and there are at least 2 gluons while( static_cast<int>(Ql.ql.size())>=2 && j== static_cast<int>(Ql.ql.size()-1) && !Ql.open && Ql.ql.at(0)==Ql.ql.at( Ql.ql.size()-1) ){ // cout << "contract_neighboring_gluons( Ql, j ): entering second while " << endl; quark_line::iterator it1=Ql.ql.begin(); quark_line::iterator it2=Ql.ql.end()-1; Ql.ql.erase(it2); Ql.ql.erase(it1); //cout << "contract_neighboring_gluons( Ql, j ): removed last and first, new Ql" << Ql << "and new j is" << j << endl; //cout.flush(); // This should increase the power of Cf Monomial Mon_tmp; Mon_tmp.pow_cf=1; Ql.Poly=Ql.Poly*Mon_tmp; // If now, all gluons are removed, increase Nc an open the ql (if not already open) if( Ql.ql.empty() && !Ql.open ){ Monomial Mon_tmp; Mon_tmp.pow_Nc=1; Ql.Poly=Ql.Poly*Mon_tmp; Ql.open=true; } j=j-2; } //cout << "contract_neighboring_gluons( Ql, j ): to return" << Ql << endl; return Ql; } // Function to contract neighboring gluons in a Quark_line starting at place 0 Quark_line Col_functions::contract_neighboring_gluons( Quark_line & Ql ) const{ //cout << "contract_neighboring_gluons( Ql ): Incoming " << Ql << endl; // Counting powers of cf, // Should be increases once for every gluon that is contracted // initially put to dummy value int cf_pow_old=-9999; int cf_pow_new=0; // Keep looping over elements as long as cf_pow changes while( cf_pow_old!= cf_pow_new ){ cf_pow_old=cf_pow_new; // Loop over elements in the Quark_line, starting at first parton // and ending with last for (uint j=0; j < Ql.ql.size(); j++){ Ql=contract_neighboring_gluons(Ql, j); } // To see if cf_pow changed, arbitrarily compare 0th element in Polynomial (if existing) // (all pow_cf changes) if( !Ql.Poly.empty() ) cf_pow_new=Ql.Poly.at(0).pow_cf; // put cf_pow_new to current pow } //cout << "contract_neighboring_gluons( Ql ): To return " << Ql << endl; return Ql; } // Function to contract next to neighboring gluons in a Quark_line // starting at place j (so gluon j and j+2) // Also looks for new neighbors Quark_line Col_functions::contract_next_neighboring_gluons(Quark_line & Ql, int j) const{ //cout << "contract_next_neighboring_gluons (Ql, j): incoming " << Ql << " int, "<< j<< "\n"; // If asked for -1st element, check last if(j==-1) j=Ql.ql.size()-1; // If asked for -2nd element, check second last if(j==-2) j=Ql.ql.size()-2; // Keep track of changes in number of gluons // add dummy number '1' to run at least once int old_size=Ql.ql.size()+1; // Loop over index in quark_line as long as the size keep changing, // or at least once while( old_size!= static_cast<int>(Ql.ql.size()) && (Ql.ql.size())>=4){ old_size= (Ql.ql.size()); // There are three cases: // the normal case, and, if the Quark_line is closed, // 2nd last =first (0th) and last =2nd (1st) // TODO Why does not static_cast<int> work here??? bool normal_case=(Ql.ql.size()>=4 && static_cast<uint>(j)< Ql.ql.size()-2 && Ql.ql.at(j)==Ql.ql.at(j+2)); bool second_last_case=( Ql.ql.size()>=4 && !Ql.open && static_cast<uint>(j)==Ql.ql.size()-2 && Ql.ql.at(j)==Ql.ql.at(0) ); bool last_case=(Ql.ql.size()>=4 && !Ql.open && static_cast<uint>(j)==Ql.ql.size()-1 && Ql.ql.at(j)==Ql.ql.at(1)); // See if next to neighbors are equal // The point of having a while is to check again if one pair was found while(normal_case or second_last_case or last_case) { //cout << "Found next to neighbor " << j << " " << Ql.ql.at(j) <<"\n"; // Erase the gluons (at places depending on size) quark_line::iterator it1; quark_line::iterator it2; if(normal_case){ it1=Ql.ql.begin()+j; it2=Ql.ql.begin()+j+2; //cout << "Normal case \n"; } else if(second_last_case){ //recently changed it1=Ql.ql.begin(); it2=Ql.ql.end()-2; //cout << "Second last case \n"; } else if(last_case){ it1=Ql.ql.begin()+1; it2=Ql.ql.end()-1; // Erase right most gluon first //cout << "Last case \n"; } // Erase right most gluon first Ql.ql.erase(it2); Ql.ql.erase(it1); // The surviving term comes with -TR/(Nc) Monomial Mon_tmp; // Mon_tmp.pow_2=-1; Mon_tmp.pow_TR = 1; Mon_tmp.n_times=-1; Mon_tmp.pow_Nc=-1; //cout <<"Ql " << Ql <<endl; Ql.Poly=Ql.Poly*Mon_tmp; //cout << Ql << endl; // Check if new neighbors are equal uint old_size2=Ql.ql.size(); // Check if inbetween gluon is now neighbor with itself to the right Ql=contract_neighboring_gluons(Ql, j); // Check if inbetween gluon is now neighbor with itself to the left Ql=contract_neighboring_gluons(Ql, j-1); // if a neighboring pair was removed if( (Ql.ql.size()) != old_size2 ) j=j-2; // Two gluons have been erased so to keep looking forward in the // Quark_line j must not be increased j--; // Why did it not work to change type of Ql.ql.size() using static_cast<int>?? normal_case=( Ql.ql.size()>=4 && static_cast<uint>(j)< Ql.ql.size()-2 && Ql.ql.at(j)==Ql.ql.at(j+2)); if(normal_case){ //cout << "j "<< j << ", Ql.ql.size() "<< Ql.ql.size() << endl; } second_last_case=( Ql.ql.size()>=4 && !Ql.open && j== static_cast<int>(Ql.ql.size())-2 && Ql.ql.at(j)==Ql.ql.at(0)); last_case=( Ql.ql.size()>=4 && !Ql.open && j== static_cast<int>(Ql.ql.size())-1 && Ql.ql.at(j)==Ql.ql.at(1)); } } //cout << "contract_next_neighboring_gluons (Ql, j): to return " << Ql << "\n"; return Ql; } // Function to contract next to neighboring gluons in a Quark_line Quark_line Col_functions::contract_next_neighboring_gluons(Quark_line & Ql) const{ //cout << "contract_next_neighboring_gluons( Ql ): Incoming " << Ql << "\n"; // Start with looking for neighbors Ql=contract_neighboring_gluons(Ql); // Keep track of changes in number of gluons // add dummy number '1' to run at least once int old_size=Ql.ql.size()+1; // Loop over index in quark-line as long as the size keep changing, // or at least once while( old_size!= static_cast<int>(Ql.ql.size()) && (Ql.ql.size())>=4){ // For seeing if size has changed old_size=Ql.ql.size(); for (int j=0; j< static_cast<int>(Ql.ql.size()); j++){ // Contract next to neighbors starting at place j Ql=contract_next_neighboring_gluons(Ql, j); } } //cout << "contract_next_neighboring_gluons( Ql ): To return " << Ql << "\n"; return Ql; } // Contract up to next to neighboring gluons in each Quark_line in the Col_str Col_str Col_functions::contract_next_neighboring_gluons( Col_str & Cs ) const{ //cout << "contract_next_neighboring_gluons( Cs ): Incoming " << Cs <<endl; // Loop over Quark_lines and remove neighboring and next to neighboring // gluon indices for( uint i=0; i < Cs.cs.size(); i++ ){ // Create a Quark_line to use as argument Quark_line Ql= Cs.at(i); Ql=contract_next_neighboring_gluons( Ql ); // Adding powers to get total power of 2, Nc and cf // Collect these powers in Cs Cs.Poly=Cs.Poly*Ql.Poly; // and put powers in Ql to 0 Ql.Poly.clear(); Cs.cs.insert(Cs.cs.begin() +i, Ql); // Remove old version of Quark_line (now at place i+1) Cs.cs.erase( Cs.cs.begin() +i + 1 ); } // Remove 1 and 0-rings, simplify Poly and normal order Cs.simplify(); //cout << "contract_next_neighboring_gluons( Cs ): To return " << Cs <<endl; return Cs; } // Contract up to next to neighboring gluons in each Quark_line in the Col_str in each Col_amp Col_amp Col_functions::contract_next_neighboring_gluons(Col_amp & Ca) const{ // Loop over Col_str's for (uint m = 0; m < Ca.ca.size(); m++) { Ca.ca.at(m) = contract_next_neighboring_gluons(Ca.ca.at(m)); } return Ca; } // Function for contracting gluon indices in a two-ring in a Col_str Col_str Col_functions::contract_2_rings(Col_str & Cs) const{ //cout << "contract_2_rings(Col_str): Called with argument " << Cs << endl; // Contract gluons from 2-ring, this gives only one term // For storing place of two-ring std::vector<int> place1; // For storing place of gluon with same index as second gluon in two-ring // i.e. the gluon to be replaced with the first index in the 2-ring std::vector<int> place2; // The second index in two-ring, to be removed int the_g; // Loop over Quark_lines to find a two-ring for (uint i = 0; i < Cs.cs.size(); i++) { place1.clear(); place2.clear(); // Search for 2-rings // A gluon 2-ring has length 2 and is closed if (Cs.cs.at(i).ql.size() == 2 && !Cs.cs.at(i).open) { // Save place of two-ring place1.push_back(i); // Pick second gluon (for no good reason) // this index should be replaced by first index // in the other place where it occurs, and the 2-ring should be removed place1.push_back(1); //cout << "Found 2-ring at "<< i << "\n"; the_g = Cs.at2(place1.at(0), place1.at(1)); //cout << "The gluon to be reomved is " << the_g << " aat place " << place1; //std::cout.flush(); // Now, in all Quark_lines, look for same the_g until found for (uint i2 = 0; i2 < Cs.cs.size(); i2++) { for (uint j2 = 0; j2 < Cs.cs.at(i2).ql.size(); j2++) { // If the_g was found at a DIFFERENT place if ((i2 != i or j2 != 1) && Cs.at2(i2, j2) == the_g) { place2.push_back(i2); place2.push_back(j2); //cout << "Found same g, " << the_g << ", at place " << place2; } } } // Check that the gluon index was found again if (place2.empty()) { std::cerr << "contract_2_rings: Did not find index " << the_g << " other than at place " << place1 << "\n"; std::cerr.flush(); throw -1; } // If the_g was found twice in the same ql if (place1.at(0) == place2.at(0)) { //cout << "Found gluon " << the_g << "twice in the quark_line "<< place1.at(0) << endl; // Keep the Monomial // Multiply with Nc Mon from the contraction Monomial Mon_tmp; Mon_tmp.pow_Nc = 1; Mon_tmp.pow_cf = 1; // Multiply the Poly of the Col_str with the Poly of the ql // and this color factor Cs.Poly = Cs.Poly * Cs.cs.at(place1.at(0)).Poly * Mon_tmp; // Erase the ql Cs.erase(place1.at(0)); return Cs; } // That index should be changed to the index of the first gluon // in the 2-ring //cout << "The gluon to be removed is: " << Cs.at2( place2.at(0), place2.at(1) ) << "\n"; Cs.cs.at(place2.at(0)).ql.at(place2.at(1)) = Cs.at2(place1.at(0), 0); //cout << "Replacement done with result: " << Cs << "\n"; // The two ring should be removed Cs.cs.erase(Cs.cs.begin() + i); Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; Mon_tmp.pow_TR = 1; Cs.Poly = Cs.Poly * Mon_tmp; i++; //cout << "2-ring removed with result: " << Cs << "\n"; } } // One-rings may have been created Cs.remove_1_rings(); //cout << "contract_2_rings(Col_str): To return " << Cs << endl; return Cs; } // Contract two-rings in each Quark_line in the Col_str in each Col_amp Col_amp Col_functions::contract_2_rings(Col_amp & Ca) const{ // Loop over Col_str's for (uint m = 0; m < Ca.ca.size(); m++) { Ca.ca.at(m) = contract_2_rings(Ca.ca.at(m)); } return Ca; } // Function for splitting a closed Quark_line into two Quark_lines // j1 && j2 are the locations of the gluons to be removed in the split // May create 1-rings and 0-rings std::string Col_functions::spm_file_name( const Col_amp & Basis, bool leading) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; ss << "SPM_q"; int nq=Basis.at(0).n_quark(); ss << nq; ss << "_g"; int ng=Basis.at(0).n_gluon(); ss << ng; if ( leading ) ss << "_l"; if ( leading_cf ) ss << "_cfl"; ss << "_Nc"; ss << Nc; ss << ".dat"; filename=ss.str(); return filename; } Col_str Col_functions::split_Quark_line( Quark_line & Ql, int j1, int j2 ) const{ // cout << "Coming in to split_Quark_line: Ql \n"; if(Ql.open){ std::cerr <<"split_Quark_line expects a closed quark_line" << std::endl; std::cerr.flush(); throw -1; } Quark_line Ql1=Ql; Quark_line Ql2=Ql; // In first part erase everything between j1 and j2 quark_line::iterator it1=Ql1.ql.begin()+j1; quark_line::iterator it2=Ql1.ql.begin()+j2; Ql1.ql.erase(it1, it2+1); // In second part, erase from j2... it1=Ql2.ql.begin()+j1; it2=Ql2.ql.begin()+j2; Ql2.ql.erase( it2, Ql2.ql.end() ); // ... and before j1 Ql2.ql.erase( Ql2.ql.begin(), it1+1 ); Col_str Cs; // Construct Col_str to return Cs.cs.push_back(Ql1); Cs.cs.push_back(Ql2); //cout << "The split quark_line is " << Cs << "\n"; return Cs; } // TODO can there be a problem with next to neighbors? // Function for contracting gluon indices within the same Quark_line // Checks only for ONE pair -Col_amp Col_functions::contract_Ql_gluons(Quark_line & Ql) const{ +Col_amp Col_functions::contract_Quark_line_gluons( Quark_line & Ql ) const{ //cout <<"contract_Ql_gluons(Ql): Incoming " << Ql << endl; // Check that all quark indices have been removed - if (Ql.open) + if (Ql.open){ std::cerr - << "Warning from contract_Ql_gluons(Quark_line Ql): all quark indices where not contracted in " << Ql << std::endl; - + << "Col_functions::contract_Quark_line_gluons: all quark indices where not contracted in " << Ql << std::endl; + } // There will be two terms to keep track of, store in Col_amp Col_amp Ca; //If the Quark_line was empty, return a Col_amp with info on Col_str if (Ql.ql.empty()) { Col_str Cs; // Move Polynomial to Col_str Cs.Poly = Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); Ca.ca.push_back(Cs); return Ca; } // Too keep track if partner was found or not bool found_pair = false; // Take first term and look for partner for (uint j1 = 0; j1 < Ql.ql.size() - 1; j1++) { int the_g = Ql.ql.at(j1); // Look for same g for (uint j2 = j1 + 1; j2 < Ql.ql.size(); j2++) { // if same g found if (Ql.ql.at(j2) == the_g) { // Special case that the gluons are neighbors if (j2 == j1 + 1 or (!Ql.open && j1 == 0 && j2 == Ql.ql.size() - 1)) { Ql = contract_neighboring_gluons(Ql, j1); // Make a Col_str out of the Quark_line Col_str Cs; Cs.cs.push_back(Ql); Ca.ca.push_back(Cs); Ca.simplify(); //cout << "contract_Ql_gluons(Ql): to return " << Ca << endl; return Ca; } // Special case that the gluons are next to neighbors if (j2 == j1 + 2 //normal case or (!Ql.open && j1 == 0 && j2 == Ql.ql.size()- 2) //first and second last or (!Ql.open && j1 == 1 && j2 == Ql.ql.size()- 1) //second and last ) { //cout << "contract_Ql_gluons: Found next to neighbors in Ql" // << Ql << endl; if (j2 == j1 + 2) Ql = contract_next_neighboring_gluons(Ql, j1); if ((!Ql.open && j1 == 0 && j2 == Ql.ql.size() - 2)) Ql = contract_next_neighboring_gluons(Ql, Ql.ql.size() - 2); if( (!Ql.open && j1 == 1 && j2 == Ql.ql.size()- 1) ) Ql = contract_next_neighboring_gluons(Ql, Ql.ql.size() - 1); // Make a Col_str out of the Quark_line Col_str Cs; Cs.cs.push_back(Ql); Ca.ca.push_back(Cs); Ca.simplify(); //cout << "contract_Ql_gluons(Ql): to return " << Ca << endl; return Ca; } // Make a Col_str out of the Quark_line Col_str Cs; // Move color factor to multiply Cs instead of Ql Cs.Poly = Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); // Make two copies to store both terms Ca.ca.push_back(Cs); Ca.ca.push_back(Cs); // The first non-suppressed term, obtained by splitting the Quark_line Ca.ca.at(0) = split_Quark_line(Ca.ca.at(0).cs.at(0), j1, j2); // This is multiplying a factor 1/2 Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; Mon_tmp.pow_TR = 1; Ca.ca.at(0).Poly = Ca.ca.at(0).Poly * Mon_tmp; // The split can generate new (next to) neighbors //cout << "Looking for nn in" << Ca.at(0) << endl ; Ca.ca.at(0) = contract_next_neighboring_gluons(Ca.ca.at(0)); //cout << "contract_Ql_gluons(Ql): First term + non changed" << Ca << endl ; // The second Nc suppressed term, obtained by just removing gluon // remove g quark_line::iterator it1 = Ca.ca.at(1).cs.at(0).ql.begin() + j1; quark_line::iterator it2 = Ca.ca.at(1).cs.at(0).ql.begin() + j2; Ca.ca.at(1).cs.at(0).ql.erase(it2); Ca.ca.at(1).cs.at(0).ql.erase(it1); // Multiply with -TR/(Nc) //Mon_tmp.pow_2 = -1; Mon_tmp.pow_TR = 1; Mon_tmp.pow_Nc = -1; Mon_tmp.n_times = -1; // cout <<" Ca.at(1).Poly old" << Ca.at(1).Poly <<endl; //cout <<" Mon_tmp " << Mon_tmp <<endl; Ca.ca.at(1).Poly = Ca.ca.at(1).Poly * Mon_tmp; //cout << "contract_Ql_gluons(Ql): both terms" << Ca << endl ; //cout <<" Ca.at(1).Poly new" << Ca.at(1).Poly <<endl; // Change sign // A pair was found, use for stoping found_pair = true; } if (found_pair) break; // Stop j2 loop } if (found_pair) break; // Stop j1 loop } // If a pair was never found return the info of the original Ql // but with the Polynomial moved to the Col_str if(!found_pair){ Col_str Cs; Cs.Poly=Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); Ca.ca.push_back(Cs); } // Remove possible 0 and 1-rings Ca.remove_1_rings(); Ca.remove_0_rings(); // cout <<"contract_Ql_gluons(Ql): To return "; //std::cout.flush(); //cout << Ca << endl; //std::cout.flush(); return Ca; } // Function for contracting gluon indices within the same Quark_line -Col_amp Col_functions::contract_Ql_gluons(Col_amp & Ca) const{ +Col_amp Col_functions::contract_Quark_line_gluons(Col_amp & Ca) const{ //cout <<"contract_Ql_gluons(Ca): Incoming " << Ca << endl; //std::cout.flush(); // If Col_amp empty, do nothing if (Ca.ca.empty()) return Ca; // Loop over individual Col_str's for (uint m = 0; m < Ca.ca.size(); m++) { // In each Col_str, loop over individual Quark_lines for (uint i = 0; (m < Ca.ca.size() && i < Ca.ca.at(m).cs.size()); i++) { // the Ql to be contracted Quark_line Ql = Ca.ca.at(m).cs.at(i); //cout << "contract_Ql_gluons(Ca): Ql to be contracted for i= " << i << " " << Ql << endl; // Contract gluons in the Quark_line // If the ql had a Polynomial, this is contained in Ca_from_Ql Col_amp Ca_from_Ql; - Ca_from_Ql = contract_Ql_gluons(Ql); + Ca_from_Ql = contract_Quark_line_gluons(Ql); //cout <<"contract_Ql_gluons(Ca): after contract_Ql_gluons( Ql ) in Quark_line " << i << " in Col_str m "<< m << " " << Ca_from_Ql << endl; // The whole Col_str has to be replaced with a linear // combination of different Col_str's where the Ql has changed to // the different options // The rest of the Col_str, without changed Ql // If the whole Col_str (at m) had a Polynomial, this is contained in // Cs_rest Col_str Cs_rest; Ca.ca.at(m).cs.erase( Ca.ca.at(m).cs.begin() + i); Cs_rest = Ca.ca.at(m); //cout <<"contract_Ql_gluons(Ca): Cs_rest " << Cs_rest << endl; // The rest of the Ca, without affected Cs Ca.erase(m); // Append Quark_line(s) to the rest of the Col_str, // Loop over Col_str's in Ca_from_Ql for (uint mq = 0; mq < Ca_from_Ql.ca.size(); mq++) { Col_str Cs_new = Cs_rest; // Append the col_str Cs_new.append(Ca_from_Ql.ca.at(mq).cs); //cout <<"contract_Ql_gluons(Ca): Cs_new from mq=" << mq << ", " << Cs_new << endl; // and multiply with the Polynomial Cs_new.Poly = Cs_new.Poly * Ca_from_Ql.ca.at(mq).Poly; //Append the new Col_str to the Ca Ca.ca.push_back(Cs_new); } // If the contraction gave a pure polynomial, this should also // give rise to a Col_str for (int mq = 0; mq < Ca_from_Ql.Scalar.size(); mq++) { // Unless the term in the Polynomial is 0 if( Ca_from_Ql.Scalar.at(mq).n_times != 0 ){ Col_str Cs_new = Cs_rest; // The Col_str contains no col_str, // but the old polynomial is multiplied with the Polynomial in Scalar Cs_new.Poly = Cs_new.Poly *Ca_from_Ql.Scalar.at(mq); // Problem when renaming Poly to Scalar? //cout <<"contract_Ql_gluons(Ca): Cs_new from mq=" << mq << ", " << Cs_new << endl; // Append the new Col_str to the Ca Ca.ca.push_back(Cs_new); } } //cout <<"contract_Ql_gluons(Ca): Ca after adding back terms " << Ca << endl; // If the contraction gave more than 1 term, // increase m too keep looking forward // TODO compensate for terms from Ca_from_Ql.Poly, m should change to keep // going forward if (Ca_from_Ql.ca.size() >= 2) m = m + Ca_from_Ql.ca.size() - 1; } } Ca.simplify(); //cout << "contract_Ql_gluons(Ca): To return " << Ca << endl; return Ca; } // Function for contracting gluon indices within the same Quark_line in Col_str -Col_amp Col_functions::contract_Ql_gluons( Col_str & Cs ) const{ +Col_amp Col_functions::contract_Quark_line_gluons( Col_str & Cs ) const{ Col_amp Ca; Ca.ca.push_back(Cs); - Ca= contract_Ql_gluons( Ca ); + Ca= contract_Quark_line_gluons( Ca ); return Ca; } // Contracts one gluon, the first g in first Quark_line // Treat only the case when the same g is found in different Ql Col_amp Col_functions::contract_a_gluon(Col_str & Cs) const{ //cout << "contract_a_gluon(Cs): Incoming " << Cs << endl; //If the Col_str is empty, or the first ql is empty return it as Col_amp if ( Cs.cs.empty() or Cs.cs.at(0).ql.empty() ) { Col_amp Ca; Cs.cs.clear(); Ca.ca.push_back(Cs); return Ca; } // Pick first gluon int the_g = Cs.at2(0, 0); std::vector<int> place; place.push_back(0); place.push_back(0); // For storing place of second gluon std::vector<int> place2; // Locate the same gluon for (uint i2 = 0; i2 < Cs.cs.size(); i2++) { for (uint j2 = 0; j2 < Cs.cs.at(i2).ql.size(); j2++) { //cout <<"i2 " << i2 <<" j2 " << j2 <<endl; // Make sure it is a different gluon if ((i2 != 0 or j2 != 0) && Cs.at2(i2, j2) == the_g) { place2.push_back(i2); place2.push_back(j2); } } } // If the gluon was not found, something went wrong if (place2.empty()) { std::cerr << "contract_a_gluon: The gluon " << the_g << " was only found once " << Cs; std::cerr.flush(); throw -1; } // If the gluon was found in same Ql, use contract_Ql_gluons if (place.at(0) == place2.at(0)) { Col_amp Ca_out; - Ca_out = contract_Ql_gluons(Cs); + Ca_out = contract_Quark_line_gluons(Cs); //cout << "contract_a_gluon(Cs): Gluon found twice in same Ql, returning contract_Ql_gluons( Cs ) " << Ca_out << endl; std::cout.flush(); //cout << "contract_a_gluon(Cs): To return" << Ca_out; return Ca_out; } // If the removed gluon was part of a two-ring if( Cs.cs.at(place.at(0)).ql.size()==2 or Cs.cs.at(place2.at(0)).ql.size()==2){ //cout << "contract_a_gluon(Cs): Found gluon in " << Cs << " is part of a two-ring "<< endl; Col_amp Ca_out; Cs=contract_2_rings(Cs); Ca_out.ca.push_back(Cs); //cout << "contract_a_gluon(Cs): After contracting two-rings " << Ca_out << endl; return Ca_out; } // The result is a sum of two terms, to contain these Col_str Cs1 = Cs; Col_str Cs2; // The Nc suppressed term, obtained by just removing the g // location of first and second g to remove quark_line::iterator it1 = Cs1.cs.at(place.at(0)).ql.begin(); quark_line::iterator it2 = Cs1.cs.at(place2.at(0)).ql.begin() + place2.at(1); // Remove the gluon in both places Cs1.cs.at(place2.at(0)).ql.erase(it2); Cs1.cs.at(place.at(0)).ql.erase(it1); // The non-suppressed term, obtained by joining the ql's (where the g is removed) Cs2 = Cs1; // Construct the contracted ql by // 1, taking the first part of the 2nd ql, by erasing everything after the erased gluon Quark_line ql_first_part=before( Cs.cs.at(place2.at(0)), place2.at(1) ); // 2, the inbetween part=first ql, the contracted gluon is already erased Quark_line middle_part=Cs2.cs.at(place.at(0)); // 3, Last part equal to second part of 2nd ql Quark_line last_part=after( Cs.cs.at(place2.at(0)), place2.at(1) ); // The new combined Ql, to replace the 2nd involved Qualk_line Quark_line new_Ql=ql_first_part; new_Ql.append( middle_part.ql ); new_Ql.append( last_part.ql ); // Copy polynomial info new_Ql.Poly=Cs.cs.at(place2.at(0)).Poly; // Replace the first involved Ql with the constructed Ql Cs2.cs.at(0)=new_Ql; //cout << "The contracted Ql is " << new_Ql<< endl; /* // Insert second term after first for (uint i = 0; i < Cs1.cs.at(place2.at(0)).ql.size(); i++) { Cs2.cs.at(0).ql.push_back(Cs2.cs.at(place2.at(0)).ql.at(i)); } */ // erase moved ql col_str::iterator it = Cs2.cs.begin() + place2.at(0); Cs2.cs.erase(it); //cout <<"Cs2 " << Cs2 << endl; // Multiply with TR for non-suppressed term Monomial Mon_tmp; //Mon_tmp.pow_2 = -1; Mon_tmp.pow_TR = 1; Cs2.Poly = Cs2.Poly * Mon_tmp; // Multiply with -TR/Nc for suppressed term Mon_tmp.pow_Nc = -1; Mon_tmp.n_times = -1; Cs1.Poly = Cs1.Poly * Mon_tmp; // Look for simple simplifications in Cs1 // This has to be done after Cs1 is used to construct Cs2 // After the removal there may be new next to neighbors in the affected ql's // First affected Ql // look forward at nn Cs1.cs.at(place.at(0)) = contract_neighboring_gluons(Cs1.cs.at(place.at(0)), place.at(0)); // look backward at nn Cs1.cs.at(place.at(0)) = contract_neighboring_gluons(Cs1.cs.at(place.at(0)), place.at(0) - 1); // look forward at next nei Cs1.cs.at(place.at(0)) = contract_next_neighboring_gluons(Cs1.cs.at(place.at(0)), place.at(0)); // look backward at next nei Cs1.cs.at(place.at(0)) = contract_next_neighboring_gluons(Cs1.cs.at(place.at(0)), place.at(0) - 2); // Second affected ql // look forward at nn Cs1.cs.at(place2.at(0)) = contract_neighboring_gluons(Cs1.cs.at(place2.at(0)), place2.at(0)); // look backward at nn Cs1.cs.at(place2.at(0)) = contract_neighboring_gluons(Cs1.cs.at(place2.at(0)), place2.at(0) - 1); // look forward at next nei Cs1.cs.at(place2.at(0)) = contract_next_neighboring_gluons(Cs1.cs.at(place2.at(0)), place2.at(0)); // look backward at next nei Cs1.cs.at(place2.at(0)) = contract_next_neighboring_gluons(Cs1.cs.at(place2.at(0)), place2.at(0) - 2); Col_amp Ca_out; Ca_out.ca.push_back(Cs1); Ca_out.ca.push_back(Cs2); //cout << "contract_a_gluon(Cs): The resulting Ca before removing 1 and 0-rings " << Ca_out << endl; // remove rings with just one gluon (=0) Ca_out.remove_1_rings(); // remove rings with no partons (=Nc, if closed) Ca_out.remove_0_rings(); //cout << "contract_a_gluon(Cs): The resulting Ca before normal_ordering " << Ca_out << endl; std::cout.flush(); //Ca_out.normal_order(); //cout << "contract_a_gluon(Cs): The Ca to return is " << Ca_out << endl; std::cout.flush(); return Ca_out; } // Contracts one gluon, the first g in first Quark_line (in each Col_str) Col_amp Col_functions::contract_all_gluons(Col_str & Cs) const{ //cout << "contract_all_gluons( Cs ): Incoming, " << Cs << endl; Col_amp Ca; Ca.ca.push_back(Cs); return contract_all_gluons(Ca); } Col_amp Col_functions::contract_a_gluon(Col_amp & Ca ) const{ //cout << "on(Ca): Incoming " << Ca << endl; // If the Ca has only an empty Col_str or a Col_str with an empty Quark_line, do nothing if(Ca.ca.size()==1 && ( Ca.ca.at(0).cs.empty() or Ca.ca.at(0).cs.at(0).ql.empty())) return Ca; // Else collect contracted Col_amps in a variable Ca_res Col_amp Ca_res; Ca_res.Scalar=Ca.Scalar; // Move Polynomial // Make one contraction in each Col_str, move Col_str's when done to Ca_res while ( Ca.ca.size() > 0) { // Contract one gluon in Col_str Col_amp Ca_part=contract_a_gluon( Ca.ca.at(0) ); //cout << "contract_a_gluon(Ca): Ca_part: " <<Ca <<endl; // ... and add resulting Col_amp to Ca_res Ca_res=Ca_res+Ca_part; // Erase old term (still at place 0) Ca.erase(0); } //cout << "contract_a_gluon(Ca): To return: " <<Ca <<endl;; return Ca_res; } // Function for contracting all gluon indices in a Col_str, // Assumes quarks already contracted Col_amp Col_functions::contract_all_gluons(Col_amp & Ca) const{ //cout << "contract_all_gluons( Ca ): Incoming, " << Ca << endl; // Make sure the Col_strs are not empty "[]"=1, as all indices contracted Ca.remove_empty_Col_strs(); Ca.remove_empty_Col_strs(); // Check that all quark indices have been removed if ( !Ca.gluons_only() ) { std::cerr << "contract_all_gluons(Cs): Error, all quark indices where not contracted in " << Ca << std::endl; std::cerr.flush(); throw -1; } // First normal order and look for simple simplification Ca.simplify(); Col_amp Ca_old; // For comparison // Contract remaining gluons as long as result keep changing // Start with contractions only giving one term while (Ca_old != Ca) { Ca_old = Ca; // Contract gluons from 2-ring as this gives only one term // and may result in new neighbors or next to neighbors Ca = contract_2_rings(Ca); //cout << "contract_all_gluons(Ca): contract_2_rings(Ca) done, with result Ca=" << Ca << endl; // Remove neighboring and next to neighboring gluon indices // in each quark_line as this gives only one term Ca = contract_next_neighboring_gluons(Ca); // cout << "contract_all_gluons(Ca): contract_next_neighboring_gluons done, with result Ca=" // << Ca << endl; // Temporarely added to see if problem with empty Ql's disappeare //Ca.simplify(); // Contract gluons within same quark_line // This will give >1 term, but at least rings will be shorter and shorter - Ca = contract_Ql_gluons( Ca ); + Ca = contract_Quark_line_gluons( Ca ); // Recently introduces to maximize number of cf's Ca = contract_next_neighboring_gluons(Ca); // Make "arbitrary" gluon contraction when no simple or Ql contraction remains // In each Col_str, contract the first gluon in the first Ql //cout << endl << "contract_all_gluons(Ca): Ca before contract_a_gluon(Ca) " << Ca <<endl; Ca = contract_a_gluon(Ca); //cout.precision(20); //cout.setf(ios_base::fixed ); //cout << endl << "contract_all_gluons(Ca): Ca after contract_a_gluon(Ca) " << Ca <<endl; //cout << "contract_all_gluons: Ca_old " << Ca_old << endl; // Look if same Col_str is represented more than once in the col_amp and simplify Ca.simplify(); } //end of while (Ca keeps changing) //cout << "contract_all_gluons(Ca): Returning: " << Ca << endl; return Ca; } // Function for finding the leading power of Nc in a Polynomial int Col_functions::leading_Nc_pow( const Polynomial & Poly) const{ if( Poly.poly.empty()) { - if( d_num(Poly)!=0 ) return 0; + if( double_num(Poly)!=0 ) return 0; else return std::numeric_limits<int>::min(); } int leading_pow=Poly.at(0).pow_Nc + Poly.at(0).pow_cf; // Loop over non-zero Monomials for (uint i=0; i<Poly.poly.size(); i++){ if( Poly.at(i).pow_Nc + Poly.at(i).pow_cf > leading_pow && Poly.at(i).n_times!=0 ) leading_pow=Poly.at(i).pow_Nc + Poly.at(i).pow_cf; } return leading_pow; } // Function for finding the leading power of Nc in a Polynomial int Col_functions::leading_Nc_pow( const poly_vec & Pv) const{ int leading_pow=leading_Nc_pow( Pv.at(0) ); // Loop over Polynomials for (uint p=0; p<Pv.size(); p++){ if ( leading_Nc_pow( Pv.at(p) ) > leading_pow ) leading_pow=leading_Nc_pow( Pv.at(p) ); } return leading_pow; } // Function for finding the leading power of Nc among the Polynomials a vector // of Polynomial pointers points to int Col_functions::leading_Nc_pow( std::vector< std::tr1::shared_ptr<Polynomial> > Pvp) const{ int leading_pow=leading_Nc_pow( *(Pvp.at(0)) ); // Loop over Polynomials for (uint p=1; p<Pvp.size(); p++){ if ( leading_Nc_pow( *(Pvp.at(p)) ) > leading_pow ) leading_pow=leading_Nc_pow( *(Pvp.at(p)) ); } return leading_pow; } // Takes the leading Nc terms of a Polynonmial, i.e. Monomials with highest power of Nc+cf // Replaces Cf or not depending on the value of the global parameter leading_cf. Polynomial Col_functions::leading( const Polynomial & Poly, bool lcf ) const{ // Candidate for highest power of Nc+cf int cand_pow = Poly.at(0).pow_Nc + Poly.at(0).pow_cf; // For containing the leading Nc Polynomial Polynomial leading_pol; leading_pol.push_back(Poly.at(0)); // Looks for Monomials with highest Nc power for (int k = 0; k < Poly.size(); k++) { // If power higher if ((Poly.at(k).pow_Nc + Poly.at(k).pow_cf) > cand_pow) { cand_pow = Poly.at(k).pow_Nc + Poly.at(k).pow_cf; leading_pol.clear(); leading_pol = leading_pol * 0; leading_pol = leading_pol + Poly.at(k); } // If power equal else if (Poly.at(k).pow_Nc + Poly.at(k).pow_cf == cand_pow) { leading_pol = leading_pol + Poly.at(k); } } // Comment the below out to keep cf, in this way the setting for cf in num matters also // for after leading is taken. cf is then replaced by cf(Nc), rather than just the leading term - // As this removes all powers of cf the setting for cf in c_num( Polynomial ) matters + // As this removes all powers of cf the setting for cf in cnum_num( Polynomial ) matters if (leading_cf || lcf) { // Loop over terms and replace Cf with the Nc -> infinity limit of cf =Nc/2 for (int i = 0; i < leading_pol.size(); i++) { int pow_cf = leading_pol.at(i).pow_cf; leading_pol.at(i).pow_Nc += pow_cf; //leading_pol.at(i).pow_2 -= pow_cf; changed 121218 leading_pol.at(i).pow_TR += pow_cf; leading_pol.at(i).pow_cf = 0; } } leading_pol.simplify(); return leading_pol; } // Take the leading part of a poly_vec poly_vec Col_functions::leading(const poly_vec & Pv) const{ // To contain the result poly_vec Pv_res; // Loop over entries in vector (Polynomials), and take the leading part of each Polynomial // after this all Monomials in the same Polynomial has the same Nc-power for (uint i = 0; i < Pv.size(); i++) { // Take the leading terms in each component Pv_res.push_back( leading( Pv.at(i) ) ); } // Find the leading power int leading_pow = leading_Nc_pow(Pv_res); // Loop over entries and keep only those with maximal power for (uint p = 0; p < Pv_res.size(); p++) { if (leading_Nc_pow(Pv_res.at(p)) != leading_pow) { Pv_res.at(p) = 0 * Pv_res.at(p); Pv_res.at(p).simplify(); } } return Pv_res; } // Take the leading part of a poly_matr, the highest Nc powers in the whole matrix poly_matr Col_functions::leading(const poly_matr & Pm) const{ // To contain the result poly_matr Pm_res; // Loop over poly_vecs, and take the leading part of each poly_vec for (uint i = 0; i < Pm.size(); i++) { // Take the leading terms in each poly_vec Pm_res.push_back( leading(Pm.at(i)) ); } int cand_pow = leading_Nc_pow(Pm.at(0)); // Loop over poly_vecs to locate highest power for (uint k = 0; k < Pm_res.size(); k++) { if (leading_Nc_pow(Pm_res.at(k)) > cand_pow) cand_pow = leading_Nc_pow(Pm_res.at(k)); } // In all Polynomials, in all poly_vec's, // put all elements which doesn't have highest power to 0 for (uint k = 0; k < Pm_res.size(); k++) { // In each poly_vec, loop over all Polynomials for (uint p = 0; p < Pm_res.at(k).size(); p++) { if (leading_Nc_pow(Pm_res.at(k).at(p)) != cand_pow) { Pm_res.at(k).at(p) = Pm_res.at(k).at(p) * 0; Pm_res.at(k).at(p).simplify(); } } } return Pm_res; } // Take the leading part of a poly_vec when given a pointer to it poly_vec Col_functions::leading( std::vector<std::tr1::shared_ptr<Polynomial> > Pvp) const{ //cout << "leading(std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > >Pm): Entering" << endl; poly_vec Pv_res; // Loop over entries in vector (poly_vecs), and take the leading part of each Polynomial // after this each Monomial has the same Nc+cf-power for (uint i = 0; i < Pvp.size(); i++) { // Take the leading terms in each component std::tr1::shared_ptr<Polynomial> the_pointer=Pvp.at(i); Pv_res.push_back( leading( (*the_pointer)) ); } // Find the leading power int leading_pow = leading_Nc_pow(Pv_res); // Loop over entries and keep only those with maximal power for (uint p = 0; p < Pv_res.size(); p++) { if (leading_Nc_pow(Pv_res.at(p)) != leading_pow) { Pv_res.at(p) = 0 * Pv_res.at(p); Pv_res.at(p).simplify(); } } return Pv_res; } // Take the leading part of a poly_matr, the highest Nc powers in the whole matrix dmatr Col_functions::leading( std::vector< std::vector< std::tr1::shared_ptr<Polynomial> > > Ppm) const { std::cout << "leading(std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > >Pm): Finding leading terms in matrix..." << std::endl; // To contain the result as a matrix of double dmatr res; res.reserve( Ppm.size() ); int cand_pow = leading_Nc_pow( Ppm.at(0) ); // Loop over poly_vecs to locate highest power for (uint k = 0; k < Ppm.size(); k++) { int the_pow=leading_Nc_pow(Ppm.at(k)); if ( the_pow > cand_pow ) cand_pow = the_pow; } //cout << "Found highest power " << cand_pow << endl; // In all Polynomials, in all poly_vec's, // put all elements which doesn't have highest power to 0 for (uint k = 0; k < Ppm.size(); k++) { //cout << "k is now " << k << endl; dvec dummy; res.push_back(dummy); // Not to run out of range // In each poly_vec, loop over all Polynomials for (uint p = 0; p < Ppm.at(k).size(); p++) { Polynomial the_poly=*Ppm.at(k).at(p); leading_Nc_pow( the_poly ); if ( leading_Nc_pow( the_poly ) != cand_pow ) {res.at(k).push_back(0);} - else res.at(k).push_back( d_num(leading(the_poly)) ); + else res.at(k).push_back( double_num(leading(the_poly)) ); } } std::cout << " done." << std::endl; return res; } /// To keep only leading terms in a map. std::map< std::string, Polynomial > Col_functions::leading( std::map< std::string, Polynomial > mem_map ) const{ //cout << "mem_map.size() " << mem_map.size() << endl; // To contain (string, leading(Polynomial)) std::map< std::string, Polynomial > res; // Iterator for looping //std::map< string, tr1::shared_ptr<Polynomial> >::iterator iter=mem_map.begin(); std::map< std::string, Polynomial >::iterator iter=mem_map.begin(); // Find the highest power of Nc+cf //cout << "The poly is " << mem_map[ static_cast<string>(iter->first) ] << endl; //cout << "The poly is " << (iter->second) << endl; int pow_cand=leading_Nc_pow( (iter->second) ); //cout << "pow_cand " << pow_cand; for( iter = mem_map.begin(); iter != mem_map.end(); ++iter ) { //cout << iter-> first <<endl; //cout.flush(); Polynomial Poly= (iter->second); if( leading_Nc_pow(Poly) > pow_cand ) pow_cand=leading_Nc_pow(Poly); } //cout << "Winning pow_cand " << pow_cand; for( iter = mem_map.begin(); iter != mem_map.end(); ++iter ) { //cout << iter-> first <<endl; // Insert pair of string and the leading versions of the Polynomial Polynomial lead_Poly= leading((iter->second)); if ( leading_Nc_pow(lead_Poly) != pow_cand ) lead_Poly=lead_Poly*0; //tr1::shared_ptr<Polynomial> ptr=shared_ptr<Polynomial> (new Polynomial(lead_Poly)); res.insert( make_pair( iter->first, lead_Poly ) ); } // After this only the leading part of each Polynomial contributes return res; } // Returns the Polynomial version of Poly where cf has been removed. Polynomial Col_functions::remove_cf( const Polynomial & Poly ) const{ // To contain the result Polynomial Poly_res=Poly; // Remove cf in the copy Poly_res.remove_cf(); return Poly_res; } // Remove cf in a poly_vec. poly_vec Col_functions::remove_cf( const poly_vec & Pv) const{ // To contain the result poly_vec Pv_res=Pv; // Loop over entries in vector (Polynomials) for ( uint i = 0; i < Pv.size(); i++ ) { // Remove cf in each term Pv_res.at(i).remove_cf(); //std::cout << "Col_functions::remove_cf: Pv_res.at(i)" << Pv_res.at(i) << std::endl; } return Pv_res; } // Remove cf in a poly_matr. poly_matr Col_functions::remove_cf( const poly_matr & Pm) const{ //std::cout << "Col_functions::remove_cf: Hi!" << std::endl; // To contain the result poly_matr Pm_res=Pm; // Loop over entries in vector (poly_vec) for ( uint i = 0; i < Pm.size(); i++ ) { // Remove cf in each term Pm_res.at(i)=remove_cf( Pm_res.at(i) ); //std::cout << "Col_functions::remove_cf: Pm_res.at(i)" << Pm_res.at(i) << std::endl; } //std::cout << "Col_functions::remove_cf: Pm_res.at(i)" <<std::cout << Pm_res; return Pm_res; } // Returns the Polynomial version of Poly where the Monomials // have been normal ordered. // In this function Poly itself is not changed, but a copy is made and changed. // (Uses the normal_order member function in Polynomial). Polynomial Col_functions::normal_order( const Polynomial & Poly ) const{ // To contain the result Polynomial Poly_res=Poly; // Normal order the copy Poly_res.normal_order(); return Poly_res; } // Normal order all polynomials in a poly_vec. // (Uses the the normal_order member function in Polynomial). poly_vec Col_functions::normal_order( const poly_vec & Pv) const{ // To contain the result poly_vec Pv_res=Pv; // Loop over entries in vector (Polynomials) for ( uint i = 0; i < Pv.size(); i++ ) { // Normal order each term Pv_res.at(i).normal_order(); } return Pv_res; } // Normal_order all Polynomials in a poly_matr. // (Uses the the normal_order member function in Polynomial). poly_matr Col_functions::normal_order( const poly_matr & Pm) const{ // To contain the result poly_matr Pm_res=Pm; // Loop over entries in vector (poly_vec) for ( uint i = 0; i < Pm.size(); i++ ) { // Remove cf in each term Pm_res.at(i)=normal_order( Pm_res.at(i) ); } return Pm_res; } - +/* // Returns the simplified Polynomial. // In this function Poly itself is not changed, but a copy is made and changed. // (Uses the simplify member function in Polynomial). Polynomial Col_functions::simplify( const Polynomial & Poly ) const{ // To contain the result Polynomial Poly_res=Poly; // Normal order the copy Poly_res.simplify(); return Poly_res; } +*/ // Returns the simplified poly_vec. // In this function poly_vec itself is not changed, but a copy is made and changed. // (Uses the simplify member function in Polynomial). poly_vec Col_functions::simplify( const poly_vec & Pv) const{ // To contain the result poly_vec Pv_res=Pv; // Loop over entries in vector (Polynomials) for ( uint i = 0; i < Pv.size(); i++ ) { // Normal order each term Pv_res.at(i).simplify(); } return Pv_res; } // Normal_order all Polynomials in a poly_matr. // In this function poly_matr itself is not changed, but a copy is made and changed. // (Uses the simplify member function in Polynomial). poly_matr Col_functions::simplify( const poly_matr & Pm) const{ // To contain the result poly_matr Pm_res=Pm; // Loop over entries in vector (poly_vec) for ( uint i = 0; i < Pm.size(); i++ ) { // Remove cf in each term Pm_res.at(i)=simplify( Pm_res.at(i) ); } return Pm_res; } // Numerically evaluates a Monomial to a complex cnum number. -cnum Col_functions::c_num(const Monomial & Mon ) const { +cnum Col_functions::cnum_num(const Monomial & Mon ) const { //cnum res=pow(Nc, Mon.pow_Nc)*pow(cf, Mon.pow_cf)*pow(2.0, Mon.pow_2)*Mon.n_times* Mon.num; // changed 121218 cnum res=pow(Nc, Mon.pow_Nc)*pow(cf, Mon.pow_cf)*pow(TR, Mon.pow_TR)*Mon.n_times* Mon.num; return res; } // Numerically evaluates a Monomial to a double. -double Col_functions::d_num( const Monomial & Mon ) const{ - - double ratio =imag( c_num(Mon) )/real( c_num(Mon) ); +double Col_functions::double_num( const Monomial & Mon ) const{ + + double ratio =imag( cnum_num(Mon) )/real( cnum_num(Mon) ); // Warn if the complex number has significant imaginary parts if( ratio > accuracy ) std::cerr << "Warning keeping only real part of complex number, the ratio im/re was " << ratio << std::endl; - return real( c_num(Mon) ); + return real( cnum_num(Mon) ); } // Numerically evaluates a Polynomial for Nc=3 -cnum Col_functions::c_num(const Polynomial & Poly) const { - //std::cout << "Col_functions::c_num: Called with cf " << cf << endl; +cnum Col_functions::cnum_num(const Polynomial & Poly) const { + //std::cout << "Col_functions::cnum_num: Called with cf " << cf << endl; // An empty Polynomial has numerical value 0 cnum res = 0; // Add contributions from Monomials for (int k = 0; k < Poly.size(); k++) { cnum part_k; part_k = pow(Nc, Poly.at(k).pow_Nc) * // The cf below is relevant only if cf has not been replaced in leading(Polynomial) pow(cf, Poly.at(k).pow_cf) * //pow( 4.0/3.0, Poly.at(k).pow_cf )* //pow(2.0, Poly.at(k).pow_2) * Poly.at(k).n_times // changed 121218 pow(TR, Poly.at(k).pow_TR) * Poly.at(k).n_times * Poly.at(k).num; - //cout << "c_num(Poly): Part k is "<< part_k << endl; + //cout << "cnum_num(Poly): Part k is "<< part_k << endl; res = res + part_k; } return res; } // Numerically evaluates a Polynomial for Nc=3 -double Col_functions::d_num( const Polynomial & Poly) const{ - - double ratio =imag( c_num(Poly) )/real( c_num(Poly) ); +double Col_functions::double_num( const Polynomial & Poly) const{ + + double ratio =imag( cnum_num(Poly) )/real( cnum_num(Poly) ); // Warn if the complex number has significant imaginary parts if( ratio > accuracy ) std::cerr << "Warning keeping only real part of complex number, the ratio im/re was " << ratio << std::endl; - return real( c_num(Poly) ); + return real( cnum_num(Poly) ); } // Keeping only leading terms, numerically evaluate a Polynomial -cnum Col_functions::leading_c_num(const Polynomial & Poly) const{ +cnum Col_functions::leading_cnum_num(const Polynomial & Poly) const{ cnum res=0; // Keep only leading terms in the polynomial // Poly=leading( Poly ); Polynomial leading_Poly=leading( Poly ); // Add contributions from remaining leading Monomials, putting for ( int k=0; k < leading_Poly.size(); k++ ){ cnum part_k; part_k=pow( Nc, leading_Poly.at(k).pow_Nc )* pow( cf, leading_Poly.at(k).pow_cf )* //putting should have no effect if cf removed using leading //pow(2.0, leading_Poly.at(k).pow_2)* // changed 121218 pow(TR, leading_Poly.at(k).pow_TR)* leading_Poly.at(k).n_times* leading_Poly.at(k).num; - //cout << "c_num(Poly): Part k is "<< part_k << endl; + //cout << "cnum_num(Poly): Part k is "<< part_k << endl; res=res+part_k; } return res; } // To take the numerical value of a map -//std::map< string, double > d_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) { -std::map< std::string, double > Col_functions::d_num( std::map< std::string, Polynomial > mem_map ) const{ - - //cout << "d_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) : Entering... " << endl; +//std::map< string, double > double_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) { +std::map< std::string, double > Col_functions::double_num( std::map< std::string, Polynomial > mem_map ) const{ + + //cout << "double_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) : Entering... " << endl; std::map< std::string, double > res; // Loop over entries //std::map< string, tr1::shared_ptr<Polynomial> >::iterator iter=mem_map.begin(); std::map< std::string, Polynomial >::iterator iter=mem_map.begin(); for( iter = mem_map.begin(); iter != mem_map.end(); ++iter) { //cout << "Hi " << endl; // Insert pair of string and the leading versions of the Polynomial - d_num( (iter->second) ); - res.insert(std::make_pair( iter->first, d_num((iter->second)) )); + double_num( (iter->second) ); + res.insert(std::make_pair( iter->first, double_num((iter->second)) )); } - //cout << "d_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) : Done." << endl; + //cout << "double_num(std::map< string, tr1::shared_ptr<Polynomial> > mem_map) : Done." << endl; return res; } // Numerically evaluates a Polynomial for Nc=3, // and stores in the format of a Polynomial with only one term with only a numerical part -Polynomial Col_functions::polynomial_c_num(const Polynomial & Poly) const{ +Polynomial Col_functions::Polynomial_cnum_num(const Polynomial & Poly) const{ // Store content in numerical part of the Monomial Monomial Mon; - Mon.num = c_num(Poly); + Mon.num = cnum_num(Poly); Polynomial res; res = res * Mon; return res; } // Numerically evaluates a poly_vec (vector of Polynomial) for Nc=3 -cvec Col_functions::c_num( const poly_vec & Pv) const{ +cvec Col_functions::cnum_num( const poly_vec & Pv) const{ // To contain the numerical result cvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { - res.push_back(c_num(Pv.at(p))); + res.push_back(cnum_num(Pv.at(p))); } return res; } // Numerically evaluates a poly_vec (vector of Polynomial) for Nc=3 -dvec Col_functions::d_num(const poly_vec & Pv) const{ - //cout << "d_num(Pv): Incoming Pv \n" << Pv; +dvec Col_functions::double_num(const poly_vec & Pv) const{ + //cout << "double_num(Pv): Incoming Pv \n" << Pv; // To contain the numerical result dvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { - res.push_back(d_num( Pv.at(p)) ); + res.push_back(double_num( Pv.at(p)) ); } return res; } // Numerically evaluates a poly_vec (vector of Polynomial). -dmatr Col_functions::d_num(const poly_matr & Pm) const{ +dmatr Col_functions::double_num(const poly_matr & Pm) const{ // To contain the numerical result dmatr res; // Loop over Polyvecs in the vector, and add the numerical value // to the vector to return for (uint pv = 0; pv < Pm.size(); pv++) { - res.push_back(d_num( Pm.at(pv)) ); + res.push_back(double_num( Pm.at(pv)) ); } return res; } // Returns a double value. The argument is a vector of pointers to Polynomials. -dvec Col_functions::d_num(const std::vector<std::tr1::shared_ptr<Polynomial> > & Pv) const{ - //cout << "d_num(Pv): Incoming Pv \n" << Pv; +dvec Col_functions::double_num(const std::vector<std::tr1::shared_ptr<Polynomial> > & Pv) const{ + //cout << "double_num(Pv): Incoming Pv \n" << Pv; // To contain the numerical result dvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { std::tr1::shared_ptr<Polynomial> the_pointer=Pv.at(p); // Want double of the polynomial which the pointer points at - res.push_back( d_num(*the_pointer) ); + res.push_back( double_num(*the_pointer) ); } return res; } // Numerically evaluates a poly_vec (vector of Polynomial) for Nc=3 // and stores in the form of a Polynomial -poly_vec Col_functions::poly_vec_c_num(const poly_vec & Pv) const{ +poly_vec Col_functions::poly_vec_cnum_num(const poly_vec & Pv) const{ // To contain the result poly_vec res; // Loop over Polynomials in the vector, put each polynomial to its numerical value for (uint p = 0; p < Pv.size(); p++) { - res.push_back( polynomial_c_num( Pv.at( p ) ) ); + res.push_back( Polynomial_cnum_num( Pv.at( p ) ) ); } return res; } // Numerically evaluates a poly_matr (vector of poly_vec) for Nc=3 // and stores in the form of a poly_matr -poly_matr Col_functions::poly_matr_c_num(const poly_matr & Pm ) const { +poly_matr Col_functions::poly_matr_cnum_num(const poly_matr & Pm ) const { // To contain the result poly_matr res_matr; // Loop over Polynomials in the matrix // and change each Polynomial to its numerical version for (uint v = 0; v < Pm.size(); v++) { - res_matr.push_back( poly_vec_c_num( Pm.at( v ) )); + res_matr.push_back( poly_vec_cnum_num( Pm.at( v ) )); } return res_matr; } // Numerically evaluates a poly_matr (vector of poly_vec) for Nc=3 -cmatr Col_functions::c_num( const poly_matr & Pm ) const{ +cmatr Col_functions::cnum_num( const poly_matr & Pm ) const{ // To contain the numerical result cmatr res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint v = 0; v < Pm.size(); v++) { - res.push_back(c_num(Pm.at(v))); + res.push_back(cnum_num(Pm.at(v))); } return res; } -dmatr Col_functions::d_num( const std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > > & Pm) const{ - //cout << " d_num(Pm): Incoming matrix \n" << Pm; +dmatr Col_functions::double_num( const std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > > & Pm) const{ + //cout << " double_num(Pm): Incoming matrix \n" << Pm; // To contain the numerical result dmatr res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint v = 0; v < Pm.size(); v++) { - res.push_back(d_num(Pm.at(v))); + res.push_back(double_num(Pm.at(v))); } return res; } /* // Numerically evaluates a poly_matr (vector of poly_vec) for Nc=3 -cmatr leading_c_num(poly_matr Pm) { +cmatr leading_cnum_num(poly_matr Pm) { // To contain the numerical result cmatr res; - res=c_num( leading( Pm ) ); + res=cnum_num( leading( Pm ) ); return res; } */ // A function to rename the indices in two Col_strs, such that in the first // they are called 1,2,3..., and in the second the relative order is kept std::pair<Col_str,Col_str> Col_functions::rename_indices(const Col_str & Cs1, const Col_str & Cs2) const{ std::cout << "rename_indices: incoming Col_strs" << std::endl << Cs1 << Cs2; // To contain the new Cs1 Col_str Cs1_new(Cs1); // To contain the new Cs2 Col_str Cs2_new=(Cs2); // Loop over Quark_lines in Cs1 int new_ind=0; // The new index for(uint i=0; i< Cs1_new.cs.size(); i++ ){ //cout << i << endl; //cout.flush(); // Loop over indices in the Quark_line for(uint j=0; j< Cs1_new.cs.at(i).ql.size(); j++ ){ new_ind++; // The old index in Cs1, to be replaced also in Cs2 int old_ind=Cs1_new.cs.at(i).ql.at(j); // Replace index in Cs1 Cs1_new.cs.at(i).ql.at(j)=new_ind; // Locate and replace index in Cs2 std::vector<int> place_in_2= Cs2_new.find_parton( old_ind ); Cs2_new.cs.at( place_in_2.at(0) ).ql.at( place_in_2.at(1) )=new_ind; } } //cout << "rename_indices: outgoing Col_strs" << endl << Cs1 << Cs2_new; return std::make_pair(Cs1_new,Cs2_new); } // A function to rename the indices in two Col_amps, such that in the first // Col_str in the first Col_amp they are called 1,2,3..., // and in the other Col_strs the relative order is kept. std::pair<Col_amp,Col_amp> Col_functions::rename_indices( Col_amp Ca1, Col_amp Ca2 ) const{ //cout << "rename_indices: incoming Col_strs" << endl << Cs1 << Cs2; // To contain the new Ca2 Col_amp Ca2_new=Ca2; // The Col_str to define the ordering Col_str Cs1=Ca1.ca.at(0); // Loop over Quark_lines in Cs1 int new_ind=0; // The new index for(uint i=0; i< Cs1.cs.size(); i++ ){ //cout << i << endl; //cout.flush(); // Loop over indices in the Quark_line for(uint j=0; j< Cs1.cs.at(i).ql.size(); j++ ){ new_ind++; // The old index in Cs1, to be replaced also in Cs2 int old_ind=Cs1.cs.at(i).ql.at(j); // Replace index in Cs1 Cs1.cs.at(i).ql.at(j)=new_ind; // Locate and replace index in all other Col_strs // Do the replacement in all Col_strs in Ca1 for(uint csi=0; csi < Ca1.ca.size(); csi++){ std::vector<int> place= Ca1.ca.at(csi).find_parton( old_ind ); Ca1.ca.at(csi).cs.at( place.at(0) ).ql.at( place.at(1) )=new_ind; } // Do the replacement in all Col_str in Ca2 for(uint csi=0; csi < Ca2.ca.size(); csi++){ std::vector<int> place= Ca2.ca.at(csi).find_parton( old_ind ); Ca2_new.ca.at(csi).cs.at( place.at(0) ).ql.at( place.at(1) )=new_ind; } } } //cout << "rename_indices: outgoing Col_strs" << endl << Cs1 << Cs2_new; return std::make_pair(Ca1,Ca2_new); } // Function for calculating scalar products between Col_amps Polynomial Col_functions::scalar_product(const Col_amp & Ca1 , const Col_amp & Ca2 ) const{ //cout <<"scalar_product(Ca1, Ca2): Entering " << endl; // To contain the result Col_amp Ca_res; // Contract the quarks Ca_res = contract_quarks(Ca1, Ca2); //cout << "scalar_product: After contract_quarks(Ca1, Ca2) " << Ca_res << endl; Ca_res.simplify(); //cout << "scalar_product: After contract_quarks(Ca1, Ca2) " << Ca_res << endl; // Look for simple simplifications Ca_res.simplify(); // Contract the gluons Ca_res = contract_all_gluons(Ca_res); //cout << "scalar_product: After contract_all_gluons(Cs_tmp ) " << Ca_res << endl; //Ca_res.remove_1_rings(); //Ca_res.remove_0_rings(); if (!Ca_res.ca.empty()) { // if( non_contracted ){ std::cerr << "Col_functions::scalar_product: terminating due to non-contracted indices." << std::endl; std::cerr << "The col_amp is " << Ca_res << std::endl; std::cerr.flush(); throw -1; } else return Ca_res.Scalar; } // Alternative function for calculating scalar products between Col_amps Polynomial Col_functions::scalar_product2(const Col_amp & Ca1 , const Col_amp & Ca2 ) const{ //cout <<"scalar_product(Ca1, Ca2): Entering " << endl; // To contain the result Polynomial Poly_res; Poly_res=Poly_res*0; // Loop over Col_strs for (uint cs1=0; cs1< Ca1.size(); cs1++){ for (uint cs2=0; cs2< Ca2.size(); cs2++){ // Calculate scalar product of Col_sts Poly_res = Poly_res+scalar_product( Ca1.at(cs1), Ca2.at(cs2) ); } } - double num_before=d_num(Poly_res); + double num_before=double_num(Poly_res); Poly_res.simplify(); - double num_after=d_num(Poly_res); + double num_after=double_num(Poly_res); if( abs(num_before - num_after) > accuracy ){ - std::cout <<"scalar_product2(Ca1, Ca2): Before simplification " << c_num(Poly_res) << std::endl; - std::cout <<"scalar_product2(Ca1, Ca2): After simplification " << c_num(Poly_res) << std::endl; + std::cout <<"scalar_product2(Ca1, Ca2): Before simplification " << cnum_num(Poly_res) << std::endl; + std::cout <<"scalar_product2(Ca1, Ca2): After simplification " << cnum_num(Poly_res) << std::endl; } return Poly_res; } // Function for calculating scalar product matrix dmatr Col_functions::scalar_product_matrix_mem(const Col_amp & Basis) const{ // To contain the resulting scalar product matrix std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > > sp_m; // For remembering already calculated topologies std::map< std::string, std::tr1::shared_ptr<Polynomial> > mem_map; // Allocate memory as scalar product matrices can get large sp_m.reserve(Basis.ca.size()); // Loop over basis vectors in Basis for ( uint i = 0; i < Basis.ca.size(); i++ ) { std::vector<std::tr1::shared_ptr<Polynomial> > rowi; //cout << "scalar_product_matrix_mem: calculating row " << i + 1 << " of " // << Basis.size() << endl; // Loop over basis vectors in Basis for ( uint j = 0; j < Basis.ca.size(); j++ ) { // To contain the ij-th entry std::tr1::shared_ptr<Polynomial> ijEntry; // Rename indices, and make string of new Col_strs, to use in map std::pair<Col_str, Col_str> Css_new = rename_indices(Basis.ca.at(i), Basis.ca.at(j)); Col_str Cs1 = Css_new.first; Col_str Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Calculate element ij in scalar product matrix // If there are quarks involved this is straight forward if (Basis.ca.size() > 0 && !Basis.ca.at(0).gluons_only()) { // If this has scalar product has occurred before, reuse old value if ( mem_map.count(Cs_string.str()) > 0 ) { ijEntry = mem_map[Cs_string.str()]; } // Otherwise, calculate value and save else { Polynomial p = scalar_product(Cs1, Cs2); ijEntry = shared_ptr<Polynomial> (new Polynomial(p)); mem_map[Cs_string.str()] = ijEntry; } } // If there are only gluons involved the complex conjugate has to be // added (or subtracted) to consider the physical states // The sign is given by (-1)^Ng for Ng gluons, see arXiv:0906.1121 (my paper) if (Basis.ca.size() > 0 && Basis.ca.at(i).gluons_only()) { // The number of gluons in the basis stated // only correct and only used if gluons only uint Ng = Basis.ca.at(i).n_gluon(); // The sign of the interference, (-1)^Ng int sign = (Ng % 2 ? -1 : 1); // We have 2 contributions, from |amp|^2 and |conjugate(amp)|^2 giving 2*Poly1, // The interferene comes later... std::tr1::shared_ptr<Polynomial> Poly1; Css_new = rename_indices(Basis.ca.at(i), Basis.ca.at(j)); Cs1 = Css_new.first; Cs2 = Css_new.second; Cs_string.str().clear(); Cs_string << Cs1 << Cs2; // Reuse value if calculated before if (mem_map.count(Cs_string.str()) > 0) { Poly1 = mem_map[Cs_string.str()]; } // Otherwise, calculate value and save else { Polynomial p = scalar_product(Cs1, Cs2); Poly1 = shared_ptr<Polynomial> (new Polynomial(p)); mem_map[Cs_string.str()] = Poly1; } // For the second part we need the conjugate // Rename indices, and make string of new Col_strs std::pair< Col_str, Col_str > Css_new = rename_indices(Basis.ca.at(i), conjugate(Basis.ca.at(j))); Cs1 = Css_new.first; Cs2 = Css_new.second; Cs_string.str().clear(); Cs_string << Cs1 << Cs2; // For the interference std::tr1::shared_ptr<Polynomial> Poly2; // Reuse value if calculated before if (mem_map.count(Cs_string.str()) > 0) { //cout << "Already calculated sp!" << endl; Poly2 = mem_map[Cs_string.str()]; } // Otherwise, calculate value and save else { Polynomial p = scalar_product(Basis.ca.at(i), conjugate( Basis.ca.at(j))); Poly2 = shared_ptr<Polynomial> (new Polynomial(p)); mem_map[Cs_string.str()] = Poly2; } Polynomial p = 2 * (*Poly1) + sign * 2 * (*Poly2); ijEntry = shared_ptr<Polynomial> (new Polynomial(p)); (*ijEntry).simplify(); } rowi.push_back(ijEntry); } sp_m.push_back(rowi); } // Leading terms version dmatr diag_matr= leading(sp_m); // Verifying that the leading terms only sit on the diagonal bool diagonal=check_diagonal(diag_matr); if( !diagonal ) {throw -1; std::cerr <<"scalar_product_matrix_mem: leading terms are not diagonal" << std::endl; std::cerr.flush(); } write_out_spm( diag_matr, Basis , true ); // Clear unneeded matrix as it may be large diag_matr.clear(); // Numerical version for checking symmetry and saving - dmatr matr= d_num(sp_m); + dmatr matr= double_num(sp_m); // Verifying that the matrix is symmetric to accuracy bool sym=check_symmetry( matr ); if( !sym ) { std::cerr <<"scalar_product_matrix_mem: scalar product matrix not symmetric" << std::endl; std::cerr.flush(); throw -1; } // Also save the numerical versions to files for future usage // Full Nc version write_out_spm( matr, Basis , false ); return matr; } // Function for calculating scalar product matrix dmatr Col_functions::scalar_product_matrix_mem2(const Col_amp & Basis, const int n_loop) const{ // To contain the resulting scalar product matrix dmatr matr; dmatr matr_l; // For remembering already calculated topologies std::map< std::string, Polynomial> mem_map; std::cout << "scalar_product_matrix_mem2: calculating topologies... " << std::endl; // Loop over basis vectors in Basis once, this should give all topologies unless gluos only // and loop level if( n_loop >0 or Basis.n_quark()>0 ) { std::cout << "scalar_product_matrix_mem2: Loop level calculation or >1 q qbar, calling scalar_product_matrix_mem instead." << std::endl; return scalar_product_matrix_mem( Basis); } // Loop over basis vectors in Basis once, this should give all topologies unless gluons only // and loop level uint max = 1; for (uint i = 0; i <max; i++) { for (uint j = 0; j < Basis.size(); j++) { // Rename indices, and make string of new Col_strs, to use in map std::pair< Col_str, Col_str > Css_new = rename_indices(Basis.ca.at(i), Basis.ca.at(j)); Col_str Cs1 = Css_new.first; Col_str Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Add result to map //if (!(mem_map.count(Cs_string.str()) > 0)) { if ((mem_map.find(Cs_string.str()) == mem_map.end())) { //cout << "adding term for" << endl << Cs_string.str(); Polynomial p; // to contain result // In the case of both quarks and gluons if (!Basis.gluons_only()) { p = scalar_product(Cs1, Cs2); } // For gluons with implicitly added topology // In the case of only gluons the conjugated topology is needed as well // Note that the full result is saved in the local map else { uint Ng = Basis.n_gluon(); int sign = (Ng % 2 ? -1 : 1); p = 2 * scalar_product(Cs1, Cs2) + 2 * sign * scalar_product(Cs1, conjugate(Cs2)); p.simplify(); } if( i>0 ) std::cout << "the combination " << Cs_string.str() << " was not found before" << std::endl; mem_map.insert(make_pair(Cs_string.str(), p)); //cout << "The memmap at place " << mem_map[Cs_string.str()] ; } // For gluons, also the conjugated topology can appear, add this as well to map if (Basis.gluons_only()) { Cs2=conjugate(Basis.ca.at(j)); Cs2.simplify(); Css_new = rename_indices( Basis.ca.at(i), Cs2 ); //cout << "the conjugate(Basis.ca.at(j)) " << Cs2 << endl; Cs1 = Css_new.first; Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; if (!(mem_map.count(Cs_string.str()) > 0)) { Polynomial p; // to contain result uint Ng = Basis.n_gluon(); int sign = (Ng % 2 ? -1 : 1); p = 2 * scalar_product(Cs1, Cs2) + 2 * sign * scalar_product(Cs1, conjugate(Cs2)); p.simplify(); mem_map.insert(make_pair(Cs_string.str(), p)); } } } // End of j-loop } // End of i-loop std::cout << "done. "; std::cout.flush(); std::cout << mem_map.size() << " possible topologies. " << std::endl; // Now that the map is found construct numerical map, and the leading Nc map std::cout << "scalar_product_matrix_mem2: Finding numerical entries " << std::endl; - std::map< std::string, double> num_mem_map = d_num(mem_map); + std::map< std::string, double> num_mem_map = double_num(mem_map); std::cout << "done " << std::endl; std::cout << "scalar_product_matrix_mem2: Finding leading entries " << std::endl; - std::map<std::string, double> num_leading_mem_map = d_num(leading(mem_map)); + std::map<std::string, double> num_leading_mem_map = double_num(leading(mem_map)); std::cout << "done " << std::endl; std::cout << "There was " << num_leading_mem_map.size() << " numerical leading topologies. " << std::endl; // Calculating numerical scalar product matrices for (uint i = 0; i < Basis.ca.size(); i++) { // To contain vectors in matrices //vector< Polynomial > rowi; dvec rowi; dvec rowi_l; std::cout << "scalar_product_matrix_mem2: calculating rows " << i + 1 << " of " << Basis.size() << std::endl; // Loop over basis vectors in Basis for (uint j = 0; j < Basis.ca.size(); j++) { // To contain the ij-th entry double ijEntry; double ijEntry_l; // Rename indices, and make string of new Col_strs, to use in map std::pair<Col_str, Col_str> Css_new = rename_indices(Basis.ca.at(i), Basis.ca.at(j)); Col_str Cs1 = Css_new.first; Col_str Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Add to ijth entries, in case of both quarks and gluons if ((mem_map.count(Cs_string.str()) > 0)) { ijEntry = num_mem_map[Cs_string.str()]; ijEntry_l = num_leading_mem_map[Cs_string.str()]; //cout << "top found" << endl; } else { std::cerr << "scalar_product_matrix_mem2: All scalar product topologies should have been found by now." << std::endl; std::cerr.flush(); throw -1; } rowi.push_back(ijEntry); rowi_l.push_back(ijEntry_l); } // End of j loop matr.push_back(rowi); matr_l.push_back(rowi_l); } // End of i loop // Verifying that the matrix is symmetric to accuracy bool sym = check_symmetry(matr); if (!sym) throw -1; // Also save the numerical versions to files for future usage std::cout << "scalar_product_matrix_mem2: Writing out scalar product matrix... " << std::endl; write_out_spm(matr, Basis, false); // Verifying that the leading terms only sit on the diagonal bool diagonal = check_diagonal(matr_l); if (!diagonal) throw -1; std::cout << "scalar_product_matrix_mem2: Writing out leading contribution to scalar product matrix... " << std::endl; write_out_spm(matr_l, Basis, true); return matr; } Polynomial Col_functions::scalar_product(const Col_str & Cs1, const Col_str & Cs2) const{ //cout << "scalar_product(Cs1, Cs2): has the arguments " << Cs1 << " " << Cs2 << endl; Col_str Cs_tmp; Col_amp Ca_tmp; // Contract the quarks Cs_tmp=contract_quarks( Cs1, Cs2 ); //cout << "scalar_product: After contraction of quarks" << Cs_tmp << endl; std::cout.flush(); // Contract the gluons Ca_tmp=contract_all_gluons(Cs_tmp ); //cout << "scalar_product: After contract_all_gluons(Cs_tmp ) " << Ca_tmp << endl; //if ( Ca_tmp.ca.empty() ) cout << "empty ca" << endl; //cout << "Ca_tmp.poly "<< Ca_tmp.poly <<endl; //cout << "scalar_product(Cs1,Cs2): To return, " << Ca_tmp.Poly << endl; if ( !Ca_tmp.ca.empty() ){ // if( non_contracted ){ std::cerr << "scalar_product: terminating due to non-contracted indices." <<std::endl; std::cerr << "The col_amp is " << Ca_tmp << std::endl; throw -1; } else return Ca_tmp.Scalar; } // Function to test if a numerical (scalar product matrix) is symmetric bool Col_functions::check_symmetry(const dmatr & matr ) const{ std::cout << "check_symmetry( matr ): Numerically verifying symmetry of matrix..."; if( matr.empty() ==0 ){ std::cout << "The numerical matrix is empty. " << std::endl; return true; } // Verifying that the matrix is symmetric to accuracy "accuracy", // if not sym is put to false bool sym=true; // Loop over basis vectors in Basis for (uint i = 0; i < matr.size(); i++) { std::vector<Polynomial> rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { if ((abs(matr.at(i).at(j) / matr.at(j).at(i) - 1.0) > accuracy) && (matr.at(i).at(j) > accuracy) && (matr.at(j).at(i) > accuracy)) { sym=false; std::cerr << "check_symmetry( matr ): Error, the resulting scalar product matrix is not symmetric. \n " << "Element " << i << "," << j << ": " << matr.at(i).at(j) << ", Element " << j << "," << i << ": " << matr.at(j).at(i) << std::endl << "This indicates an error in calculation of scalar products. " << std::endl; } } } std::cout << " done." << std::endl; return sym; } // Function to test if a numerical (scalar product matrix) is diagonal bool Col_functions::check_diagonal(const dmatr & matr) const{ std::cout << "check_diagonal( matr ): Numerically checking if the matrix is diagonal..."; // Verifying that the matrix is diagonal // if not sym is put to false bool diag=true; // Loop over basis vectors in Basis for (uint i = 0; i < matr.size(); i++) { std::vector<Polynomial> rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { if (matr.at(i).at(j)!=0.0 && i!=j) { diag=false; std::cerr << "check_diagonal( matr ): Warning, the matrix is not diagonal. \n " << "Element " << i << "," << j << ": " << matr.at(i).at(j) << std::endl << "This indicates an error in calculation of scalar products. " << std::endl; } } } std::cout << " done." << std::endl; return diag; } // A function for calculating the scalar products between the basis vectors // (stored in a Col_amp) poly_matr Col_functions::scalar_product_matrix(const Col_amp & Basis) const{ // To contain the resulting scalar product matrix poly_matr sp_m; // Loop over basis vectors in Basis for( uint i=0; i< Basis.ca.size(); i++){ std::vector< Polynomial > rowi; // Loop over basis vectors in Basis for( uint j=0; j< Basis.ca.size(); j++){ //cout << "scalar_product_matrix(Col_amp):: calculating for i " << i << " j " << j << endl; //cout.flush(); // Calculate element ij in scalar product matrix // If there are quarks involved this is straight forward Polynomial ijEntry; if( Basis.ca.size()>0 && !Basis.ca.at(i).gluons_only() ){ ijEntry=scalar_product(Basis.ca.at(i), Basis.ca.at(j)); } // If there are gluons involved the complex conjugate has to be // added or subtracted to consider the physical states as // the conjugate, "anti-cyclic", amplitude is implicit // The sign is given by (-1)^Ng for Ng gluons, see arXiv:0906.1121 (my paper) if( Basis.ca.size()>0 && Basis.ca.at(i).gluons_only() ){ // The number of gluons in the basis stated // only correct and only used if gluons only uint Ng=Basis.ca.at(i).n_gluon(); // The sign of the interference, (-1)^Ng int sign=(Ng % 2 ? -1:1); //cout << "The sign is " << sign << " for Ng= " << Ng <<endl; // If only gluons the basis states are composed of 2 color structures // Add together to get scalar product matrix for basis // Using the realness of the vector space ijEntry=2*scalar_product( Basis.ca.at(i), Basis.ca.at(j) ) +sign*2*scalar_product( Basis.ca.at(j), conjugate( Basis.ca.at(i) ) ); ijEntry.simplify(); } rowi.push_back( ijEntry ); } sp_m.push_back(rowi); } // For saving and checking symmetry of numerical version - dmatr matr= d_num(sp_m); + dmatr matr= double_num(sp_m); // Verifying that the matrix is symmetric to accuracy bool sym=check_symmetry( matr ); if( !sym ) throw -1; // Also save the numerical versions to files for future usage // Full Nc version write_out_spm( matr, Basis , false ); // Leading terms version - matr= d_num( leading(sp_m) ); + matr= double_num( leading(sp_m) ); write_out_spm( matr, Basis , true ); // Verifying that the leading terms only sit on the diagonal bool diagonal=check_diagonal(matr); if(!diagonal) throw -1; return sp_m; } // Functions for generating the n_partons random amplitudes in [-1/2,1/2] // for emission from n_partons. cvec Col_functions::generate_random_amplitudes(int n_p) const{ cvec amps; // Loop over partons which can radiate for(int i=0; i< n_p; i++ ){ // Random number in [-0.5,0.5] double r=1.0; r=rand()/float(RAND_MAX)-0.5; amps.push_back(r); } return amps; } poly_vec Col_functions::decompose(const Col_amp & Ca, const Col_amp & Basis) const{ //cout << "decompose: Incoming Basis and Col_amp " << Basis << Ca << endl; // To contain the decomposed vector poly_vec Decv; Polynomial Zero; Zero=Zero*0; // Initially set all components of the vectors to 0 for (uint m2 = 0; m2 < Basis.ca.size(); m2++){ Decv.push_back(Zero); } //cout<<"decompose: initial Decv "<< Decv << "with size "<< Decv.size() << endl; // Loop over Cs in Ca and check which basis vector they equal for (uint m1 = 0; m1 < Ca.ca.size(); m1++) { for (uint m2 = 0; m2 < Basis.ca.size(); m2++) { bool found=false; if (Ca.ca.at(m1).cs == Basis.ca.at(m2).cs) { found=true; //cout << m2 << " Parts " << Decv.at(m2) << " and " << Ca.ca.at(m1).Poly << endl; //if( Ca.ca.at(m1).Poly.empty() ) cout << m2 << "Poly was empty" << endl; Decv.at(m2)=Decv.at(m2)+Ca.ca.at(m1).Poly; //cout.flush(); - //if(found) cout << m2 << " result " << Decv.at(m2) << " num " << d_num(Decv.at(m2)) << endl; + //if(found) cout << m2 << " result " << Decv.at(m2) << " num " << double_num(Decv.at(m2)) << endl; //cout.flush(); } if(found) break; // The tensor is ONE of the tensors in the Basis } } //cout << "decompose: final Decv "<< Decv << "with size "<< Decv.size() << endl; return Decv; } // Function for calculating scalar products // given the information about the basis and the scalar product matrix in the basis Polynomial Col_functions::scalar_product(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const{ // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps poly_vec Polyv1=conjugate(decompose(Ca1, Basis));// conjugation added 130530 poly_vec Polyv2=decompose(Ca2, Basis); //std::cout << "scalar_product(Ca1,Ca2,Basis,sp_matr): Decomposition done."<< std::endl; // Then add contributions for (uint m1=0; m1< Basis.ca.size(); m1++){ // Diagonal terms Poly_res=Poly_res+Polyv1.at(m1) *Polyv2.at(m1) *sp_matr.at(m1).at(m1); for (uint m2=0; m2< m1; m2++){ // Other terms, use symmetry of scalar product matrix //Poly_res=Poly_res+ 2*Polyv1.at(m1) *Polyv2.at(m2) *sp_matr.at(m1).at(m2); how could this be right Poly_res=Poly_res+ ( Polyv1.at(m1) *Polyv2.at(m2)+Polyv1.at(m2) *Polyv2.at(m1) ) *sp_matr.at(m1).at(m2); } } return Poly_res; } // Function for calculating scalar products numerically // given the information about the basis and the scalar product matrix in the basis -Polynomial Col_functions::scalar_product_c_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const{ +Polynomial Col_functions::scalar_product_cnum_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const{ // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps poly_vec Polyv1=decompose(Ca1, Basis); // added complex conjugation 130525 Polyv1=conjugate(Polyv1); poly_vec Polyv2=decompose(Ca2, Basis); //std::cout << "scalar_product(Ca1,Ca2,Basis,sp_matr): Decomposition done."<< std::endl; // Then add contributions for (uint m1=0; m1< Basis.ca.size(); m1++){ // Diagonal terms Poly_res=Poly_res+ Polyv1.at(m1) *Polyv2.at(m1) *sp_matr.at(m1).at(m1); for (uint m2=0; m2< m1; m2++){ // Other terms, use symmetry of scalar product matrix //Poly_res=Poly_res+ 2*Polyv1.at(m1) *Polyv2.at(m2) *sp_matr.at(m1).at(m2); // How could this be right? // added complex conjugation 130525 Poly_res+= ( Polyv1.at(m1) *Polyv2.at(m2) +Polyv1.at(m2) *Polyv2.at(m1) ) *sp_matr.at(m1).at(m2); } } return Poly_res; } // Function for calculating scalar products // given the information about the basis in the scalar product matrix in the basis // This is an attempt to speed up -cnum Col_functions::scalar_product_c_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const dmatr & sp_matr) const{ +cnum Col_functions::scalar_product_cnum_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const dmatr & sp_matr) const{ // To contain the resulting Polynomial cnum res=0; uint basis_size=Basis.ca.size(); //time_t t1, t2, t3; //t1=clock( ); // Decompose the Col_amps poly_vec Polyv1, Polyv2; Polyv1=decompose(Ca1, Basis); if( &Ca1==&Ca2 ) Polyv2=Polyv1; else Polyv2=decompose(Ca2, Basis); - //cout << "scalar_product_c_num(Ca1, Ca2, Basis, sp_matr)" << "\n Polyv1: " << Polyv1 << endl; + //cout << "scalar_product_cnum_num(Ca1, Ca2, Basis, sp_matr)" << "\n Polyv1: " << Polyv1 << endl; // Conjugate first arg, added 130525 Polyv1=conjugate(Polyv1); // Make ordinary vectors to speed up multiplication cvec v1, v2; v1.reserve( basis_size ); for (uint i=0; i< basis_size; i++){ - v1.push_back( c_num( Polyv1.at(i) ) ); + v1.push_back( cnum_num( Polyv1.at(i) ) ); }; //if( &Ca1==&Ca2 ) v2=v1; if (false) v2=v1; else{ v2.reserve(basis_size); - for (uint i=0; i< basis_size; i++) v2.push_back( c_num( Polyv2.at(i) ) ); + for (uint i=0; i< basis_size; i++) v2.push_back( cnum_num( Polyv2.at(i) ) ); } - //cout << "scalar_product_c_num(Ca1, Ca2, Basis, sp_matr) " << "\n v1: " << v1 << endl; + //cout << "scalar_product_cnum_num(Ca1, Ca2, Basis, sp_matr) " << "\n v1: " << v1 << endl; //t2=clock(); - //cout << "scalar_product_c_num(Ca1,Ca2,Basis, cmatr): Decomposition done at time: " << t2-t1 << endl; + //cout << "scalar_product_cnum_num(Ca1,Ca2,Basis, cmatr): Decomposition done at time: " << t2-t1 << endl; // Then add contributions cnum v1m1, v2m1; dvec row; for (uint m1=0; m1< basis_size; m1++){ v1m1=v1.at(m1); v2m1=v2.at(m1); row=sp_matr.at( m1 ); // Diagonal terms res=res+ v1m1 *v2.at(m1) *row.at(m1); for (uint m2=0; m2< m1; m2++){ //res=res+ v1.at(m1) *v2.at(m2) *sp_matr.at(m1).at(m2); //res=res+ 2.0*ind1*v2.at(m2) *row.at(m2);// how could this be true res += (v1m1*v2.at(m2) +v1.at(m2)*v2m1 ) *row.at(m2); // changed to this 130524 } } //t3=clock(); - //cout << "scalar_product_c_num(Ca1,Ca2,Basis, cmatr): Matrix multiplication done at time: " << t3-t2 << endl; + //cout << "scalar_product_cnum_num(Ca1,Ca2,Basis, cmatr): Matrix multiplication done at time: " << t3-t2 << endl; return res; } // Connect n_q q and qbars in all n_q! ways, returns a basis (in the form of a Col_amp) // for the case of n_q q qbar pairs. Col_amp Col_functions::connect_quarks( int n_q ) const{ Col_amp Basis; // If n_q=1, there is only one state if( n_q==1 ) { Col_str Cs_tmp("[{1,2}]"); Basis=Basis+Cs_tmp; } else{ // For more than one q qbar pair, build up states iteratively // Start from the state with one less q qbar state Col_amp Old_bas=connect_quarks( n_q-1 ); // Loop over old basis vectors for (uint old_v = 0; old_v < Old_bas.ca.size(); old_v++) { // Some, 1*(n_q-1)!, new states are obtained by just adding q_new qbar_new to the old states // Construct the new Ql with i.e. {q_new, qbar_new} Quark_line Ql_new; Ql_new.open = true; Ql_new.ql.push_back(2 * n_q - 1); Ql_new.ql.push_back(2 * n_q); Col_str New_state; New_state.cs.push_back(Ql_new); New_state.append( Old_bas.ca.at(old_v).cs ); Basis=Basis+New_state; } // The other (n_q-1)*(n_q-1)! states are obtained by combining the new qbar with any old quark // Loop over old basis states for (uint old_v = 0; old_v < Old_bas.ca.size(); old_v++) { // Loop over old qbar indices for (int qbar_old = 2; qbar_old < 2 * n_q; qbar_old += 2) { // To contain the new basis vector Col_str New_state = Old_bas.ca.at(old_v); // Replace the index qbar_old with 2*nq New_state.replace(qbar_old, 2 * n_q); // Create the Quark_line {q_new, qbar_old} Quark_line Ql_new; Ql_new.open = true; Ql_new.ql.push_back(2 * n_q - 1); Ql_new.ql.push_back(qbar_old); // Add the Col_str from {q_new, qbar_old} to the new basis vector New_state.cs.push_back(Ql_new); // Add the new basis tensor to the basis Basis = Basis + New_state; } } } return Basis; } // Function for calculating scalar products // This function assumes that the Col_amps are normal ordered and that the full polynomial is // contained in the numerical factor // It also assumes that the BASIS in which sp_matr is calculated is the SAME as that of the Col_amp // and that the Col_amp is a linear combination of ALL states // The coefficients of the basis vectors can then simply be read off in the Polynomials multiplying the Col_strs // This is an attempt to speed up cnum Col_functions::scalar_product_num_fast(const Col_amp & Ca1, const Col_amp & Ca2, const dmatr & sp_matr) const{ // To contain the result cnum res=0; uint basis_size=Ca1.ca.size(); // Both Ca's should have same length //time_t t1, t2, t3; //t1=clock( ); // Make ordinary vectors of numerical information in Polynomials, to speed up multiplication cvec v1, v2; v1.reserve(basis_size); for (uint i=0; i< basis_size; i++){ // added conjugation of first arg 130524 - v1.push_back( conj( c_num( Ca1.ca.at(i).Poly ) ) ); + v1.push_back( conj( cnum_num( Ca1.ca.at(i).Poly ) ) ); }; if( &Ca1==&Ca2 ) v2=v1; else{ v2.reserve(basis_size); - for (uint i=0; i< basis_size; i++) v2.push_back( c_num( Ca1.ca.at(i).Poly ) ); + for (uint i=0; i< basis_size; i++) v2.push_back( cnum_num( Ca1.ca.at(i).Poly ) ); } //t2=clock(); // Then add contributions cnum ind1; dvec row; for (uint m1=0; m1< basis_size; m1++){ ind1=v1.at(m1); row=sp_matr.at(m1); // Diagonal parts res=res+ ind1 *v2.at(m1) *row.at(m1); for (uint m2=0; m2< m1; m2++){ //res=res+ v1.at(m1) *v2.at(m2) *sp_matr.at(m1).at(m2); // Non diagonal parts res=res+ 2.0*ind1 *v2.at(m2) *row.at(m2); } } //t3=clock(); return res; } // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Warning: ordering of basis tensors must be the same cnum Col_functions::scalar_product_num_fast(const cvec & v1, const cvec & v2, const dmatr & spm_matr) const { if(v1.size()!= v2.size()){ std::cerr << "Col_functions::scalar_product_num_fast: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; throw v2; } if(v1.size()!= spm_matr.size()){ std::cerr << "Col_functions::scalar_product_num_fast: Size of vectors " << v1.size() << " do not agree with size of matrix " << spm_matr.size() << std::endl; throw spm_matr; } uint basis_size=v1.size(); // To contain the result cnum res=0; cnum tmp=0, tmp2=0; //time_t t1, t2; //t1=clock( ); // Add contributions cnum v1m1, v2m1; for (uint m1=0; m1< basis_size; m1++){ v1m1=v1.at(m1); v2m1=v2.at(m1); const dvec &row = spm_matr.at(m1); // Diagonal parts res += v1m1 *v2.at(m1) *row.at(m1); tmp = 0; tmp2=0; for (uint m2=0; m2< m1; m2++){ //res=res+ v1.at(m1) *v2.at(m2) *sp_matr.at(m1).at(m2); //res=res+ 2.0*ind1 *v2.at(m2) *row.at(m2); //tmp += v2[m2] *row[m2]; // 40% quicker tmp += (v1m1*v2[m2]+v2m1*v1[m2]) *row[m2]; //alt //tmp+=v2[m2]*row[m2]; //tmp2+=v1[m2]*row[m2]; } //res += 2.0*v1m1*tmp; res+=tmp; // alt //res+= v1m1*tmp+v2m1*tmp2; } //t2=clock(); //cout << "scalar_product_num_fast(v1, v2, spm_matr): scalar prod calculated at time " // << t2-t1 << endl; return res; } // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Warning: ordering of basis tensors must be the same // Function for calculating scalar products, assuming that only diagonal parts contribute // This is useful for calculating the leading Nc result // This function assumes that the Col_amps are normal ordered and that the full polynomial is // contained in the numerical factor // It also assumes that the BASIS in which sp_matr is calculated is the SAME as that of the Col_amp // and that the Col_amp is a linear combination of ALL states // The coefficients of the basis vectors can then simply be read off in the Polynomials multiplying the Col_strs // This is an attempt to speed up // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Assumes that there are only diagonal contributions. This is useful for calculations in leading Nc limit // Warning: ordering of basis tensors must be the same cnum Col_functions::scalar_product_num_diagonal(const cvec & v1, const cvec & v2, const dmatr & spm_matr) const { // To contain the result cnum res=0; // Check dimension uint basis_size=v1.size(); //time_t t1, t2; //t1=clock( ); // Then add contributions for (uint m1=0; m1< basis_size; m1++){ // added conjugation of first arg 130524 res=res+ conj( v1.at(m1) ) *v2.at(m1) *spm_matr.at(m1).at(m1); } //t2=clock(); //cout << "scalar_product_num_diagonal(v1, v2, spm_matr): scalar prod calculated at time " // << t2-t1 << endl; return res; } // Compute the new basis states coming from one old vector when one gluon, g_new, is added. Col_amp Col_functions::add_one_gluon(const Col_str & Cs, int g_new, int n_loop) const{ //cout << "add_one_gluon( Col_str, int): Entering with arg" << Old_tens // << endl; // For storing the new basis Col_amp New_tensors; // Add the new gluon in all possible ways to the old Color structure // Loop over the Quark_lines for (uint ql = 0; ql < Cs.cs.size(); ql++) { // The old Quark_line, before insertion of the new gluon index Quark_line Old_Ql = Cs.cs.at(ql); Col_str New_tensor = Cs; // Special case of insertion of a gluon in a 2-ring in a gluons only basis // in this case only "half" the basis states for rings with >= 3 gluons are // generated. The rest are obtained by taking the indices in anti-cyclic order // i.e. by complex conjugating if ((Old_Ql.ql.size() == 2 && Cs.gluons_only())) { // Insert the new gluon after the existing gluons Quark_line New_Ql = Old_Ql; New_Ql.ql.push_back(g_new); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } else { // ordinary case // Loop over (potential) insertion places in the Quark_lines, starting from the end for (int j = Old_Ql.ql.size(); j > 0; j--) { //cout << "add_one_gluon( Col_str, int): ql " << ql << " j " << j // << endl; // Special treatment of last place, insert here only if the ring is open // (the gluon index cannot take the place of the a quark index) if (Old_Ql.open && j == static_cast<int>(Old_Ql.ql.size()) ) j--; Quark_line New_Ql = Old_Ql; quark_line::iterator it = New_Ql.ql.begin() + j; New_Ql.ql.insert(it, g_new); //// Inserting element at place given by iterator itj //quark_line::iterator itj = cs.at(i).ql.begin() + j; //cs.at(i).ql.insert(itj, part_num); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } } } //cout << "add_one_gluon( Col_str, int): to return " << New_tensors << endl; return New_tensors; } // Compute the basis states coming from the whole Col_amp if one gluon is added // If n_loop==0, only tree level states are constructed. Col_amp Col_functions::add_one_gluon(const Col_amp & Old_basis, int n_q, int n_g, int g_new, int n_loop) const{ //cout << "add_one_gluon( Col_amp, int): Entering with arg " << Old_bas // << endl; // For storing the new basis Col_amp New_bas; //cout << "add_one_gluon( Col_amp, int): current state " << New_bas << endl; // Add the new gluon to each of the previous color tensors for (uint t = 0; t < Old_basis.ca.size(); t++) { New_bas = New_bas + add_one_gluon(Old_basis.ca.at(t), g_new, n_loop); //cout << "add_one_gluon( Col_amp, int): current state " << New_bas << endl; } //cout << "add_one_gluon( Col_amp, int): Gluons added to old states done with result " << New_bas // << endl; // Create new states with 2-rings formed from taking the new gluon and any old gluon // these states can be created by taking the n_g-2 basis but use the indices // 1...i_ex-1, i_ex+1 n_g-1 // for the gluons, that is exclude one index (to be combined with the index n_g in a 2-ring) // First, create the n_g-2 basis here, must have at least 4 gluons // Do this only is not tree-level mode, i.e. n_loop>0 if ( n_loop>0 && (( n_q>0 && g_new-2*n_q>=2 ) or (n_q==0 && g_new >= 4) )) { Col_amp Bas_n_g_minus_2 = create_trace_basis(n_q, g_new-n_q*2-2, n_loop); // cout << "add_one_gluon( Col_amp, int): Bas_n_g_minus_2 " << Bas_n_g_minus_2 // << endl; // Loop over indices to group with the new gluon index //for(int g_ex=2*n_q+1; g_ex <= n_g-2; g_ex++){ for (int g_ex = 2 * n_q + 1; (g_ex <= g_new - 1 ); g_ex++) { //cout // << "add_one_gluon( Col_amp, int): Looping over excluded index, g_ex " // << g_ex << endl; // Create the 2-ring with g_ex and g_new Quark_line Ql2_ring; Ql2_ring.open = false; Ql2_ring.append(g_ex); Ql2_ring.append(g_new); //cout << "add_one_gluon( Col_amp, int): Created 2-ring " << Ql2_ring // << endl; // Loop over Col_str's=basis vectors for (uint bv = 0; bv < Bas_n_g_minus_2.ca.size(); bv++) { //cout << "add_one_gluon( Col_amp, int): Looping over bv " << bv // << endl; Col_str Cs_new; // To contain the new basis vecto Cs_new = Bas_n_g_minus_2.ca.at(bv); // Loop over the Quark_lines in the basis vectors for (uint ql = 0; ql < Bas_n_g_minus_2.ca.at(bv).cs.size(); ql++) { //cout << "add_one_gluon( Col_amp, int): Looping over ql " // << ql << endl; // Loop over positions in the quark_line for (uint pos = 0; pos < Bas_n_g_minus_2.ca.at(bv).cs.at(ql).ql.size(); pos++) // cout << "add_one_gluon( Col_amp, int): Looping over pos " << pos <<endl; // Jump over index g_ex by increasing the index g_ex, and all indices above with 1 if (Bas_n_g_minus_2.ca.at(bv).cs.at(ql).ql.at(pos) >= g_ex) { // Change the index if >= g_ex Cs_new.cs.at(ql).ql.at(pos)++; } } //Col_str Cs_new; //To contain the new basis vector //Cs_new=Bas_n_g_minus_2.ca.at(bv); Cs_new.cs.push_back(Ql2_ring); New_bas = New_bas + Cs_new; //cout << "add_one_gluon( Col_amp, int): New state " << Cs_new // << endl; } } } return New_bas; } // Function for creating a basis with n_q=n_qbar quarks and n_g gluons. // Note: If there are only gluons the anti-cyclic part of the Col_amp is implicit. // For example, for (1,2,3) there is also an implicit part -(1,3,2) which is // not separately constructed. This is compensated for in scalar_product_matrix and scalar_product_matrix_mem, // but not in scalar_product() Col_amp Col_functions::create_trace_basis(int n_q, int n_g, int n_loop) const{ //cout << "create_trace_basis: Called with n_g=" << n_g << " and n_q=" << n_q << endl; // To contain the resulting basis Col_amp Basis; // First connect all q and qbars in all n_q! ways // This gives a basis for the case of n_q quarks and n_qbar anti-quarks if( n_q != 0) Basis=connect_quarks( n_q ); // If there were no quarks if( n_q==0 ){ // There has to be at least two gluons if( n_g <= 1 ) { std::cerr << "create_trace_basis: For 0 quarks there is no basis with only " << n_g << " gluons" << std::endl; throw -1; } // If 2 or more gluons, build from the 2-gluon basis else if( n_g>=2 ){ Col_str Cs_OnlyState("[(1,2)]"); Col_amp Ca_tmp; Ca_tmp.ca.push_back(Cs_OnlyState); Basis= Ca_tmp; // For 2 gluons, the work is done if( n_g==2 ) return Basis; //cout << Basis; } } // Then, add the gluons one at the time // If there are only gluons the generation should start from gluon 3, // otherwise from 2*n_q+1; for( int g_new=std::max(3, 2*n_q+1); g_new <= 2*n_q+ n_g; g_new++){ // If only gluons start from the 2-gluon state, so add gluon 3 //cout << "create_trace_basis: gluon to be added "<< g_new<< endl; Basis=add_one_gluon(Basis, n_q ,n_g, g_new, n_loop); //add_one_gluon( Col_amp Old_bas, int n_g, int n_q, int g_new) } // If there are only gluons, to count only physical states each basis state // should be combined with its complex conjugate // For uniformity, do this for the 2-gluon states as well // Normal order the Col_str's Basis.normal_order(); return Basis; } // A function for radiating n_g gluons n_run times double Col_functions::just_radiate(Col_amp & Ca1, int n_g_rad, double n_run, int n_loop) const{ int n_q=Ca1.n_quark(); int n_g=Ca1.n_gluon(); // Find the basis std::cout << "just_radiate: Constructing the basis for " << n_q << " q qbar pair(s) and " << n_g + n_g_rad << " gluon(s)..."<< std::endl; std::cout.flush(); Col_amp Basis = create_trace_basis(n_q, n_g + n_g_rad, n_loop); std::cout << "just_radiate: Constructed the basis: " << Basis << " with " << Basis.ca.size() << " basis tensors \n"; std::cout.flush(); // and calculate the scalar product matrix in this basis std::cout << "just_radiate: Calculating the scalar product matrix..." << std::endl; // Calculating the scalar product matrix and its numerical versions (once and for all) // do this both in the leading Nc limit and else poly_matr sp_m = scalar_product_matrix(Basis); poly_matr sp_m_leading = leading(sp_m); - dmatr sp_m_num = d_num(sp_m); - dmatr sp_m_num_leading = d_num(sp_m_leading); + dmatr sp_m_num = double_num(sp_m); + dmatr sp_m_num_leading = double_num(sp_m_leading); std::cout << sp_m; //cout << sp_m_leading; // For containing the scalar product cnum sp_num, sp_num_leading; // For containing the average ratio of amp^2 for Nc=3 and Nc=infinity double ratio_average=0; // Col_amp to start the radiation from Col_amp Ca_initial = Ca1; // The initial number of partons int n_initial = 2 * n_q + n_g; std::cout << "just_radiate: Running the shower... " << std::endl; // Make n different showers for (int n = 1; n <= n_run; n++) { std::cout << "just_radiate: Calculating amplitude... " << std::endl; // Reset n_parton and the starting Col_amp int n_parton = n_initial; Ca1 = Ca_initial; // Radiate n_g partons // Radiate parton p, where p=n_parton+1 ... n_initial+n_g_rad for (int p = n_parton + 1; p <= n_initial + n_g_rad; p++) { Col_amp Ca_new; // Add contributions where p is radiated from all partons (one at the time) for (int i = 1; i <= n_parton; i++) { // Radiate parton p from parton i Ca Col_amp part_i = emit_gluon(Ca1, i, p); // Multiply with a random number to mimic amplitude from other DOF double r=1.0; r=rand()/float(RAND_MAX)-0.5; part_i=part_i*r; // Append to new Ca (the Ca after emission of one more parton) Ca_new.append(part_i.ca); } // Now the number of partons have increased with 1 n_parton++; // Simplify the resulting amplitude (turning off makes things slow, but tests scalar prod) Ca_new.simplify(); Ca1 = Ca_new; //cout << "just_radiate: Ca1" << Ca1 << endl; } // cout << "just_radiate: Ca1" << Ca1 << endl; // Calculate the scalar product std::cout << "just_radiate: Calculating |A|^2..." << std::endl; //sp = scalar_product(Ca1, Ca1, Basis, sp_m); - sp_num = scalar_product_c_num(Ca1, Ca1, Basis, sp_m_num); - sp_num_leading = scalar_product_c_num(Ca1, Ca1, Basis, sp_m_num_leading); + sp_num = scalar_product_cnum_num(Ca1, Ca1, Basis, sp_m_num); + sp_num_leading = scalar_product_cnum_num(Ca1, Ca1, Basis, sp_m_num_leading); // Write out result the first few times if (n <= 2) { std::cout << "Resulting Col_amp" << Ca1 << std::endl; std::cout << "with scalar prod " << sp_num << std::endl; std::cout << "In the infinity limit " << sp_num_leading << std::endl; } std::cout << "Run " << n << " The ratio Nc=Infinity / Nc=3 " << sp_num_leading/sp_num << std::endl; ratio_average = ratio_average + real(sp_num_leading / sp_num ) / n_run; } return ratio_average; } // A function for radiating n_g gluons n_run times, using that only tree level graphs contribute double Col_functions::just_radiate_tree_level(Col_amp & Ca1, int n_g_rad, double n_run) const{ int n_loop=0; int n_q=Ca1.n_quark(); int n_g=Ca1.n_gluon(); // Find the basis // Find the basis std::cout << "just_radiate_tree_level: Constructing the basis for " << n_q << " q qbar pair(s) and " << n_g + n_g_rad << " gluon(s)"; if( n_loop==0 ) std::cout << " at tree level"; std::cout << " ..." << std::endl; std::cout.flush(); Col_amp Basis = create_trace_basis(n_q, n_g + n_g_rad, n_loop); // For containing numerical scalar product matrices dmatr sp_m_num, sp_m_num_leading; // Calculating the scalar product matrix and its numerical versions (once and for all) // do this both in the leading Nc limit and else // Check if this has been calculated before std::string filename, filenamel; filename=spm_file_name(Basis, false); filenamel=spm_file_name(Basis, true); std::ifstream ifile(filename.c_str()); std::ifstream ifilel(filenamel.c_str()); // Reuse old results if available, otherwise calculate scalar product matrix if ((ifile && ifilel)) { // The file exists and old scalar products can be reused std::cout <<"just_radiate_tree_level: Scalar product matrix has been calculated before, reusing old result." << std::endl << "Warning: the ordering of basis vectors is important!" << std::endl; sp_m_num=read_in_matrix(filename.c_str()); sp_m_num_leading=read_in_matrix(filenamel.c_str()); } else{ // Calculate the scalar product matrix in this basis std::cout << "just_radiate_tree_level: Calculating the scalar product matrix..." << std::endl; std::cout.flush(); poly_matr sp_m = scalar_product_matrix(Basis); poly_matr sp_m_leading = leading(sp_m); - sp_m_num = d_num(sp_m); - sp_m_num_leading = d_num(sp_m_leading); + sp_m_num = double_num(sp_m); + sp_m_num_leading = double_num(sp_m_leading); std::cout << sp_m; } //cout << sp_m_leading; // For containing the scalar product Polynomial sp, sp_leading; cnum sp_num, sp_num_leading; // For containing the average ratio of amp^2 for Nc=3 and Nc=infinity double ratio_average=0.0; // Col_amp to start the radiation from Col_amp Ca_initial = Ca1; // The initial number of partons int n_initial = 2 * n_q + n_g; std::cout << "just_radiate_tree_level: Running the shower... " << std::endl; // Make n different showers for (int n = 1; n <= n_run; n++) { std::cout << "just_radiate_tree_level: Calculating amplitude... " << std::endl; time_t t1, t2, t3; // Reset n_parton and the starting Col_amp int n_parton = n_initial; Ca1 = Ca_initial; t1=clock(); // Radiate n_g partons // Radiate parton p, where p=n_parton+1 ... n_initial+n_g_rad for (int p = n_parton + 1; p <= n_initial + n_g_rad; p++) { Col_amp Ca_new; // Add contributions where p is radiated from all partons (one at the time) for (int i = 1; i <= n_parton; i++) { // Radiate parton p from parton i Ca Col_amp part_i = emit_gluon(Ca1, i, p); // Multiply with a random number to mimic amplitude from other DOF double r=1.0; r=rand()/float(RAND_MAX)-0.5; part_i=part_i*r; // Append to new Ca (the Ca after emission of one more parton) Ca_new.append(part_i.ca); } // Now the number of partons have increased with 1 n_parton++; // Simplify the resulting amplitude (turning off makes things slow, but tests scalar prod) Ca_new.simplify(); Ca1 = Ca_new; //cout << "just_radiate: Ca1" << Ca1 << endl; } // Needed to quickly calculate the scalar product, state is assumed to be normal ordered t2=clock(); Ca1.normal_order(); t3=clock(); std::cout << "just_radiate_tree_level: Generated event, took " << t3-t1 << " of which " << t2-t1 << " was Ca1.normal_order" << std::endl; // cout << "just_radiate: Ca1" << Ca1 << endl; // Calculate the scalar product std::cout << "just_radiate_tree_level: Calculating |A|^2..." << std::endl; sp_num = scalar_product_num_fast( Ca1, Ca1, sp_m_num ); sp_num_leading = scalar_product_num_fast( Ca1, Ca1, sp_m_num_leading ); // Write out result the first few times if (n <= 2) { std::cout << "Resulting Col_amp" << Ca1 << std::endl; std::cout << "with scalar prod " << sp_num << std::endl; std::cout << "In the infinity limit " << sp_num_leading << std::endl; } std::cout << "Run " << n << " The ratio (leading Nc) / (all terms) " << real(sp_num_leading/sp_num) << std::endl; ratio_average = ratio_average + real( sp_num_leading / sp_num ) / n_run; } return ratio_average; } // A function for radiating n_g gluons n_run times, using that only tree level graphs contribute // TODO more explanation double Col_functions::just_radiate_tree_level2(const Col_amp & Ca1, int n_g_rad, double n_run) const{ int n_loop=0; // Number of quarks and gluons int n_q = Ca1.n_quark(); int n_g = Ca1.n_gluon(); if( (!n_loop==0) or n_q>=2 ) { std::cout << "just_radiate_tree_level2: Function intended for fully connected states only, " "n_loop=0, and n_q=1 or 0. Calling just_radiate_tree_level instead." << std::endl; Col_amp the_Ca= Ca1; return just_radiate_tree_level( the_Ca, n_g_rad, n_run); } // Find the basis std::cout << "just_radiate_tree_level2: Constructing the basis for " << n_q << " q qbar pair(s) and " << n_g + n_g_rad << " gluon(s)"; if( n_loop==0 ) std::cout << " at tree level"; std::cout << " ..." << std::endl; Col_amp Basis = create_trace_basis(n_q, n_g + n_g_rad, n_loop); std::cout << Basis << std::endl; std::cout << "just_radiate_tree_level2: Basisis with " << Basis.ca.size() << " basis vectors found." << std::endl; // For containing numerical scalar product matrices dmatr sp_m_num, sp_m_num_leading; // Calculating the scalar product matrix and its numerical versions (once and for all) // do this both in the leading Nc limit and for Nc=3, // but first check if this has been calculated before std::string filename, filenamel; filename = spm_file_name(Basis, false); filenamel = spm_file_name(Basis, true); std::ifstream ifile(filename.c_str()); std::ifstream ifilel(filenamel.c_str()); // Reuse old results if available, otherwise calculate scalar product matrix if ((ifile && ifilel)) { // The file exists and old scalar products can be reused std::cout << "just_radiate_tree_level2: Scalar product matrix has been calculated before, reusing old result. \n Warning: the ordering of basis vectors is important!" << std::endl; std::cout << "just_radiate_tree_level2: Reading in numerical matrix..." << std::endl; sp_m_num = read_in_matrix(filename.c_str()); std::cout << "just_radiate_tree_level2: Reading in numerical leading matrix..." << std::endl; sp_m_num_leading = read_in_matrix(filenamel.c_str()); } else { // Calculate the scalar product matrix in this basis std::cout << "just_radiate_tree_level2: Calculating the scalar product matrix..." << std::endl; std::cout.flush(); // poly_matr sp_m=scalar_product_matrix(Basis); // cout << sp_m; // poly_matr sp_m_leading = leading(sp_m); - // sp_m_num = d_num(sp_m); - // sp_m_num_leading = d_num(sp_m_leading); + // sp_m_num = double_num(sp_m); + // sp_m_num_leading = double_num(sp_m_leading); dmatr sp_m=scalar_product_matrix_mem2(Basis, n_loop); if( sp_m.size() < 20 ) std::cout << sp_m; sp_m_num = read_in_matrix(filename.c_str()); sp_m_num_leading = read_in_matrix(filenamel.c_str()); } if( Basis.size()!=sp_m_num.size() ){ std::cout << "just_radiate_tree_level2: Warning: Non-matching number of basis size " << Basis.size() << " and scalar product matrix size " << sp_m_num.size() << "."<< std::endl << "This should happen only if states are tree level and matrix contains information for all states." << std::endl; } // For containing the scalar product cnum sp_num, sp_num_leading; // For containing the average ratio of amp^2 double ratio_average = 0.0; // The initial number of partons int n_initial = 2 * n_q + n_g; std::cout << "just_radiate_tree_level2: Running the shower... " << std::endl; // Make n_run different showers for (int n = 1; n <= n_run; n++) { std::cout << "just_radiate_tree_level2: Calculating amplitude... " << std::endl; // For speed testing //time_t t1, t2, t3; //t1 = clock(); // Reset n_parton in each run int n_parton = n_initial; // To contain the total amplitudes for Col_str's in basis // numbers stored in same order as in basis cvec tot_amp; uint n_states=Basis.size(); if( sp_m_num.size() < n_states ) n_states=sp_m_num.size(); //min( Basis.size(), sp_m_num.size() ); for (uint i = 0; i < n_states; i++) tot_amp.push_back(1.0); // Loop over emissions, where parton p, p=n_parton+1 ... n_initial+n_g_rad, is emitted // This is later to be replaced with a check of the cut of scale Q0 // and a check that we do not have more radiated partons than we can handle, roughly 7 for (int p = n_parton + 1; p <= n_initial + n_g_rad; p++) { // The p-1 various amplitudes for the p-1 partons to radiate, // to be replaced by shower amplitudes. // The shower interferences should look like // (p_left . p_right)/((p_new . p_right) (p_new . p_left)). // The amplitudes should be of from p_old^{\mu} /(p_old . p_new) // Note that the amps will be proportional to 4-vectors, p_old^{\mu} // All basis tensors should have same amplitudes, as this is set by kinematics cvec amps = generate_random_amplitudes(p ); // Loop over possible Col_strs in the basis for (uint Cs_num = 0; Cs_num < n_states; Cs_num++) { // Find right and left neighbors of parton of (the newly emitted) parton p int left = Basis.ca.at(Cs_num).find_nearest_smaller_left(p); int right = Basis.ca.at(Cs_num).find_nearest_smaller_right(p); // Adjust the amplitude // This should rather be // (p_left )/(p_new . p_left) - (p_right )/(p_new . p_right) tot_amp.at(Cs_num) *= (amps.at(left) - amps.at(right)); } } //t2 = clock(); //std::cout << "just_radiate_tree_level2: Generated event, took " << t2 - t1 << std::endl; // cout << "just_radiate: Ca1" << Ca1 << endl; // Calculate the scalar product std::cout << "just_radiate_tree_level2: Calculating |A|^2..." << std::endl; //t2 = clock(); sp_num = scalar_product_num_fast(tot_amp, tot_amp, sp_m_num); sp_num_leading = scalar_product_num_diagonal(tot_amp, tot_amp, sp_m_num_leading); //sp_num_leading = scalar_product_num_diagonal(tot_amp, tot_amp, sp_m_num); //t3 = clock(); /// cout << " done in " << t3-t2; // Write out result the first few times if (n <= 2) { std::cout << "Resulting Col_amp" << tot_amp << std::endl; std::cout << "with scalar prod " << sp_num << std::endl; std::cout << "In the infinity limit " << sp_num_leading << std::endl; } std::cout << "Run " << n << " The ratio (leading Nc) / (all terms) " << real( sp_num_leading / sp_num) << std::endl; ratio_average = ratio_average + real(sp_num_leading / sp_num) / n_run; } return ratio_average; } // Function for creating the name of the file containing the scalar product matrix // The argument leading should be true if it is a leading matrix that is to be saved. // Function for creating the name of the file containing the radiation amplitude matrix. // The argument leading should be true if it is a leading matrix that is to be saved. std::string Col_functions::ram_file_name( const Col_amp & Basis, bool leading) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; ss << "RAM_q"; int nq=Basis.at(0).n_quark(); ss << nq; ss << "_g"; int ng=Basis.at(0).n_gluon(); ss << ng; if ( leading ) ss << "_l"; if ( leading_cf ) ss << "_cfl"; ss << "_Nc"; ss << Nc; ss << ".dat"; filename=ss.str(); return filename; } // Function for reading in a numerical matrix given the file name. dmatr Col_functions::read_in_matrix(const char* filename) const{ //cout << filename << endl; // Read in file std::ifstream fin(filename); // Check that file exists if( !fin ){ std::cerr << "Col_functions::read_in_matrix: The file " << filename << " could not be opened." << std::endl; throw filename; } // Copy info from file to string std::string str((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()); // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "First char in matrix data file should be '{', it was: " << str.at(0) << std::endl; throw -1; } // Row to contain numbers dvec row; // To contain matrix of scalar products dmatr matr; // Read the string, starting from 0th element unsigned int i = 0; while (i < str.size() - 2) { i += 1; // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == ' ' or str.at(i) == '{') ) i++; // String to make a number of, and double to contain number std::string num_str; num_str.clear(); double num; // Keep reading the number while not ',' or '}' while ( i< str.size()-2 && (str.at(i) != ',' && str.at(i) != '}') ) { num_str.push_back(str.at(i)); i++; } // now, at(i), there is either , or } //std::cout << str.at(i); // num_str contains the string to make a number of std::istringstream parton_str_st( num_str ); parton_str_st >> num; // Add number to vector row.push_back(num); // If we have a new row if( i< str.size()-2 && str.at(i)=='}'){ // Save row in matrix, and empty row matr.push_back(row); row.clear(); //std::cout << "Intermediate matr : " << matr << std::endl; //std::cout << str.at(i); // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' ) ) { i++; } } // Otherwise just keep on reading the next number in row }; //std::cout << "read_in_matrix: Read from file: " << str << std::endl; //std::cout << "read_in_matrix: Constructed: " << matr << std::endl; return matr; } void Col_functions::write_out_spm(const dmatr & matr, const char* filename) const{ std::ofstream outfile(filename); outfile << matr; } void Col_functions::write_out_spm( const poly_matr & matr, const Col_amp & Basis, bool leading ) const{ // Creating file name (part) std::string filename = spm_file_name(Basis, leading); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << matr; } // Function for writing out a numerical scalar product matrix to the default filename and file path // The filename is constructed using info of the basis cnum Col_functions::scalar_product_num_diagonal(const Col_amp & Ca1, const Col_amp & Ca2, const dmatr & sp_matr) const{ // To contain the result cnum res=0; uint basis_size=Ca1.ca.size(); // Both Ca's and sp_matr should have same length time_t t1, t2, t3; t1=clock( ); // Make ordinary vectors of numerical information in Polynomials, to speed up multiplication cvec v1, v2; v1.reserve(basis_size); for (uint i=0; i< basis_size; i++){ // added conjugate of first arg 130524 - v1.push_back( conj( c_num( Ca1.ca.at(i).Poly ) ) ); + v1.push_back( conj( cnum_num( Ca1.ca.at(i).Poly ) ) ); }; if( &Ca1==&Ca2 ) v2=v1; else{ v2.reserve(basis_size); - for (uint i=0; i< basis_size; i++) v2.push_back( c_num( Ca1.ca.at(i).Poly ) ); + for (uint i=0; i< basis_size; i++) v2.push_back( cnum_num( Ca1.ca.at(i).Poly ) ); } t2=clock(); std::cout << "scalar_product_num_diagonal(Ca1,Ca2,dmatr): Decomposition done at time: " << t2-t1 << std::endl; // Then add contributions for (uint m1=0; m1< basis_size; m1++){ res=res+ v1.at(m1) *v2.at(m1) *sp_matr.at(m1).at(m1); } t3=clock(); std::cout << "scalar_product_num_diagonal(Ca1,Ca2,dmatr): Matrix multiplication done at time: " << t3-t2 << std::endl; return res; } void Col_functions::write_out_spm(const dmatr & matr, const Col_amp & Basis, bool leading) const{ //cout << "write_out_spm(dmatr, Ca, bool): Incoming matrix \n" << matr; // Creating file name (part) std::string filename = spm_file_name(Basis, leading); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << matr; } // Function for calculating the weight < M | T^(i) T^(j) | M > / (< M | M > (c_f or c_a) ), // used in the Sudakov for generating the next k_T. // The Ca should be | M >, and i and j are the partons evolved in the emission. // The factor (c_f or c_a) is present to compensate for factor built in in kinematics part cnum Col_functions::color_shower_weight(Col_amp Ca, int i, int j, int g_new) const{ // The amplitudes after emission Col_amp Cai = emit_gluon(Ca ,i, g_new); Col_amp Caj = emit_gluon(Ca ,j, g_new); cnum numerator=0; cnum denominator=1.0; int n_q=Ca.n_quark(); int n_g=Ca.n_gluon(); // If gluons only, take care of implicit scalar product if( Ca.gluons_only() ){ // The numerator int sign = ((n_g+1) % 2 ? -1 : 1); - numerator = c_num( 2*scalar_product (Cai, Caj) +2*sign*scalar_product(Cai, conjugate(Caj))); + numerator = cnum_num( 2*scalar_product (Cai, Caj) +2*sign*scalar_product(Cai, conjugate(Caj))); // Before the emission there is one parton less, so the sign is opposite sign = (n_g % 2 ? -1 : 1); - denominator = c_num( 2*scalar_product(Ca, Ca) +2*sign*scalar_product (Ca, conjugate(Ca)) ); + denominator = cnum_num( 2*scalar_product(Ca, Ca) +2*sign*scalar_product (Ca, conjugate(Ca)) ); } else if(n_q==1){ - numerator = c_num( scalar_product (Cai, Caj) ); - denominator = c_num( scalar_product (Ca, Ca) ); + numerator = cnum_num( scalar_product (Cai, Caj) ); + denominator = cnum_num( scalar_product (Ca, Ca) ); } // The c_f or c_a factor if ( Ca.ca.at(0).find_kind(i) == "g" ) denominator=denominator*Nc; else denominator=denominator*cf; return numerator/denominator; } // Function for calculating the radiation amplitudes associated with emissions from // the various partons, starting with n_q quarks and n_g gluons. // The content is C^m T^(i) T^(j) C^n as a matrix of matrices. // The factors associated with i and j stand in the same sub-matrix. // For example, the first n_basis*n_basis block corresponds to the coherent // emission from parton 1 and parton 1. The block to the right of this // corresponds to coherent emission of parton 1 and parton 2 etc. // Blocks further down in the matrix corresponds to first parton > 1. // TODO test leading terms make work for nq!=1 dmatr Col_functions::radiation_amplitue_matrix(uint n_q, uint n_g, int n_loop) const{ std::cout << "radiation_amplitue_matrix: calculating radiation amplitue matrix" << std::endl; // Total number of partons, for each q there is a qbar uint n_partons=2*n_q+n_g; // The gluon to be emitted int g_new=n_partons+1; // Make sure only used at tree level if (! (n_loop==0 && (n_q==1 or n_q==0)) ) { std::cerr << "radiation_amplitue_matrix: Function so far only intended for tree level processes with one open Quark_line." << std::endl; throw -1;} // The basis before emission std::cout << "radiation_amplitue_matrix: creating basis for " << n_q << " quark(s) (and as many anti-quarks) and " << n_g << " gluons..." << std::endl; Col_amp Basis=create_trace_basis(n_q, n_g, n_loop); uint basis_size=Basis.size(); std::cout << "radiation_amplitue_matrix: Constructed basis with " << Basis.size() << " basis vectors." << std::endl; // For remembering already calculated topologies std::map< std::string, Polynomial> mem_map; // To contain the resulting color weight matrices _l is the leading version dmatr matr; dmatr matr_l; matr.reserve( basis_size*n_partons ); matr_l.reserve( basis_size*n_partons ); // Initialize matrices to 0 for (uint i = 0; i < n_partons*basis_size; i++) { std::vector<double> rowi, rowi_l;// Create the row i for (uint j = 0; j < n_partons*basis_size; j++) { rowi.push_back(0); // Create entry j=0 rowi_l.push_back(0); // Create entry j=0 } matr.push_back(rowi); matr_l.push_back(rowi); } std::cout << "radiation_amplitue_matrix: Calculating 'topologies'..." << std::endl; // If there is only one q qbar pair get all topologies by allowing the q and qbar (1 and 2) // and 2 different gluons to radiate int partons_needed=n_partons; if(n_q==1) partons_needed=4; if(n_q==0) partons_needed=4; // Gluon 4 can stand in any position // Loop over possible emitters i and j for (int i = 1; i <= partons_needed; i++) { std::cout << "radiation_amplitue_matrix: Calculating 'topologies' for emitter " << i << std::endl; for (int j = 1; j <= partons_needed; j++) { // Loop over basis vectors (in the relevant basis before emission) for (uint m = 0; m < basis_size; m++) { for (uint n = 0; n < basis_size; n++) { //cout << "i " << i << " j " << j << " m " << m << " n " << n << endl; // Calculate element // The amplitudes after emission Col_amp Cai = emit_gluon(Basis.at(m), i, g_new); Col_amp Caj = emit_gluon(Basis.at(n), j, g_new); // Rename indices, and make string of new Col_amps, to use in map std::pair<Col_amp, Col_amp> Css_new = rename_indices(Cai, Caj); Col_amp Ca1 = Css_new.first; Col_amp Ca2 = Css_new.second; std::ostringstream Ca_string; Ca_string << Ca1 << Ca2; - //cout << d_num(scalar_product(Caj, Cai)) << " "; + //cout << double_num(scalar_product(Caj, Cai)) << " "; // Add result to map if (!(mem_map.count(Ca_string.str()) > 0)) { //cout << "adding term for" << endl << Ca_string.str(); Polynomial p; // to contain result if(n_q==1) p = scalar_product (Cai, Caj); else if(n_q==0) { // Add implicit gluon state int sign = ((n_g+1) % 2 ? -1 : 1); p = 2*scalar_product (Cai, Caj) + 2*sign*scalar_product( conjugate(Cai), Caj ) ; } p.simplify(); mem_map.insert(make_pair(Ca_string.str(), p)); } } // End of m-loop } // End of n-loop } // End of j-loop } // End of i-loop std::cout << "radiation_amplitue_matrix: Calculating numerical version..." << std::endl; - std::map< std::string, double> mem_map_num=d_num(mem_map); + std::map< std::string, double> mem_map_num=double_num(mem_map); // Taking the leading part for each "topology" and making numerical maps std::cout << "radiation_amplitue_matrix: Calculating leading numerical version..." << std::endl; - std::map< std::string, double> mem_map_l_num=d_num(leading(mem_map)); + std::map< std::string, double> mem_map_l_num=double_num(leading(mem_map)); // After this the mem_map is not needed anymore mem_map.clear(); std::cout << "radiation_amplitue_matrix: Calculating matrix entries..." << std::endl; // Loop over possible emitters i and j for (uint i = 1; i <= n_partons; i++) { std::cout << "radiation_amplitue_matrix: Calculating matrix entries for emitter " << i << std::endl; for (uint j = 1; j <= n_partons; j++) { // Loop over basis vectors (in the relevant basis before emission) for (uint m = 0; m < basis_size; m++) { for (uint n = 0; n < basis_size; n++) { // The amplitudes after emission Col_amp Cai = emit_gluon(Basis.at(m), i, g_new); Col_amp Caj = emit_gluon(Basis.at(n), j, g_new); // Rename indices, and make string of new Col_amps, to use in map std::pair<Col_amp, Col_amp> Css_new = rename_indices(Cai, Caj); Col_amp Ca1 = Css_new.first; Col_amp Ca2 = Css_new.second; std::ostringstream Ca_string; Ca_string << Ca1 << Ca2; // Find the value using the map double entry = mem_map_num[Ca_string.str()]; double entry_l = mem_map_l_num[Ca_string.str()]; // Insert element in right place uint row=basis_size*(i-1)+m; uint column= basis_size*(j-1)+n; matr.at(row).at(column)=entry; matr_l.at(row).at(column)=entry_l; } } } } // Verifying that the matrix is symmetric to accuracy std::cout << "radiation_amplitue_matrix: Checking symmetry... "; bool sym = check_symmetry(matr); if (!sym) throw -1; // Also save the numerical versions to files for future usage std::cout << "radiation_amplitue_matrix: Writing out color radiation amplitude matrix... " << std::endl; std::ofstream outfile(ram_file_name(Basis, false).c_str()); outfile << matr; //matr.clear(); std::cout << "done." << std::endl; std::cout << "radiation_amplitue_matrix: Writing out leading contributions to radiation amplitude matrix... " << std::endl; std::ofstream outfile_l(ram_file_name(Basis, true).c_str()); outfile_l << matr_l; std::cout << "done." << std::endl; matr_l.clear(); std::cout << "radiation_amplitue_matrix: Returning matrix... " << std::endl; return matr; } // Function for calculating the weight < M | T^(i) T^(j) | M > / (< M | M > (c_f or c_a) ), // used in the Sudakov, for generating the next k_T. // The amp should be | M >, and i and j are the partons involved in the emission. // The index g_new denotes the parton to be emitted and spm the scalar product matrix used // for numerical evaluation (before emission). // The factor (c_f or c_a) is present to compensate for factor built in in kinematics part. // In the momentum code i is the emitter and j is the spectator. // The matrix radamps contains the amplitudes associated with emission, // and can be calculated using radiation_amplitue_matrix. // The number of existing quarks is n_q and the number of gluons is n_g. cnum Col_functions::color_shower_weight(const cvec & amp, int i, int j, int g_new, const dmatr & radamps, const dmatr & spm_matr, int n_q, int n_g, int n_loop) const{ // Make sure only used at tree level if (! (n_loop==0 && (n_q==1 or n_q==0)) ) { std::cerr << "color_shower_weight: Function so far only intended for tree level processes with one quark line." << std::endl; throw -1;} //int n_partons=2*n_q+n_g; // Calculate the size of the basis before emission //uint basis_size=radamps.size()/n_partons; uint basis_size=spm_matr.size(); // To contain the numerator and denominator cnum numerator=0; cnum denominator=1.0; // The denominator <M| SPM |M> // This should work also in the gluons only case, as states implicit in spm_matr and in amp denominator = scalar_product_num_fast(amp, amp, spm_matr); // The c_f or c_a factor (1 is a q 2 is a qbar) if ( (i==1 or i==2) && n_q==1) denominator=denominator*cf; else denominator=denominator*Nc; // To find the numerator loop over color structures // Loop over basis vectors in | M > for (uint m = 0; m < basis_size; m++) { // Loop over basis vectors in < M | for (uint n = 0; n < basis_size; n++) { uint row=basis_size*(i-1)+m; uint column= basis_size*(j-1)+n; // This should work also in the gluons only case, as states implicit in rad_amps and in amp numerator += amp.at(m)*radamps.at(row).at(column)*amp.at(n); } } return numerator/denominator; } // The factorial of an int int Col_functions::factorial (int i) const{ if(i<0) { std::cerr << "Col_functions: factorial: intended for int >=0, argument was " << i << std::endl; std::cerr.flush(); throw -1; } if (i==0) return 1; return factorial(i-1)*i; // Recursive call } // Function for finding out the number of the vector with only one quark_line // with the color information of ql. // The quark_line should be normal ordered before usage. // TODO does not yet work for gluons only think of the fact that half the state is implicit int Col_functions::quark_line_number( int n_q, int n_g, const quark_line & ql) const{ if( !( ql.at(0)==1 && n_q==1 && ql.at( ql.size()-1 )==2 ) && !( ql.at(0)==1 && n_q==0) ){ std::cerr << "quark_line_number: Expects a normal ordered quark_line with 1 as the first entry " << " (and 2 the last entry if only gluons)." << std::endl << "Received " << ql << std::endl; throw -1; } if( n_q==0 ){ std::cerr << "quark_line_number: Does not yet work for gluons only." << std::endl; throw -1; } // To contain the number of the vector int tens_num=0; // If there is only one vector if( (n_q==0 && n_g==2) or (n_q==1 && n_g==1) ) return 0; // Loop over positions and count up tenors number // pos 0 should contain 1 and is irrelevant, pos last is similarly irrelevant if quark_line has quarks. for(int pos=1; pos <= static_cast<int>(ql.size())-1-n_q; pos++){ // Look among already found and see how many where smaller // The effective parton number shoudl be counted down with this number // Alternatively one can look to the right and see how many where larger // Thus what counts is the number of smaller integers to the right int parton=ql.at(pos); for(int p=1; p< pos; p++) if( ql.at(p) < ql.at(pos)) parton=parton-1; //cout << "factorial " << factorial(n_g+n_q-1-pos) << endl; //cout << "other part " << (parton-1-n_q-1) << endl; tens_num=tens_num+factorial(n_g+n_q-1-pos)*(parton-1-n_q-1); } return (tens_num); } // Function for locating the parton parton in the normal ordered basis, -// given the number of the vector vec_num, and the number of quarks and gluons in the basis -std::pair<int,int> Col_functions::find_parton(int parton, int vec_num, int n_q, int n_g, int n_loop) const{ +// given the number of the vector vecnum_num, and the number of quarks and gluons in the basis +std::pair<int,int> Col_functions::find_parton(int parton, int vecnum_num, int n_q, int n_g, int n_loop) const{ if( !(n_q==1 or n_q==0 or n_q==2) or n_loop >0 ){ std::cerr << "find_parton: Function only intended for special case of 0-2 q qbar pairs, and a tree level basis." << std::endl; throw -1; } if( parton==1 && n_q == 1) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==1 && n_q == 0) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==2 && n_q==1 ) return std::make_pair( 0, n_g+1 ); // qbar has last pos // If n_q=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*n_g! options for each breakpoints, // where the factor 2 comes from the 2 options for the first anti-quark // we thus have int break_after=n_g; if (n_q == 2) { // There are n_q!n_q!n_g! assignments for each break - break_after = break_after-(vec_num) / (2*2*factorial(n_g)); - vec_num= vec_num -(n_g-break_after)*(2*2*factorial(n_g)); + break_after = break_after-(vecnum_num) / (2*2*factorial(n_g)); + vecnum_num= vecnum_num -(n_g-break_after)*(2*2*factorial(n_g)); //cout << "find_parton: break_after " << break_after << endl; - //cout << "find_parton: vec_num after break compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num after break compensation " << vecnum_num << endl; // Check if first parton is 1 or 3, there are 2 options for each from the qbar being 2 or 4 bool first3=false; - if ( ( vec_num / ( 2*factorial(n_g)) ) ) first3=true; + if ( ( vecnum_num / ( 2*factorial(n_g)) ) ) first3=true; //cout << "The first is 3? "<< first3; - vec_num= vec_num % (2*factorial(n_g)); + vecnum_num= vecnum_num % (2*factorial(n_g)); // Special cases of asked for quark if(parton == 1){ if(!first3) return std::make_pair(0,0); else return std::make_pair(1,0); } if(parton == 3){ if(first3) return std::make_pair(0,0); else return std::make_pair(1,0); } - //cout << "find_parton: vec_num after first quark compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num after first quark compensation " << vecnum_num << endl; } // The number of partons gluons with lower parton number than the parton // (and possibly standing to the right, hence -1) int lower=parton-2-n_q; //if (n_q==2 ) lower=parton -2*n_q-1; if ( n_q==2 ) lower=parton -2*n_q-1; // quarks don't count // To contain the number of gluons standing to the right of the parton under consideration // and being smaller than 2, noting to the right is smaller than 2, hence 0 // needed for special case of gluons only int lower2=0; // Number of partons to the right, when checking the various positions int n_r=0; // For quark lines(s) all gluons except the one under consideration matter if ( n_q>0 ) n_r=n_g-1; // For a gluon line we have to subtract 1 extra as we start read at place 1 anyway else if ( n_q==0 ) n_r=n_g-2; // Was the gluon 2 found or not, needed for special case of gluons only bool found_2=false; // The resulting quark_line number (can change only in case 2 q qbar pairs) int ql_num=0; // Loop over possible places of the gluon, start with checking place 1 // n_l_r contains the number of partons lower than the parton at the position under consideration int n_l_r=0; // To make compiler not warn int looped_over=0; while(true){ if( n_q==1 ) { - n_l_r=vec_num/factorial(n_r); // The result of integer division - vec_num=vec_num%factorial(n_r); // The rest after integer division + n_l_r=vecnum_num/factorial(n_r); // The result of integer division + vecnum_num=vecnum_num%factorial(n_r); // The rest after integer division } if( n_q==2 ) { // If both qbars to right extra factor 2! if( break_after > looped_over) { //cout << "break_after > looped_over=" << looped_over << endl; - n_l_r=vec_num/(factorial(n_r)*2); // The result of integer division - vec_num=vec_num%(factorial(n_r)*2); // The rest after integer division + n_l_r=vecnum_num/(factorial(n_r)*2); // The result of integer division + vecnum_num=vecnum_num%(factorial(n_r)*2); // The rest after integer division } else{ // Check if it is time to compensate for first qbar and second q if ( break_after==looped_over ){ //cout << "break_after == looped_over=" << looped_over << endl; - //cout << "qbar compensation starting with vector number " << vec_num << endl; + //cout << "qbar compensation starting with vector number " << vecnum_num << endl; //cout.flush(); // If we have already looped over all gluons if(n_r==-1) n_r=0; //cout << "find_parton: n_r " << n_r << endl; //cout.flush(); // Special cases if asked for first qbar = 2 or 4 if( parton==2){ // All gluons which are not looped over are to right - if (vec_num/(factorial(n_r+1))==0) return std::make_pair(0,break_after+1); + if (vecnum_num/(factorial(n_r+1))==0) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_g-break_after+1); } if( parton==4){ - if (vec_num/(factorial(n_r+1))==1) return std::make_pair(0,break_after+1); + if (vecnum_num/(factorial(n_r+1))==1) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_g-break_after+1); } - //cout << "find_parton: vec_num before qbar compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num before qbar compensation " << vecnum_num << endl; //cout << "find_parton: n_r " << n_r << endl; - vec_num=vec_num%(factorial(n_r+1)); // compensated for first qbar - - - //cout << "find_parton: vec_num after first qbar compensation " << vec_num << endl; + vecnum_num=vecnum_num%(factorial(n_r+1)); // compensated for first qbar + + + //cout << "find_parton: vecnum_num after first qbar compensation " << vecnum_num << endl; // After looking at first qbar the ql_num should be 1 ql_num=1; } //else cout << "break_after < looped_over=" << looped_over << endl; // Otherwise the situation is as for gluons only - n_l_r=vec_num/(factorial(n_r)); // The result of integer division - vec_num=vec_num%factorial(n_r); // The rest after integer division + n_l_r=vecnum_num/(factorial(n_r)); // The result of integer division + vecnum_num=vecnum_num%factorial(n_r); // The rest after integer division } //cout << "n_l_r " << n_l_r << endl; } else if (n_q==0){ //cout << "found 2? " << found_2 << endl; if( found_2 ){ //cout << "New n_r " << n_r << endl; - n_l_r=vec_num/factorial(n_r); - vec_num=vec_num%factorial(n_r); + n_l_r=vecnum_num/factorial(n_r); + vecnum_num=vecnum_num%factorial(n_r); //cout << "n_l_r " << n_l_r << endl; } else{ // First check if 2 was at the position //cout << "Before finding 2 n_r " << n_r << endl; - //cout << "Before finding 2 vec_num " << vec_num << endl; - n_l_r=vec_num/(factorial(n_r)); // If 2 was at the place, then all orders matter + //cout << "Before finding 2 vecnum_num " << vecnum_num << endl; + n_l_r=vecnum_num/(factorial(n_r)); // If 2 was at the place, then all orders matter //cout << "Before finding 2 n_l_r " << n_l_r << endl; // If 2 was found if( n_l_r==lower2 ) { found_2=true; - vec_num=vec_num%(factorial(n_r)); } + vecnum_num=vecnum_num%(factorial(n_r)); } else{ // Recalculate knowing that we did not find 2 - n_l_r=vec_num/(factorial(n_r)/2); // There is only one relative order of gluon 2 and 3 - vec_num=vec_num%(factorial(n_r)/2); // The rest after integer division + n_l_r=vecnum_num/(factorial(n_r)/2); // There is only one relative order of gluon 2 and 3 + vecnum_num=vecnum_num%(factorial(n_r)/2); // The rest after integer division } } } if( n_l_r==lower ) break; // Number smaller to right = number smaller than parton to right if( n_l_r < lower ) lower--; n_r--; looped_over++; // needed to compare with break in 2q case //cout << "n_l_r " << n_l_r << endl; } //if( n_q==1) return make_pair( 0, n_g-n_r ); //else if( n_q==0 ) return make_pair( 0, n_g-n_r-1 ); // If we have 2 quark lines we have to first figure out the quark line number //int ql_num=n_g-break_after; // and then the number in the quark_line int pos_num=n_g-n_r; // n_q==1 case if( n_q==0) pos_num=n_g-n_r-1; else if( n_q==2 && ql_num==0) pos_num=n_g-n_r; else if(n_q==2 && ql_num==1) pos_num=n_g-break_after-n_r; return std::make_pair( ql_num, pos_num ); } // Function for finding the new vector number in the basis with n_p+1 partons // after inserting a new parton with larger parton number at the place place. -// The old vector has number old_num, and there were, before emission +// The old vector has number oldouble_num, and there were, before emission // n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. // Function so far only intended for tree-level processes with at most 2 qqbar pairs. // Function explicitly tested for initial states with 2qqbar pairs and up to 5 gluons, // 1qqbar pair and up to 7 gluons, 0 qqbar pairs and up to 8 gluons. -int Col_functions::new_vector_number( int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const{ +int Col_functions::new_vector_number( int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const{ //cout << "new_vector_number: inserting at place (" << place.first <<", " << place.second << ") for n_g=" << n_g << " and n_q=" << n_q<< endl; std::cout.flush(); if( !( n_q==0 or n_q==1 or n_q==2 ) ){ std::cerr << "new_vector_number: Function so far only intended for special case of 1 quark line or 2 open quark lines." << std::endl; std::cerr.flush(); throw -1; } if( n_q==2 && n_g>5 ){ std::cerr << "new_vector_number: WARNING: function only tested for 2 q qbar pairs and at most 5 gluons." << std::endl; } if( n_q==1 && n_g>7 ){ std::cerr << "new_vector_number: WARNING: function only tested for 1 q qbar pair and at most 7 gluons." << std::endl; } if( n_q==0 && (n_g>8 or n_g==1) ){ std::cerr << "new_vector_number: WARNING: function only tested 2-8 gluons when no quarks." << std::endl; } if(place.second == 0 && n_q>0 ){ std::cerr << "qber: Cannot insert gluon at place 0, reserved for quark." << std::endl; std::cerr.flush(); throw -1; } else if(place.second==n_g+2 && n_q==1){ std::cerr << "new_vector_number: Cannot insert gluon at place n_g+2, reserved for qbar." << std::endl; std::cerr.flush(); throw -1;} else if(n_q==2 && (0 > place.second or place.second > n_g+1+1) ){ std::cerr << "new_vector_number: Cannot insert gluon outside range 1...n_g ." << std::endl; std::cerr.flush(); throw -1; } else if( 0 > place.second or place.second > n_g+1+n_q){ std::cerr << "new_vector_number: Cannot insert gluon outside range 1...n_g ." << std::endl; std::cerr.flush(); throw -1; } // If asked to insert a gluon at place 0, it should be inserted beyond the last place instead if( n_q==0 && place.second==0 ){ place.second=n_g; //cerr << "new_vector_number: Cannot insert gluon at place 0 inserting at last place " << place.second << " instead." << endl; //cerr.flush(); } // In the gluons only case we need to keep track of the next position relative to gluon 2 int place2=0; - int fnext=old_num; // To contain the remaining part of tensor number + int fnext=oldouble_num; // To contain the remaining part of tensor number // If there are 2 quarks, there is an effective scalar place in {1,g1...gm,2}{3,gm+1...4} // obtained by crossing 2}{3 out. This place is given by // (place in the first ql) if it is in the first ql // (number of gluons in first ql) + place in second if it's in the second ql // If n_q=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*2*n_g! options for each breakpoints, // where the factor 2*2 comes from the options for the quarks // we thus have int break_after=0; int fsplit=0; // Contribution from where the ql is split int fq1=0; // Contribution from first quark if (n_q == 2) { // To contain the number of gluon in the first ql // First the number of gluons in 2nd ql // For each gluon in 2nd ql there was // 2 (from n_q)*2 (from n_qbar)*factorial(n_g) (from the gluons) // tensors with lower numbers from other breaks // Note that for all other splitting the factor from the first q is relevant - break_after = old_num /( 2*2*factorial(n_g) ) ; - - - fnext= old_num % (2*2*factorial(n_g)); + break_after = oldouble_num /( 2*2*factorial(n_g) ) ; + + + fnext= oldouble_num % (2*2*factorial(n_g)); // The factor from the split - fsplit=old_num-fnext; + fsplit=oldouble_num-fnext; break_after=n_g-break_after; //cout << "new_vector_number: break_after " << break_after << endl; //cout << "new_vector_number: fsplit " << fsplit << endl; //cout << "new_vector_number: fnext after finding break " << fnext << endl; // Check if the first quark is 1 or 3 // The factor from the first quark fq1=(fnext/( 2*factorial(n_g) ))*2*factorial(n_g); // Special case of 2 quark lines of equal length, then there is no option for q1 (it is 1) if( break_after==n_g-break_after ) fq1=0; // 1 is first, no factor from first quark //cout << "new_vector_number: fq1 " << fq1 << endl; fnext= fnext % (2*factorial(n_g)); //cout << "new_vector_number: fnext after counting first quark " << fnext << endl; // First treat the special case when the ql's switch place // This can happen when // 1) The 2nd ql gets longer than the first such as // emission from 6 in {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // 2) The 2nd is one shorter than the first but has the q=1 standing first // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} if( ((break_after==n_g-break_after) && place.first==1) or ((break_after==n_g+1-break_after) && fq1>0 && place.first==1)) { //cout << "WARNING: this is the hard case" << endl; // Locate all partons in old ql std::vector< std::pair<int, std::pair<int,int> > > parton_and_place; for( int p=1; p<=n_g+2*n_q; p++ ){ - std::pair<int,int> p_place=find_parton(p, old_num, n_q, n_g, n_loop); + std::pair<int,int> p_place=find_parton(p, oldouble_num, n_q, n_g, n_loop); make_pair(p,p_place); // note that parton p stands at place p-1 parton_and_place.push_back( make_pair(p,p_place) ) ; } //cout << "new_vector_number: reconstructed all parton places" <<endl; // To contain new tensor number int new_num=0; // First calculate the number from the split // After the split the number of gluons in the qls are // break_after in the 2nd ql // This is true for both special cases of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} new_num=(break_after)*2*2*factorial(n_g+1); //cout << "new_vector_number: new_num after adding split "<< new_num <<endl; // Then calculate the number from the first quark // There is a factor 2 from the anti-quark and a factor (n_g+1)! from the gluons // if the first factor is 3, which it is in swapings of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} if( (break_after==n_g-break_after) && place.first==1 ) new_num=new_num+2*factorial(n_g+1); // In cases of form {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} // the factor from the first quark is 0, and //cout << "new_vector_number: new_num after first quark "<< new_num <<endl; // For the anti-quark qbar=2, check if it stands in first or 2nd ql // If it stands in first ql, there is no contribution to the tensor numbers // otherwise there is a contribution from all orders of gluons in 2nd ql if( parton_and_place.at(2-1).second.first==0) new_num+=factorial(break_after); //cout << "new_vector_number: new_num after first anti-quark "<< new_num <<endl; // The 2nd anti-quark and 2nd quark gives no contribution // Then calculate the contribution to the new tensor number from the gluons // Loop over gluons for( int p=5; p<=n_g+2*n_q; p++ ){ // The NEW scalar place of the gluon int p_place=parton_and_place.at(p-1).second.second; // contribution from pos in ql // If a gluon was standing in the first ql it is afterwards standing in the 2nd // All gluons in the new first (old 2nd ql) are therefore standing in front // they are n_g-break_after +1 (the +1 is added below) if(parton_and_place.at(p-1).second.first==0) p_place+=(n_g-break_after); // If the new gluon was inserted before the parton, then the p_place should be increased with one // This happens when the parton stands in the new 2nd ql= old first ql (always) // or when the gluon is in the new first ql, but after the emitter if( parton_and_place.at(p-1).second.first==0 or place.second <=parton_and_place.at(p-1).second.second ) p_place++;// ?? is this right //cout << "new_vector_number: looping over gluons, gluon "<< p <<" at new place " << p_place <<endl; // See how many smaller stand to the right int smaller_right_of_p=0; // Check all gluons with smaller number than p to see how many stand to the right for( int ps=5; ps<p; ps++ ){ int ps_place=parton_and_place.at(ps-1).second.second; // Contribution from pos in ql if(parton_and_place.at(ps-1).second.first==0) ps_place+=(n_g-break_after); // From first ql if( parton_and_place.at(ps-1).second.first==0 or place.second <=parton_and_place.at(ps-1).second.second ) ps_place++; // From new g // If the smaller parton ps stand to the right of the parton p if( ps_place>p_place ) smaller_right_of_p++; } //cout << "new_vector_number: Found number of smaller to the right "<< smaller_right_of_p <<endl; // Increase the new tensor number for partons to the right if( smaller_right_of_p>0 ){ // If the gluon stands in the first ql (former 2nd), there is also a factor of 2 from the // choice of anti-quarks // All smaller_right_of_p ways of replacing p with a smaller number contributes a factor // factorial(gluons to the right), and there are smaller_right_of_p options if(parton_and_place.at(p-1).second.first==1) new_num+=2*smaller_right_of_p*factorial(n_g+1-p_place); else new_num+=smaller_right_of_p*factorial(n_g+1-p_place); } //cout << "new_vector_number: new_num after adding info from gluon " << p << " at place " << p_place<< " " << new_num <<endl; } // End of loop over gluons // Add contribution from the new gluon. The gluon number is larger than every other gluon number, // so all gluons standing to the right are smaller // In the new first ql the new gluon is at place place.second // and there are in total n_g-break_after+1 gluons, so the total number of gluons to the right is // break_after+(n_g-break_after+1) -place.second int n_right_new=(n_g+1)-place.second; // The overall contribution also has a factor 2 from the qbar choice // The factor 2 is from the qbar, factorial(n_right_new) from ordering gluons new_num+=2*n_right_new*factorial(n_right_new); //cout << "new_vector_number: new_num after adding info from new gluon " << new_num <<endl; return new_num; } // End of hard case } - if ( n_q==0 ) place2=find_parton(2, old_num, n_q, n_g, n_loop).second; + if ( n_q==0 ) place2=find_parton(2, oldouble_num, n_q, n_g, n_loop).second; //cout << "The place of 2 was " << place2 << endl; //cout.flush(); // Initially we had q=1, g1,....gn, qbar=2 // After the insertion at place place we have q=1, g1, ...g(place-1), g_new, g(place), ...gn, qbar=2 // The number of the new vector can be seen as a sum of 3 factors: // f1 = the contribution from q=1, g1, ...g(place-1) // fnew= the contribution from the newly inserted gluon // f2= the contribution from g(place), ...gn, qbar=2 (this contribution is the same as before the insertion) // Of these contributions fnew is easiest to calculate it is simply: int fnew=0; // If one ql if( n_q==1 ) fnew=(n_g - place.second + 1)*factorial(n_g - place.second + 1); // Special case of place after last gluon else if( n_q==0 && place.second == n_g+1 ) fnew=0; // If one closed ql and the parton is to the right of 2 else if( n_q==0 && place.second > place2 && place.second < n_g+1) fnew=(n_g - place.second )*factorial(n_g - place.second ); // If the parton is before 2 else if( n_q==0 && place2 >= place.second ) fnew=(n_g - place.second )*(factorial(n_g - place.second )/2); //Rel order 2 3 fixed // as all indices standing to the right are smaller //cout << "fnew " << fnew << endl; // i.e. the (number of partons with smaller number to the left)(number of partons to the left)! // The factor f1 is trickier to obtain. Its initial value (before emission) is // sum_{i=1}^(place-1) n_sri (n_p-i)! // where n_sri denotes the number of smaller partons standing to the rigt at place i // these factors are not known, but can be calculated. // Once these factors are known the final value of f1 can also be obtained from // sum_{i=1}^(place-1) n_sri (n_p-i+1)! // where the extra 1 in the factorial is present as after the emission there is // one more parton standing to the right. int f1new=0; // To contain the new f1 int f1old=0; // To contain the old f1 int scalar_place=0; // The scalar place if( place.first!=0 ) scalar_place=break_after; scalar_place+=place.second; //cout << "Scalar place " << scalar_place << endl; if( n_q==2 ) { fnew=(n_g + 1- scalar_place )*factorial(n_g + 1- scalar_place ); // If g inserted in first ql, there is an extra factor of 2 from interchanging quarks //if( break_after>=scalar_place-1 ) fnew*=2; if( place.first==0 ) fnew*=2; //cout << "new_vector_number: scalar_place " << scalar_place << endl; //cout << "new_vector_number: fnew " << fnew << endl; } // end of if( n_q==2 ) // The last relevant parton for calculating the contribution to f1 int last_rel=scalar_place-1; // If it's a quark_line and the new gluon is to be inserted after the last gluon // then the last place with a q, is not important //if(scalar_place==n_g+1) last_rel--; // If the g is on the first ql //if(place.second==break_after+1) last_rel--; // if the g is in the second ql //if( place.second+break_after==n_g+1 ) last_rel--;??? //cout << "last_rel " << last_rel << endl; //cout.flush(); for (int i=1; i<=last_rel; i++){ //cout << "i " << i << endl; // number of partons to the right int n_r=n_g-i; if( n_q==0 ) n_r--; // nsri is the number of smaller partons to the right int nsri=0; if (n_q==1 ) { // Note integer division, how many times do we find the factorial nsri=fnext/(factorial(n_r)); // The contribution to the old f1 f1old+=nsri*factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(factorial(n_r+1)); } else if (n_q==2) { int q_part=1; // Keeps track of a possible multiplicative factor from first qbar option if ( i <= break_after) q_part=n_q; //if (place.first==0) q_part=2; // Note integer division, how many times do we find the factorial nsri=fnext/(q_part*factorial(n_r)); // The contribution to the old f1 f1old+=nsri*q_part*factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(q_part*factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(q_part*factorial(n_r+1)); //2*2! //cout << "i " << i << " nsri " << nsri << endl; // Take care of factor from first qbar "when we reach it" // if( i == break_after && place.second!=break_after+1 && break_after=n_g-break_after) { if( i == break_after ) { // Note integer division, how many times do we find the factorial nsri=fnext/(factorial(n_r)); int f1old_before_qbar=f1old; // The contribution to the old f1 f1old+=nsri*factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(factorial(n_r)); // if the gluon is inserted just before the qbar the factor from // the qbar should be taken care of in the f2 instead // (f1old still has to be recalculated as it is used for finding f2 which is unchanged) // just added 11 02 03 //if( !break_after==n_g-break_after ){ // If the g is NOT inserted just before the qbar // i.e. if it is either inserted in 2nd ql or at other place than break_after+1 if ( !(scalar_place == break_after+1) or place.first ==1){ // The contribution to the new f1 from the anti-quark //f1new+=nsri*(factorial(n_r+1)); // There are (n_r+1)! options to have 2 before 4 // and we should only have a contribution if 4 is before 2 f1new+=nsri*(factorial(n_r+1)); } else { // If the gluon is inserted just before the qbar // The factor should be accounted for in fnew instead // If the second qbar was 4, this should be compensated for in fnew if( f1old_before_qbar!=f1old && place.first ==0){ // The factor is the number of ways of ordering the gluons in 2nd ql // The q and qbar are fixed and do not contribute // There is no factor if no gluons to the right ?? // There are n_r gluons in the second ql (also after emission) //if (n_r!=0) Why? even with 0 gluons to the right there is one lower option from qbar fnew=fnew+factorial(n_r); } } } } // end if (n_q==2) // If i is after the place of 2 we have the standard case else if(n_q==0 && i >= place2) { //cout << "i " << i << " after= 2 " << " n_r " << n_r << endl; nsri=fnext/(factorial(n_r)); f1old+=nsri*factorial(n_r); fnext=fnext%(factorial(n_r)); f1new+=nsri*(factorial(n_r+1)); } // If i is before the place of 2 ordering of 2 and 3 irrelevant else if(n_q==0 && i < place2) { //cout << "i " << i << " before 2 " << " n_r " << n_r <<endl; nsri=fnext/(factorial(n_r)/2); f1old+=nsri*(factorial(n_r)/2); fnext=fnext%(factorial(n_r)/2); f1new+=nsri*(factorial(n_r+1)/2); } /* cout << "f1new " << f1new << endl; cout << "f1old " << f1old << endl; cout << "fnext " << fnext << endl; cout << "f1new " << f1new << endl; */ } /* cout << "f1old " << f1old << endl; cout << "f1new " << f1new << endl; cout << "fnew " << fnew << endl; */ // Once f1 is known then f2 is simply, int f2=0; if( n_q==2 ) { // For 2 qqbar pairs we have to take care of factor from split - f2=old_num-f1old-fsplit-fq1; + f2=oldouble_num-f1old-fsplit-fq1; } - else f2=old_num-f1old; + else f2=oldouble_num-f1old; //cout << "f2 " << f2 << endl; // and the contribution from f2 is the same as before (irrespectively of position relative 2) // In the case of 2 quarks, if there was a contribution to the tensor number from the split // this has to be added back - // break_after = old_num / (2*factorial(n_g)); + // break_after = oldouble_num / (2*factorial(n_g)); // The contribution was (n_g-break_after)*(2*factorial(n_g)) // The special case of ql swapping should already have been taken care of if( n_q==2 ) { // Calculating new factor from the split int fsplitnew=0; //fsplitnew=fsplit*(n_g+1); // New factor from split after insertion in first ql if(place.first==0) { // The length of 2nd ql is then still (n_g-break_after) // and for each break there are 2*factorial(n_g+1) options // The lengths of first and 2nd ql are never equal as 1st was at least as long as 2nd // -> factor 2 from option of first q fsplitnew=(n_g-break_after)*2*2*factorial(n_g+1); } // If the insertion was in the second ql (of new length n_g-break_after+1) if(place.first==1) { // The length of the 2nd ql is now (n_g+1-break_after) // all gluon orders matter, and the qbar matters // Also the factor fomr the split, coming from all "lower splits" // has a factor 2 as the q matters for the lower splits fsplitnew=(n_g+1-break_after)*2*2*factorial(n_g+1); // 1*2*2! // Unless the lengths are equal there is also a factor from the q //if( break_after!=n_g-break_after+1 ) fsplitnew*=2; // This is not right one has to loop over splitings some may be eual and some not.... } // New factor from first q in the normal case //cout << "fq1 " << fq1 << endl; int fq1new=fq1*(n_g+1); //cout << "fq1new " << fq1new << endl; // If after emission the 2nd ql has the same length as the first if(place.first==1 && break_after==n_g+1-break_after){ fq1new=fq1new/2; // How can this be right?? There should be no factor from first quark then } f1new=fsplitnew+fq1new+f1new; /* cout << "fsplitnew " << fsplitnew << endl; cout << "fq1new " << fq1new << endl; cout << "f1new " << f1new << endl; cout << "fnew " << fnew << endl; */ } // End of n_q==2 // The total vector number is just the sum of the parts //cout << "total " << f1new+fnew+f2 << endl; return f1new+fnew+f2; } // Function for finding the new vector numbers in the basis for n_p+1 partons // after radiating a new parton from the parton emitter. -// The old vector has number old_num, and there were, before emission +// The old vector has number oldouble_num, and there were, before emission // n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. // For emission from q or qbar there is only one resulting color structure, // and -1 is returned in the place of the absent color structure. // The second vector, where the new parton is inserted before the emitter // comes with a minus sign in the new total amplitude. // Function so far only intended for tree-level processes with at most 2 qqbar pairs. // TODO make work more general, so far only intended for at most 2 qqbar pairs at tree level. -std::pair<int, int> Col_functions::new_vector_numbers(int old_num, int emitter, int n_q, int n_g, int n_loop) const{ +std::pair<int, int> Col_functions::new_vector_numbers(int oldouble_num, int emitter, int n_q, int n_g, int n_loop) const{ //cout << "new_vector_numbers: emission from " << emitter <<" for n_g=" << n_g << " and n_q=" << n_q<< endl; if( !(n_q==0 or n_q==1 or n_q==2) or n_loop!=0 ){ std::cerr << "new_vector_numbers: Function only intended for special case of 0-2 q qbar pair at tree level. For the general case use the general version." << std::endl; throw -1; } // Find the place of the parton - std::pair<int,int> parton_place=find_parton( emitter, old_num, n_q, n_g, n_loop); + std::pair<int,int> parton_place=find_parton( emitter, oldouble_num, n_q, n_g, n_loop); //cout << "new_vector_numbers: Parton " << emitter << " found at place (" << parton_place.first << " " << parton_place.second << ")" << endl; // If the q or qbar is radiating, there is only one term if(parton_place.second==0 && n_q>=1)// If the q is radiating - return std::make_pair(new_vector_number(old_num, std::make_pair(parton_place.first,parton_place.second+1), n_q, n_g, n_loop),-1); + return std::make_pair(new_vector_number(oldouble_num, std::make_pair(parton_place.first,parton_place.second+1), n_q, n_g, n_loop),-1); else if(parton_place.second==n_g+1 && n_q==1) - return std::make_pair(-1,new_vector_number(old_num, parton_place, n_q, n_g, n_loop)); + return std::make_pair(-1,new_vector_number(oldouble_num, parton_place, n_q, n_g, n_loop)); // If the emitter is a g there are two different terms std::pair<int,int> first_insertion_place, second_insertion_place; if( n_q==0 && parton_place.second!=0) { first_insertion_place=std::make_pair(parton_place.first, parton_place.second + 1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second ); } // Special case that the emitter is standing first else if(n_q==0 && parton_place.second==0){ first_insertion_place=std::make_pair(0, 1); second_insertion_place=std::make_pair(0, n_g ); } else if(n_q==1){ // special cases already excluded first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } else if(n_q==2){ // special case of quark already excluded // If the emitter is an anti-quark if(emitter==2 or emitter==4){ - return std::make_pair(-1,new_vector_number(old_num, parton_place, n_q, n_g, n_loop)); + return std::make_pair(-1,new_vector_number(oldouble_num, parton_place, n_q, n_g, n_loop)); } // Emitter is a gluon, and is thus not standing in the ends first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } - int first_tens = new_vector_number(old_num, first_insertion_place, n_q, n_g, n_loop); - int second_tens = new_vector_number(old_num, second_insertion_place, n_q, n_g, n_loop); + int first_tens = new_vector_number(oldouble_num, first_insertion_place, n_q, n_g, n_loop); + int second_tens = new_vector_number(oldouble_num, second_insertion_place, n_q, n_g, n_loop); //cout << "new_vector_numbers: First tensor was " << first_tens << endl; return std::make_pair( first_tens, second_tens ); } // Function for finding the new vector numbers in the basis for n_p+1 partons // after radiating a new parton from the parton emitter. // The old vector is Cs, and after emission it is decomposd in the new basis // NewBasis. // Returned is the number of the new basis vectors. // For emission from q or qbar there is only one resulting color structure, // and -1 is returned in the place of the absent color structure. // The second vector, where the new parton is inserted before the emitter // comes with a minus sign in the new total amplitude. std::pair<int, int> Col_functions::new_vector_numbers( const Col_str & Cs, int emitter, const Col_amp & New_basis ) const{ // Number of new gluon int new_g=Cs.n_gluon()+2*Cs.n_quark()+1; // To contain the numbers of the new non-zero vectors int plus_comp=-1; int minus_comp=-1; // The vector after emission Col_amp Ca=emit_gluon(Cs, emitter, new_g); //cout << "new_vector_numbers: Ca " << Ca << endl; poly_vec vec=decompose(Ca, New_basis); // Loop over vector entries to identify the non-zero ones for (uint veci= 0; veci < New_basis.size(); veci++) { - if( d_num(vec.at(veci))!=0 ){ - if(d_num(vec.at(veci)) > 0){plus_comp=veci;} - if(d_num(vec.at(veci)) < 0){minus_comp=veci;} + if( double_num(vec.at(veci))!=0 ){ + if(double_num(vec.at(veci)) > 0){plus_comp=veci;} + if(double_num(vec.at(veci)) < 0){minus_comp=veci;} } } std::pair<int, int> res= std::make_pair(plus_comp,minus_comp); return res; } } diff --git a/Core/Col_functions.h b/Core/Col_functions.h --- a/Core/Col_functions.h +++ b/Core/Col_functions.h @@ -1,685 +1,685 @@ // -*- C++ -*- /* * Col_functions.h * Contains declarations of the class Col_str and associated types and operators * Author: Malin Sjodahl */ #ifndef COLORFULL_ColorFunctions_h #define COLORFULL_ColorFunctions_h #include <list> #include <map> #include <tr1/memory> #include <algorithm> #include <limits> #include "Col_amp.h" -// TODO Conjugation should be of first argument in scalar product!!! -// TODO Fix this also in scalar product matrices (at least in mem_version) - namespace ColorFull { using std::tr1::shared_ptr; /////////////////////// DECLEARING OPERATORS ///////////////////// /// Defining + operator to be able to erase elements at place std::list<int>::iterator operator+(std::list<int>::iterator x, int n); std::list<int>::iterator operator-(std::list<int>::iterator x, int n); /// Defining + operator to be able to erase elements at place std::list< Quark_line >::iterator operator+( std::list < Quark_line >::iterator x, int n); col_str::iterator operator-( col_str::iterator x, int n); /// Define the operator << for vector of int. std::ostream& operator<<(std::ostream& out, std::vector<int> vec); /// Define the operator << for cvec. std::ostream& operator<<(std::ostream& out, const cvec & cv); /// Define the operator << for dvec. std::ostream& operator<<(std::ostream& out, const dvec & cv); /// Define the operator << for cmatr. std::ostream& operator<<(std::ostream& out, const cmatr & cm); /// Define the operator << for dmatr. std::ostream& operator<<(std::ostream& out, const dmatr & matr); /// Define the operator << for std::pair<int, int>. std::ostream& operator<<(std::ostream& out, std::pair<int, int> pair); /////////////////////// DECLARING FUNCTIONS //////////////////// class Col_functions { -//private: -public: +private: +//public: /// The number of colors, used in numerical results. Change here for different value. double Nc; /// The trace convention Tr( t^a t^a )=TR. /// The normalization of the SU(Nc) generators, to be used in numerical evaluation. /// The value 1/2 corresponds to the Gell-Mann normalization. double TR; /// The value of cf=TR (Nc^2-1)/(Nc). /// Note that cf can be changed independently of Nc. double cf; /// For the leading terms one may want to keep the value of cf for Nc=3, or only keep the leading Nc term (Nc/2). // const bool leading_cf=true; //Keeping only the leading term for Nc=3 => cf->3/2 when taking leading //const bool leading_cf=false; // Replaces cf with the value TR (Nc^2-1)/(Nc) bool leading_cf; public: /// Default constructor Col_functions() : Nc(3.0), TR(0.5), cf(4.0/3.0), leading_cf(false) {} -//: Nc(3.0), TR(1.0), cf(8.0/3.0), leading_cf(false) {} // Set the number of colors -//void nc(double n) { -// Nc = n; -// cf = tr*(Nc*Nc-1.)/Nc; -//} +void N_c( double n) { + Nc = n; + cf = TR*(Nc*Nc-1.)/Nc; +} /// Get the number of colors -//double nc() const { return Nc; } +double N_c() const { return Nc; } /// Switch on leading CF -//void leadingCF(bool is = true) { leading_cf = is; } +void leading_CF(bool is = true) { leading_cf = is; } /// Return true, if leading CF is used -//bool is_leading_CF() const { return leading_cf; } +bool is_leading_cf() const { return leading_cf; } /// Set the normalization of the generators -//void trace(double t) { -// cf *= t/tr; -// tr = t; -//} +void T_R( double t) { + cf *= t/TR; + TR = t; +} -/// Return the normalization of the generators -//double trace() const { return tr; } +/// Return the normalization of the generators, +/// tr(t^a t^b)=TR \delta^{a,b}. +double T_R() const { return TR; } /****************** Functions for conjugation *************************/ /// Function returning the a conjugated copy of a Monomial. Monomial conjugate(const Monomial Mon) const; /// Function returning the a conjugated copy of a Polynomial. Polynomial conjugate(const Polynomial Poly) const; /// Function returning the conjugate of a Polynomial vector. poly_vec conjugate( const poly_vec Pv ) const; /// Function returning the conjugate of a Polynomial matrix. poly_matr conjugate( const poly_matr PM ) const; /// Function returning the a conjugated copy of a Quark_line. Quark_line conjugate(const Quark_line Ql) const; /// Function returning the a conjugated copy of a Col_str. Col_str conjugate(const Col_str Cs) const; /// Function returning the a conjugated copy of a Col_amp. Col_amp conjugate(const Col_amp Ca) const; /****************** Functions for gluon emission and exchange *************************/ - // Functions manipulating Quark_lines Col_strs Col_amps /// Function for emitting a gluon from a Col_str. Col_amp emit_gluon(const Col_str & in_Col_str, int emitter, int g_new) const; /// Function for emitting a gluon from a Col_amp Col_amp emit_gluon(const Col_amp & Ca_in, int emitter, int g_new) const; /// Function for splitting an open Quark_line into two Quark_lines, /// j is the locations of the gluon to be removed in the split. Col_str split_after( Quark_line & Ql, int j ) const; -/// Returns a Quark_line with the the elements before j in a Quark_line. +/// Returns a Quark_line with the the elements before place j in a Quark_line. +// TODO move to Quark_line? Quark_line before( Quark_line Ql, int j ) const; -/// Returns the elements after j in a Quark_line. +/// Returns the elements after place j in a Quark_line. +// TODO move to Quark_line? Quark_line after( Quark_line & Ql, int j ) const; -//Exchange between 6 and 6 -//My Ca is [{1,5,6,2}{3,4}] -//After exchange 1*1 Nc^(1)[{1,5,6,2}{3,4}] -// TODO The above example does not give the right TR factor Probably TR=1/2 /// Function for exchanging a gluon between the partons p1 and p2 in the Col_str Cs. Col_amp exchange_gluon( const Col_str & Cs, int p1, int p2) const; /// Function for exchanging a gluon between two partons p1 and p2 in the Col_amp Ca. -// TODO Check Col_amp exchange_gluon( const Col_amp & Ca, int p1, int p2) const; /// Function for splitting a closed Quark_line into two Quark_lines, /// j1 & j2 are the locations of the gluons to be removed in the split. /// May create 1-rings and 0-rings. +// TODO move to quark line? Col_str split_Quark_line( Quark_line & Ql, int j1, int j2 ) const; /****************** Functions for index contraction *************************/ /// Function for contracting quarks /// between two color structures Cs1 and Cs2. Col_str contract_quarks( Col_str Cs1, Col_str Cs2 ) const; /// Function for contracting quarks /// between two color amplitudes. Col_amp contract_quarks( Col_amp Ca1, Col_amp Ca2 ) const; /// Function to contract neighboring gluons in a Quark_line starting at j. Quark_line contract_neighboring_gluons( Quark_line & Ql, int j ) const; /// Function to contract neighboring gluons in a Quark_line starting at place 0, /// and looking everywhere. Quark_line contract_neighboring_gluons( Quark_line & Ql ) const; /// Function to contract next to neighboring gluons in a Quark_line, /// starting at place j (so gluon j and j+2). /// Also looks for new neighbors. Quark_line contract_next_neighboring_gluons( Quark_line & Ql, int j ) const; /// Function to contract next to neighboring gluons in a Quark_line. Quark_line contract_next_neighboring_gluons( Quark_line & Ql ) const; /// Contract up to next to neighboring gluons in each Quark_line in the Col_str; Col_str contract_next_neighboring_gluons( Col_str & Cs ) const; /// Contract up to next to neighboring gluons in each Quark_line in each Col_str in each Col_amp. Col_amp contract_next_neighboring_gluons(Col_amp & Ca) const; /// Function for contracting gluon indices in closed Quark_lines with only 2 gluons. /// This removes the 2-ring, replaces one of the gluon indices and /// multiplies with a factor tr[t^a t^a]=(1/2). Col_str contract_2_rings(Col_str & Cs) const; /// Contract closed Quark_lines with only 2 gluons in each Quark_line in each Col_str in the Col_amp Ca. /// This removes the 2-ring, replaces one of the gluon indices and /// multiplies with a factor tr[t^a t^a]=(1/2) Col_amp contract_2_rings(Col_amp & Ca) const; /// Function for contracting gluon indices within the same Quark_line. /// Checks only for ONE pair, i.e. if several gluon indices appear /// only one pair is contracted -Col_amp contract_Ql_gluons(Quark_line & Ql) const; +Col_amp contract_Quark_line_gluons(Quark_line & Ql) const; /// Function for contracting gluon indices within the same Quark_line. /// Checks only for ONE pair in each Quark_line, i.e. if several gluon indices appear, /// only one pair is contracted in each Quark_line. -Col_amp contract_Ql_gluons(Col_amp & Ca) const; +Col_amp contract_Quark_line_gluons(Col_amp & Ca) const; /// Function for contracting gluon indices within the same Quark_line in Col_str. /// Checks only for ONE pair in each Quark_line, i.e. if several gluon indices appear, /// only one pair is contracted in each Quark_line. -Col_amp contract_Ql_gluons( Col_str & Cs ) const; +Col_amp contract_Quark_line_gluons( Col_str & Cs ) const; /// Contracts one gluon, the first g in first Quark_line. /// Treat only the case when the same g is found in different Ql Col_amp contract_a_gluon(Col_str & Cs) const; /// Contracts one gluon, the first g in first Quark_line (in each Col_str). Col_amp contract_a_gluon(Col_amp & Ca ) const; /// Function for contracting all gluon indices in a Col_amp, /// Assumes quarks already contracted. Col_amp contract_all_gluons(Col_amp & Ca) const; /// Function for contracting all gluon indices in a Col_str, /// Assumes quarks already contracted. Col_amp contract_all_gluons(Col_str & Cs) const; /// Function for calculating scalar products between Col_amps. /// Does not add implicit state in the gluons only case. Polynomial scalar_product(const Col_amp & Ca1 , const Col_amp & Ca2 ) const; /// Function for calculating scalar products of Col_amps in different order /// for corss checking. Polynomial scalar_product2(const Col_amp & Ca1 , const Col_amp & Ca2 ) const; /// Function for calculating scalar product between two Col_strs. /// Does not add implicit state in the gluons only case. Polynomial scalar_product(const Col_str & Cs1, const Col_str & Cs2) const; /****************** Functions for leading terms *************************/ /// Function for finding the leading power of Nc in a Polynomial. int leading_Nc_pow( const Polynomial & Poly) const; /// Function for finding the leading power of Nc in a Polynomial. int leading_Nc_pow( const poly_vec & Pv) const; /// Function for finding the leading power of Nc in a Polynomial. int leading_Nc_pow( std::vector< std::tr1::shared_ptr<Polynomial> > Pvp) const; /// Takes the leading Nc terms of a Polynonmial. -/// Replaces cf with Nc/2, depending on if the parameter leading_cf is true or not. -/// (leading_cf is set in parameters.h). +/// Replaces cf with Nc*TR, depending on if the parameter leading_cf is true or not. Polynomial leading( const Polynomial & Poly, bool lcf = false ) const; /// Take the leading part of a poly_vec. /// Keeps only Monomials with maximal power of cf + Nc. /// Uses leading( const Polynomial & Poly). poly_vec leading(const poly_vec & Pv) const; /// Takes the leading part of a matrix of Polynomials, /// keeping only those with maximal power of cf + Nc. poly_matr leading(const poly_matr & Pm) const; /// Take the leading part of a poly_vec, given a vector of pointers to the Polynomials. /// Keeps only Monomials with maximal power of cf + Nc. poly_vec leading(std::vector<std::tr1::shared_ptr<Polynomial> > Pvp) const; /// Take the leading part of a poly_matr, given a vector of vector of pointers to the Polynomials. /// Loops over Monomials in all Polynomials /// and keeps only those with maximal power of cf + Nc. dmatr leading( std::vector< std::vector< std::tr1::shared_ptr<Polynomial> > > Pm) const; /// To keep only leading terms in a map. std::map< std::string, Polynomial > leading(std::map< std::string, Polynomial > mem_map) const; +/****************** Functions for removing cf, normal ordering and simplification *************************/ + + /// Returns the Polynomial version of Poly where cf has been removed. /// In this function Poly itself is not changed, but a copy is made and changed. Polynomial remove_cf( const Polynomial & Poly ) const; /// Remove cf in a poly_vec, returns a copy where cf has been removed. poly_vec remove_cf( const poly_vec & Pv ) const; /// Remove cf in a poly_matr, returns a copy where cf has been removed. poly_matr remove_cf( const poly_matr & Pm ) const; /// Returns the Polynomial version of Poly where the Monomials /// have been normal ordered. /// In this function Poly itself is not changed, but a copy is made and changed. /// (Uses the normal_order member function in Polynomial). Polynomial normal_order( const Polynomial & Poly ) const; /// Normal order all polynomials in a poly_vec. /// (Uses the normal_order member function in Polynomial). poly_vec normal_order( const poly_vec & Pv) const; /// Normal_order all Polynomials in a poly_matr. /// (Uses the normal_order member function in Polynomial). poly_matr normal_order( const poly_matr & Pm) const; /// Returns the simplified Polynomial. /// In this function Poly itself is not changed, but a copy is made and changed. /// (Uses the simplify member function in Polynomial). -Polynomial simplify( const Polynomial & Poly ) const; +//Polynomial simplify( const Polynomial & Poly ) const; /// Returns the simplified poly_vec. /// In this function poly_vec itself is not changed, but a copy is made and changed. /// (Uses the simplify member function in Polynomial). poly_vec simplify( const poly_vec & Pv) const; /// Normal_order all Polynomials in a poly_matr. /// In this function poly_matr itself is not changed, but a copy is made and changed. /// (Uses the simplify member function in Polynomial). poly_matr simplify( const poly_matr & Pm) const; + /********************* Functions for numerical evaluation *************************/ +// These functions should be kept in Col_functions class as they need numerical +// values for evaluation. Letting each Poly carry around its own Nc etc. +// would not be nice + /// To take the numerical value of a map. -std::map< std::string, double > d_num(std::map< std::string, std::tr1::shared_ptr<Polynomial> > mem_map) const; - +std::map< std::string, double > double_num(std::map< std::string, std::tr1::shared_ptr<Polynomial> > mem_map) const; /// Numerically evaluates a Monomial for Nc=3, putting cf to Nc=3 value. -cnum c_num( const Monomial & Mon ) const; +cnum cnum_num( const Monomial & Mon ) const; // Numerically evaluates a Monomial to a double. -double d_num( const Monomial & Mon ) const; +double double_num( const Monomial & Mon ) const; /// Numerically evaluates a Polynomial, using the cf and Nc variables. /// NOTE: this affects both leading and subleading terms, as leading terms are defined to be those with maximal /// power of cf+Nc, see also leading(Polynomial Poly). -cnum c_num( const Polynomial & Poly ) const; +cnum cnum_num( const Polynomial & Poly ) const; /// Numerically evaluates a Polynomial for Nc=3, putting cf to Nc=3 value. /// NOTE: this affects both leading and subleading terms, as leading terms are defined /// to be those with maximal power of cf+Nc, see also leading(Polynomial Poly). -double d_num( const Polynomial & Poly) const; +double double_num( const Polynomial & Poly) const; /// To take the numerical value of a map. -std::map< std::string, double > d_num(std::map< std::string, Polynomial > mem_map) const; +std::map< std::string, double > double_num(std::map< std::string, Polynomial > mem_map) const; /// Keeping only leading terms, numerically evaluates a polynomial /// putting cf to 3/2 should have no effect IF cf removed using leading(Polynomial). /// If this happens depends on leading_cf set in parameters.h. -cnum leading_c_num(const Polynomial& Poly ) const; +cnum leading_cnum_num(const Polynomial& Poly ) const; /// Numerically evaluates a poly_vec (vector of Polynomial) for Nc=3. -dvec d_num( const poly_vec & Pv ) const; +dvec double_num( const poly_vec & Pv ) const; /// Returns a double value. The argument is a vector of pointers to Polynomials. -dvec d_num( const std::vector<std::tr1::shared_ptr<Polynomial> > & Pv ) const; +dvec double_num( const std::vector<std::tr1::shared_ptr<Polynomial> > & Pv ) const; /// Returns a double value. The argument is a vector of vector of pointers to Polynomials. -dmatr d_num( const std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > > & Pm ) const; +dmatr double_num( const std::vector<std::vector<std::tr1::shared_ptr<Polynomial> > > & Pm ) const; /// Numerically evaluates a Polynomial for Nc=3, -/// and stores in the format of a Polynomial with only one term with only a numerical part -Polynomial polynomial_c_num( const Polynomial & Poly ) const; +/// and stores in the format of a Polynomial with only one term with only a numerical part. +Polynomial Polynomial_cnum_num( const Polynomial & Poly ) const; /// Numerically evaluates a poly_vec (vector of Polynomial), -/// using c_num (Polynomial). -cvec c_num(const poly_vec & Pv) const; +/// using cnum_num (Polynomial). +cvec cnum_num(const poly_vec & Pv) const; /// Numerically evaluates a poly_vec (vector of Polynomial) -/// and stores in the form of a Polynomial, uses polynomial_c_num( Pv.at( p ) ). +/// and stores in the form of a Polynomial, uses polynomial_cnum_num( Pv.at( p ) ). /// for each Polynomial. -poly_vec poly_vec_c_num( const poly_vec & Pv ) const; +poly_vec poly_vec_cnum_num( const poly_vec & Pv ) const; ///Numerically evaluates a poly_matr (vector of poly_vec) /// and stores in the form of a poly_matr -poly_matr poly_matr_c_num( const poly_matr & Pm ) const; +poly_matr poly_matr_cnum_num( const poly_matr & Pm ) const; /// Numerically evaluates a poly_matr (vector of poly_vec), -/// using c_num(poly_vec) for each poly_vec. -cmatr c_num(const poly_matr & Pm) const; +/// using cnum_num(poly_vec) for each poly_vec. +cmatr cnum_num(const poly_matr & Pm) const; /// Numerically evaluates a poly_matr (vector of poly_vec). -dmatr d_num(const poly_matr & Pm) const; +dmatr double_num(const poly_matr & Pm) const; /********************* Other functions *************************/ /// The factorial of an int. 0! is defined as 1. int factorial (int i) const; /********************* Functions which should be moved to some kind of basis *************************/ /// Function for calculating the weight < M | T^(i) T^(j) | M > / (< M | M > (c_f or c_a) ), /// used in the Sudakov for generating the next k_T. /// The Ca should be | M >, and i and j are the partons involved in the emission. /// The factor (c_f or c_a) is present to compensate for factor built in in kinematics part cnum color_shower_weight(Col_amp Ca, int i, int j, int g_new) const; /// Function for calculating the weight < M | T^(i) T^(j) | M > / (< M | M > (c_f or c_a) ), /// used in the Sudakov, for generating the next k_T. /// The amp should be | M >, and i and j are the partons involved in the emission. /// The index g_new denotes the parton to be emitted and spm_matr the scalar product matrix used /// for numerical evaluation (before emission). /// The factor (c_f or c_a) is present to compensate for factor built in in kinematics part. /// In the momentum code i is the emitter and j is the spectator. /// The matrix radamps contains the amplitudes associated with emission, /// and can be calculated using radiation_amplitue_matrix. /// The number of existing quarks is n_q and the number of gluons is n_g. /// TODO make work more general, so far only intended for one (open or closed) quark-line. cnum color_shower_weight(const cvec & amp, int i, int j, int g_new, const dmatr & radamps, const dmatr & spm_matr, int n_q, int n_g, int n_loop) const; /// Function for calculating the radiation amplitudes associated with emissions from /// the various partons, starting with n_q quarks and n_g gluons. /// The content is C^m T^(i) T^(j) C^n as a matrix of matrices. /// The factors associated with i and j stand in the same sub-matrix. /// For example, the first n_basis*n_basis block corresponds to the coherent /// emission from parton 1 and parton 1. The block to the right of this /// corresponds to coherent emission of parton 1 and parton 2 etc. /// Blocks further down in the matrix corresponds to first parton > 1. /// TODO make work more general, so far only intended for one (open or closed) quark line. dmatr radiation_amplitue_matrix( uint n_q, uint n_g, int n_loop ) const; /********************* Functions which have been moved to some kind of basis *************************/ // TODO Remove below functions /// Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix. /// Assumes that there are only diagonal contributions. This is useful for calculations in leading Nc limit. /// Warning: ordering of basis vectors must be the same. /// moved to trace type basis cnum scalar_product_num_diagonal(const cvec & v1, const cvec & v2, const dmatr & spm_matr) const; /// Calculates the scalar product between numerical (complex) amplitudes v1, V2 given the scalar product matrix. /// Warning: ordering of basis vectors must be the same. /// moved to trace_type_basis, only used in just_radiate_tree_level_2 cnum scalar_product_num_fast(const cvec & v1, const cvec & v2, const dmatr & spm_matr) const; /// Function for calculating scalar products. /// This function assumes that the Col_amps are normal ordered and that the full polynomial is /// contained in the numerical factor. /// It also assumes that the BASIS in which sp_matr is calculated is the SAME as that of the Col_amp /// and that the Col_amp is a linear combination of ALL states. /// The coefficients of the basis vectors can then simply be read off in the Polynomials multiplying the Col_strs /// This is an attempt to speed up. /// moved to Trace_type_basis cnum scalar_product_num_fast(const Col_amp & Ca1, const Col_amp & Ca2, const dmatr & sp_matr) const; /// Function for calculating scalar products /// given the information about the basis and the scalar product matrix in numerical form in the same basis. /// Does add implicit state in gluon only case, as these terms are contained in the matrix of scalar products. /// moved to Trace_type_basis, renamed to scalar_product_num -cnum scalar_product_c_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const dmatr & sp_matr) const; +cnum scalar_product_cnum_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const dmatr & sp_matr) const; /// Function for calculating scalar products /// given the information about the basis and the scalar product matrix (in Polynomial form) in the basis. /// moved to Trace_type_basis (uses decompose) Polynomial scalar_product(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const; /// A function for calculating the scalar products between the basis vectors /// (stored in a Col_amp), memorizing the result of a topology using a map. /// moved to Col_basis dmatr scalar_product_matrix_mem(const Col_amp & Basis) const; /// Function for decomposing an amplitude Ca in a Basis Basis. /// The basis should be the "trace basis", basis vectors which are /// linear combinations are not possible. /// Does not use a scalar product, but the special properties of the trace basis. poly_vec decompose(const Col_amp & Ca, const Col_amp & Basis) const; /// Function for reading in a numerical matrix given the file name. dmatr read_in_matrix( const char* filename ) const; /// Function for creating the name of the file containing the scalar product matrix /// The argument leading should be true if it is a leading matrix that is to be saved. std::string spm_file_name( const Col_amp & Basis, bool leading) const; /// Function for creating a basis with n_q=n_qbar quarks and n_g gluons. /// Note: If there are only gluons the anti-cyclic part of the Col_amp is implicit. /// For example, for (1,2,3) there is also an implicit part -(1,3,2) which is /// not separately constructed. This is compensated for in scalar_product_matrix and scalar_product_matrix_mem, /// but not in scalar_product(). /// If the global parameter n_loop=0 only lowest order basis states are constructed /// (n_loop set in parameters.h). /// to be removed Col_amp create_trace_basis(int n_q, int n_g, int n_loop) const; /// Connect n_q q and qbars in all n_q! ways, returns a basis (in the form of a Col_amp) /// for the case of n_q q qbar pairs. /// remove, moved to Trace_basis Col_amp connect_quarks( int n_q ) const; /// Compute the new basis states coming from one old vector when one gluon, g_new, is added. /// moved to Trace_basis and Tree_level_gluon basis Col_amp add_one_gluon(const Col_str & Cs, int g_new, int n_loop) const; /// Compute the basis if one gluon is added to the old basis Old_bas. /// If n_loop==0, only tree level states are constructed. /// moved to Trace_basis and Tree_level_gluon basis Col_amp add_one_gluon(const Col_amp & Old_basis, int n_q, int n_g, int g_new, int n_loop) const; /// A function to rename the indices in two Col_strs, such that in the first /// they are called 1,2,3..., and in the second the relative order is kept. /// Used for calculating scalar products using memory. /// Moved to Col_basis, consider removing std::pair<Col_str,Col_str> rename_indices(const Col_str & Cs1, const Col_str & Cs2) const; /// A function to rename the indices in two Col_amps, such that in the first /// they are called 1,2,3..., and in the second the relative order is kept. /// Used for calculating scalar products using memory. /// moved to Col_basis only used in radiation amplitude matrix, consider removing std::pair<Col_amp,Col_amp> rename_indices(Col_amp Ca1, Col_amp Ca2) const; /// Function for writing out a numerical scalar product matrix to the file filename. /// moved to Col_basis, remove void write_out_spm(const dmatr & matr, const char* filename) const; /// Function for writing out a numerical scalar product matrix to the default filename and file path. /// The filename is constructed using info of the basis, how many quarks and gluons. /// (The number of anti-quarks is implicitly the same as the number of quarks.) /// moved to Col_basis, remove void write_out_spm( const dmatr & matr, const Col_amp & Basis, bool leading ) const; /// Function for writing out a polynomial scalar product matrix to the default filename and file path. /// The filename is constructed using info of the basis, how many quarks and gluons. /// (The number of anti-quarks is implicitly the same as the number of quarks.) /// moved to Col_basis, remove void write_out_spm( const poly_matr & matr, const Col_amp & Basis, bool leading ) const; /// A function for calculating the scalar products between the basis vectors /// (stored in a Col_amp). /// If there are gluons involved the complex conjugate has to be /// added or subtracted to consider the physical states as /// the conjugate, "anti-cyclic", amplitude is implicit /// The sign is given by (-1)^Ng for Ng gluons, see arXiv:0906.1121 /// remove, moved to Col_basis poly_matr scalar_product_matrix(const Col_amp & Basis) const; /// Function to test if a numerical (scalar product matrix) is symmetric using a accuracy. /// remove, moved to Col_basis bool check_symmetry(const dmatr & matr) const; /// Function to test if a numerical (scalar product matrix) is diagonal using an accuracy. /// remove, moved to Col_basis bool check_diagonal(const dmatr & matr) const; /// Function for creating the name of the file containing the radiation amplitude matrix. /// The argument leading should be true if it is a leading Nc matrix that is to be saved. /// consider moving to Col_basis std::string ram_file_name( const Col_amp & Basis, bool leading) const; /// Function for locating the parton parton in the normal ordered basis, -/// given the number of the vector vec_num, and the number of quarks and gluons in the basis. -std::pair<int,int> find_parton(int parton, int vec_num, int n_q, int n_g, int n_loop) const; +/// given the number of the vector vecnum_num, and the number of quarks and gluons in the basis. +std::pair<int,int> find_parton(int parton, int vecnum_num, int n_q, int n_g, int n_loop) const; /// Function for finding the new vector number in the basis with n_p+1 partons /// after inserting a new parton with larger parton number at the place place. -/// The old vector has number old_num, and there were, before emission +/// The old vector has number oldouble_num, and there were, before emission /// n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. /// Function so far only intended for tree-level processes with at most 2 qqbar pairs. /// Function explicitly tested for initial states with 2qqbar pairs and up to 5 gluons, /// 1qqbar pair and up to 7 gluons, 0 qqbar pairs and up to 8 gluons. -int new_vector_number( int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop ) const; +int new_vector_number( int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop ) const; /// This function is only for tree-level processes with at most 2 qqbar pairs. /// Function for finding the new vector numbers in the basis for n_p+1 partons /// after radiating a new parton from the parton emitter. -/// The old vector has number old_num, and there were, before emission +/// The old vector has number oldouble_num, and there were, before emission /// n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. /// For emission from q or qbar there is only one resulting color structure, /// and -1 is returned in the place of the absent color structure. /// The second vector, where the new parton is inserted before the emitter /// comes with a minus sign in the new total amplitude. -std::pair<int, int> new_vector_numbers(int old_num, int emitter, int n_q, int n_g, int n_loop) const; +std::pair<int, int> new_vector_numbers(int oldouble_num, int emitter, int n_q, int n_g, int n_loop) const; /// Function for finding the new vector numbers in the basis New_basis /// after radiating a new gluon from the parton emitter. /// The old color structure is Cs, and after emission a linear combination /// of new basis vectors is obtained. /// For emission from q or qbar there is only one resulting color structure, /// and -1 is returned in the place of the absent color structure. /// The second vector, where the new parton is inserted before the emitter /// comes with a minus sign in the new total amplitude. std::pair<int, int> new_vector_numbers(const Col_str & Cs, int emitter, const Col_amp & New_basis) const; /********************* Functions which probably should be completely removed *********************/ /// TODO Remove these functions /// Exchange gluons between all possible parton pairs /// remove, not in use Col_amp exchange_gluon(Col_amp & Ca, int n_p) const; /// Function for calculating scalar products numerically /// given the information about the basis and the scalar product matrix in the basis. /// Does add implicit state in gluon only case, as these terms should be contained in the matrix of scalar products. /// Returns a (complex) numerical value in the numerical part of a Polynomial. /// remove? not used and not numerical! -Polynomial scalar_product_c_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const; +Polynomial scalar_product_cnum_num(const Col_amp & Ca1, const Col_amp & Ca2, const Col_amp & Basis, const poly_matr & sp_matr) const; /// Function for calculating scalar products, assuming that only diagonal parts contribute. /// This is useful for calculating the leading Nc results. /// This function assumes that the Col_amps are normal ordered and that the full Polynomial is /// contained in the numerical factor. /// It also assumes that the BASIS in which sp_matr is calculated is the SAME as that of the Col_amp /// and that the Col_amp is a linear combination of ALL states. /// The coefficients of the basis vectors can then simply be read off in the Polynomials multiplying the Col_strs. /// This is an attempt to speed up. /// Remove?, never used cnum scalar_product_num_diagonal(const Col_amp & Ca1, const Col_amp & Ca2, const dmatr & sp_matr) const; /// Functions for generating the n_p random amplitudes in [-1/2,1/2] /// for emission from n_p partons. /// remove, just used by just_radiate_tree_level2 cvec generate_random_amplitudes(int n_p) const; /// A function for radiating n_g gluons n_run times, using that only tree level graphs contribute /// starting with 2*n_q+ n_g partons ///more explanation. Explain how this can work. /// The starting state is Ca1, /// in which the parton has all numbers between 1 and the number of partons. /// to be removed double just_radiate_tree_level2(const Col_amp & Ca1, int n_g_rad, double n_run) const; /// A function for radiating n_g gluons n_run times /// starting with the initial state Ca1, /// in which the parton has all numbers between 1 and the number of partons. /// to be removed double just_radiate(Col_amp & Ca1, int n_g_rad, double n_run, int n_loop) const; /// A function for radiating n_g gluons n_run times, using that only tree level graphs contribute /// starting with 2*n_q+ n_g partons. /// The starting state is Ca1, /// in which the parton has all numbers between 1 and the number of partons. /// to be removed double just_radiate_tree_level(Col_amp & Ca1, int n_g_rad, double n_run) const; /// Function simulating virtual gluon exchane, evolving the color structure. /// Evolution performed n_steps time with size alpha. /// n_p is the number of partons. /// Warning, not yet much tested. /// Consider removing Col_amp step_evolv(Col_amp & Ca, int n_p, int n_steps, double alpha) const; /// Function for finding out the number of the vector with only one quark_line /// with the color information of ql. /// The quark_line should be normal ordered before usage. /// remove? /// moved to some kind of basis int quark_line_number( int n_q, int n_g, const quark_line & ql) const; /// A function for calculating the scalar products between the basis vectors /// (stored in a Col_amp), memorizing the result of a topology using a map. /// remove, less general and not much faster than scalar_product_matrix_mem(const Col_amp & Basis) dmatr scalar_product_matrix_mem2(const Col_amp & Basis, const int n_loop) const; }; } #endif /* COLORFULL_Col_functions_h */ diff --git a/Core/Orthogonal_basis.cc b/Core/Orthogonal_basis.cc --- a/Core/Orthogonal_basis.cc +++ b/Core/Orthogonal_basis.cc @@ -1,518 +1,518 @@ /* * Orthogonal_basis.cc * * Created on: May 25, 2013 * Author: Malin Sjodahl */ #include "Orthogonal_basis.h" namespace ColorFull { void Orthogonal_basis::scalar_product_matrix(){ if(n_g+n_q>5){ std::cout << "Orthogonal_basis::scalar_product_matrix: n_q+n_g0=" << n_q+n_g << " is large, consider using numerical and/or memory version. " << std::endl; std::cout.flush(); } return scalar_product_matrix( true, true, false ); } void Orthogonal_basis::diagonal_scalar_product_matrix( bool save_P_diagonal_spm, bool save_d_diagonal_spm, bool use_mem ){ //std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: entering" << std::endl; // If the diagonal_P_spm and diagonal_d_spm have already been calculated, erase them diagonal_P_spm.clear(); diagonal_d_spm.clear(); if( (cb.size()==0 ) ) { std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: There are no basis vectors in this basis, consider using read_in_basis." << std::endl; std::cout.flush(); return ; } // Check that all Polynomials are real // (check doesn't cover the case of complex Polynomial in a Quark_line) // but if an entry is not real this will be discovered as the d_spm is calculated for ( uint cbi=0; cbi < cb.size(); cbi++){ for ( uint j=0; j< cb.at(cbi).size(); j++){ - if( imag (CF->c_num( cb.at(cbi).at(j).Poly )) > accuracy ){ + if( imag (CF->cnum_num( cb.at(cbi).at(j).Poly )) > accuracy ){ std::cerr << "Orthogonal_basis::diagonal_scalar_product_matrix: ColorFull expects real Polynomial multiplying the color structure, but the Polynomial\n" << cb.at(cbi).at(j).Poly << std::endl << " appearing in front of the Col_str " << cb.at(cbi).at(j).cs << " in basis vector number " << cbi << " is not real."<< std::endl << std::endl; std::cerr.flush(); throw cb.at(cbi).at(j).Poly; } } } // For remembering already calculated topologies std::map<std::string, std::tr1::shared_ptr<Polynomial> > mem_map; // Loop over basis vectors in Basis for( uint i=0; i < cb.size(); i++){ // To contain the result of vector i square Polynomial iiRes; iiRes=iiRes*0; if(use_mem){ //std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: Using memoization." << std::endl; // Loop over Col_strs in first Ca for( uint Ca1i=0; Ca1i< cb.at(i).size(); Ca1i++){ // Loop over Col_strs in second Ca for( uint Ca2i=0; Ca2i< cb.at(i).size(); Ca2i++){ // To contain the contribution to the ii-th entry if memoization is used std::tr1::shared_ptr<Polynomial> iiEntry_contr; // Rename indices, and make string of new col_strs, to use in map // strip off Polynomial information to make the map minimal Col_str Cs1_cs, Cs2_cs; Cs1_cs.append(cb.at(i).at(Ca1i).cs); Cs2_cs.append(cb.at(i).at(Ca2i).cs); std::pair<Col_str, Col_str> Css_new = rename_indices( Cs1_cs, Cs2_cs ); //std::pair<Col_str, Col_str> Css_new = rename_indices( cb.at(i).at(Ca1i),cb.at(j).at(Ca2i) ); Col_str Cs1 = Css_new.first; Col_str Cs2 = Css_new.second; std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Calculate element contribution to the ijth element in scalar product matrix // If this has scalar product has occurred before, reuse old value if (mem_map.count( Cs_string.str() ) > 0) { iiEntry_contr = mem_map[Cs_string.str()]; //std::cout << "Reusing old res " << Cs_string.str()<< std::endl; } // Otherwise, calculate value and save topology else { Polynomial p = CF->scalar_product(Cs1, Cs2); iiEntry_contr = shared_ptr<Polynomial> (new Polynomial(p)); mem_map[Cs_string.str()] = iiEntry_contr; } // Sum up all the contributions to one Polynomial, recall to multiply with Polynomials Polynomial iiEntry_contr_poly=(*iiEntry_contr)* cb.at(i).at(Ca1i).Poly*cb.at(i).at(Ca2i).Poly; if( !save_P_diagonal_spm ) { - double iiEntry_contr_d= CF-> d_num( iiEntry_contr_poly ); + double iiEntry_contr_d= CF-> double_num( iiEntry_contr_poly ); Monomial Mon( iiEntry_contr_d ); //ijRes=ijRes + Mon; iiRes+= Mon; } else iiRes += iiEntry_contr_poly; } // end looping over Cs in first Ca } // end looping over Cs in 2nd Ca // If Polynomial result is wanted, simplify if ( save_P_diagonal_spm ) iiRes.simplify(); // Otherwise convert to numerical else { - double num_iiRes= CF-> d_num( iiRes ); + double num_iiRes= CF-> double_num( iiRes ); iiRes.clear(); Monomial Mon( num_iiRes ); iiRes.push_back(Mon); } diagonal_P_spm.push_back( iiRes ); } // end if ( use_mem ) else{ //std::cout << "Col_basis::scalar_product_matrix: Now looping over i " << i << " j " << j <<std::endl; // Calculate element ij in scalar product matrix Polynomial iiRes=CF->scalar_product(cb.at(i), cb.at(i)); iiRes.simplify(); diagonal_P_spm.push_back( iiRes ); } //end if (not mem) }// end looping over i if ( save_d_diagonal_spm ){ - diagonal_d_spm=CF->d_num(diagonal_P_spm); + diagonal_d_spm=CF->double_num(diagonal_P_spm); } //std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: calculated" << diagonal_d_spm <<std::endl; if (! save_P_diagonal_spm){ diagonal_P_spm.clear(); } //std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: P_spm\n" << diagonal_d_spm << std::endl // << diagonal_d_spm; return; } // Calculates the scalar product between numerical (complex) amplitudes v1, v2 given the scalar product matrix // Warning: ordering of basis tensors must be the same void Orthogonal_basis::scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ) { //std::cout << "Orthogonal_basis::scalar_product_matrix: entering knowing diagonal_d_spm" << diagonal_d_spm << std::endl; // If the P_spm and d_spm have already been calculated, erase them P_spm.clear(); d_spm.clear(); // First calculate diagonal version diagonal_scalar_product_matrix( save_P_spm, save_d_spm, use_mem ); //std::cout << "Orthogonal_basis::scalar_product_matrix: diagonal_d_spm" << diagonal_d_spm << std::endl; // Then copy content to matrix if( save_P_spm ){ // First empty P_spm P_spm.clear(); Polynomial Zero; Zero=0*Zero; for (uint i=0; i< diagonal_P_spm.size(); i++ ){ poly_vec rowi; for (uint j=0; j< diagonal_P_spm.size(); j++ ){ if(i==j){// diagonal entries rowi.push_back( diagonal_P_spm.at(i)); } else{// non-diagonal parts rowi.push_back(Zero); } } P_spm.push_back(rowi); } // write out P_spm write_out_spm( P_spm, false ); } if( save_d_spm ){ // First empty P_spm d_spm.clear(); for (uint i=0; i< diagonal_d_spm.size(); i++ ){ dvec rowi; for (uint j=0; j< diagonal_d_spm.size(); j++ ){ if(i==j){// diagonal entries rowi.push_back( diagonal_d_spm.at(i)); } else{// non-diagonal parts rowi.push_back( 0 ); } } d_spm.push_back(rowi); } // write out d_spm write_out_spm( d_spm, false ); } } //poly_vec Orthogonal_basis::decompose( const Col_amp & Ca ) { poly_vec Orthogonal_basis::decompose( Col_amp & Ca ) { //cout << "decompose: Incoming Basis and Col_amp " << Basis << Ca << endl; //Ca.simplify(); // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::decompose: The basis vector cb is empty consider reading in basis." << std::endl; throw cb; } // Check that quark and gluon content agree with that in Col_amp else if(Ca.size()>0 ){ if(Ca.at(0).n_quark() != n_q) { std::cerr << "Orthogonal_basis::decompose: The number of quarks in the argument Col_amp, " << Ca.at(0).n_quark() << ", does not fit the number of quarks in the basis " << n_q << std::endl;} if(Ca.at(0).n_gluon() != n_g ) { std::cerr << "Orthogonal_basis::decompose: The number of gluons in the argument Col_amp " << Ca.at(0).n_gluon() << " does not fit the number of gluons in the basis " << n_q << std::endl; } } // This version of decompose (as opposed to trace type versions) // need scalar product matrix // If P_spm is not empty, but has wrong size if( P_spm.size() != cb.size() and !P_spm.empty() ){ std::cerr << "Orthogonal_basis::decompose: The size of the scalar product matrix and the basis do not agree." << std::endl; throw P_spm; } // If diagonal_P_spm is not empty, but has wrong size if( diagonal_P_spm.size() != cb.size() and !diagonal_P_spm.empty() ){ std::cerr << "Orthogonal_basis::decompose: The size of the diagonal scalar product matrix and the basis do not agree." << std::endl; throw P_spm; } // If both are empty, calculate diagonal version if( P_spm.empty() and diagonal_P_spm.empty() ){ diagonal_scalar_product_matrix(true, true, true); } // Use matrix information if diagonal spm is empty if( diagonal_P_spm.empty() ){ for ( uint i=0; i< P_spm.size(); i++ ){ diagonal_P_spm.push_back( P_spm.at(i).at(i) ); } - diagonal_d_spm=CF->d_num(diagonal_P_spm); + diagonal_d_spm=CF->double_num(diagonal_P_spm); } //std::cout << "Orthogonal_basis::decompose: diagonal_P_spm" << diagonal_P_spm << std::endl; // To contain the decomposed vector poly_vec Decv; //std::cout << "Orthogonal_basis::decompose: constructed 0 Decv " << Decv << std::endl; // Loop over all vectors in cb and calculate projection for (uint i = 0; i < cb.size(); i++) { double inv_norm=1.0/diagonal_d_spm.at(i); //Decv.at(i)= CF->scalar_product( cb.at(i) , Ca )*inv_norm; Decv.push_back( CF->scalar_product( cb.at(i) , Ca )*inv_norm ); } return Decv; } std::string Orthogonal_basis::diagonal_spm_file_name(const bool leading, const bool poly ) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // CF as in ColorFull ss << "CF_"; // Prefix according to basis type ss << "OB_"; // diagonal version ss << "diagonal_"; // Polynomial or numerical matrix? if( poly )ss << "P_"; else ss << "d_"; ss << "spm_q"; ss << n_q; ss << "_g"; ss << n_g; if ( leading ) ss << "_l"; - if ( CF->leading_cf ) ss << "_cfl"; - if(CF->Nc != 3 ){ + if ( CF->is_leading_cf() ) ss << "_cfl"; + if(CF->N_c() != 3 ){ ss << "_Nc_"; - ss << CF->Nc; + ss << CF->N_c(); } - if(CF->TR != 0.5 ){ + if(CF->T_R() != 0.5 ){ ss << "_TR_"; - ss << CF->TR; + ss << CF->T_R(); } filename=ss.str(); return filename; } // Function for calculating scalar products // given the information about the basis and the scalar product matrix in the basis Polynomial Orthogonal_basis::scalar_product( Col_amp & Ca1, Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::scalar_product: The basis vector cb is empty consider using create_basis or read_in_basis." << std::endl; throw cb; } // Check that size of P_spm is consistent (if calculated) if( (P_spm.size() != cb.size()) and P_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product: Size of scalar product matrix P_spm and color basis cb do not agree." << std::endl; throw P_spm; } // Check that size of diagonal_P_spm is consistent (if calculated) if( (diagonal_P_spm.size() != cb.size()) and diagonal_P_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product: Size of diagonal_P_spm and color basis cb do not agree." << std::endl; throw P_spm; } // Check if at least one of P_spm and diagonal_P_spm exist, // If non exist, calculate diagonal if( P_spm.empty() and diagonal_P_spm.empty() ){ diagonal_scalar_product_matrix(true, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_P_spm.empty() ){ for( uint i=0; i< P_spm.size(); i++ ){ diagonal_P_spm.push_back(P_spm.at(i).at(i)); } } // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps poly_vec Polyv1=CF->conjugate( decompose(Ca1) ); poly_vec Polyv2=decompose( Ca2 ); // Then add contributions for (uint m1=0; m1< cb.size(); m1++){ // Diagonal terms Poly_res=Poly_res+Polyv1.at(m1) *Polyv2.at(m1) *diagonal_P_spm.at(m1); } return Poly_res; } // Function for calculating scalar products // given the information about the basis and the numerical scalar products cnum Orthogonal_basis::scalar_product_num( Col_amp & Ca1, Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::scalar_product_num: The basis vector cb is empty consider using create_basis or read_in_basis." << std::endl; throw cb; } // Check that size of d_spm is consistent (if calculated) if( (d_spm.size() != cb.size()) and d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of scalar product matrix d_spm and color basis cb do not agree." << std::endl; throw d_spm; } // Check that size of diagonal_d_spm is consistent (if calculated) if( (diagonal_d_spm.size() != cb.size()) and diagonal_d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of diagonal_d_spm and color basis cb do not agree." << std::endl; throw diagonal_d_spm; } // Check if at least one of d_spm and diagonal_d_spm exist, // If non exist, calculate diagonal version if( d_spm.empty() and diagonal_d_spm.empty() ){ diagonal_scalar_product_matrix(false, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_d_spm.empty() ){ for( uint i=0; i< d_spm.size(); i++ ){ diagonal_d_spm.push_back(d_spm.at(i).at(i)); } } // To contain the result double res=0; // Decompose the Col_amps poly_vec Polyv1=CF->conjugate( decompose(Ca1) ); poly_vec Polyv2=decompose( Ca2 ); - dvec v1=CF->d_num (Polyv1 ); - dvec v2=CF->d_num (Polyv2 ); + dvec v1=CF->double_num (Polyv1 ); + dvec v2=CF->double_num (Polyv2 ); // Then add contributions for (uint m1=0; m1< cb.size(); m1++){ // Diagonal terms res=res+v1.at(m1) *v2.at(m1) *diagonal_d_spm.at(m1); } return res; } // Calculates the scalar product between decomposed amplitudes v1, V2 // using the diagonal_d_spm diagonal numerical scalar product matrix. // The vectors thus needs to be expressed in the basis contained in cb. cnum Orthogonal_basis::scalar_product_num_fast( const cvec & v1, const cvec & v2) { if(v1.size()!= v2.size()){ std::cerr << "Orthogonal_basis::scalar_product_num_fast: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; throw v2; } // Check that size of d_spm is consistent (if calculated) if( ( d_spm.size() != cb.size()) and d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num_fast: Size of scalar product matrix d_spm and color basis cb do not agree." << std::endl; throw d_spm; } // Check that size of diagonal_d_spm is consistent (if calculated) if( ( diagonal_d_spm.size() != cb.size()) and diagonal_d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num_fast: Size of diagonal_d_spm and color basis cb do not agree." << std::endl; throw diagonal_d_spm; } // Check if at least one of d_spm and diagonal_d_spm exist, // If non exist, calculate diagonal version if( d_spm.empty() and diagonal_d_spm.empty() ){ diagonal_scalar_product_matrix(false, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_d_spm.empty() ){ for( uint i=0; i< d_spm.size(); i++ ){ diagonal_d_spm.push_back(d_spm.at(i).at(i)); } } uint basis_size=v1.size(); // To contain the result cnum res=0; // Add contributions, diagonal parts only for (uint m1=0; m1< basis_size; m1++){ res += conj( v1.at(m1) ) *v2.at(m1) *diagonal_d_spm.at(m1); } return res; } void Orthogonal_basis::write_out_diagonal_spm( const dvec & dv, const bool leading ) const{ // Creating file name (part) std::string filename = diagonal_spm_file_name( leading, false ); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << dv; } void Orthogonal_basis::write_out_diagonal_spm( const poly_vec & pv, const bool leading ) const{ // Creating file name (part) std::string filename = diagonal_spm_file_name( leading, true ); // Then, write to the file std::ofstream outfile(filename.c_str()); outfile << pv; } /* void Orthogonal_basis::write_out_diagonal_d_spm( ) const{ std::string filename = diagonal_spm_file_name( true, false ); std::ofstream outfile(filename); outfile << std::setprecision(16); outfile << d_spm; } */ } //end namespace ColorFull diff --git a/Core/Test_functions.cc b/Core/Test_functions.cc --- a/Core/Test_functions.cc +++ b/Core/Test_functions.cc @@ -1,1481 +1,1476 @@ // -*- C++ -*- /* Test_functions.cc * Contains the definitions of the class Test_Functions related types and operators. * Created on: Nov 29, 2012 * Author: Malin Sjodahl */ #include "Test_functions.h" #include <ctime> namespace ColorFull { // Checks that a numerical and Polynomial version of a matrix is the same. // This checks that the reading in of Polynomials is correctly handled, // at least numerically. double Test_functions::Pd_spm_read_test_num( const char* d_spm_file_name, const char* P_spm_file_name, const char* logfile, const double accu ){ // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::Pd_spm_read_test_num" << std::endl; //std::cout << "Test_functions::Pd_spm_read_test_num" << std::endl; // To contain largest numerical difference double largest_diff=0; // Read in numerical spm Col_basis Cbd; Cbd.read_in_d_spm( d_spm_file_name ); //std::cout << "Test_functions::Pd_spm_read_test_num: read d_matr" << std::endl; // Read in Polynomial spm, and make numerical version Col_basis CbP; CbP.read_in_P_spm( P_spm_file_name ); //std::cout << "Test_functions::Pd_spm_read_test_num: read P_matr" << std::endl; - CbP.d_spm = d_num(CbP.P_spm); + CbP.d_spm = double_num(CbP.P_spm); // Check that size agree if( CbP.d_spm.size() != Cbd.d_spm.size() ){ std::stringstream ss; ss << "The size of the numerical spm was " << Cbd.d_spm.size() << " whereas the Polynomial had size " << Cbd.P_spm.size() << ". Check input files, if are ok, check read in functions." << std::endl; std::cerr << "Pd_spm_read_test_num:" << ss.str(); outfile << ss.str(); throw CbP.d_spm; } //std::cout << "Test_functions::The matrix size is " << Cbd.d_spm.size() << std::endl; //std::cout << "Test_functions::The matrix size is " << CbP.d_spm.size() << std::endl; // Loop over all entries in matrices for (uint mi = 0; mi < CbP.d_spm.size(); mi++) { for (uint mj = 0; mj < CbP.d_spm.size(); mj++) { double abs_diff = 0.0; if ( CbP.d_spm.at(mi).at(mj) != 0 and Cbd.d_spm.at(mi).at(mj) != 0 ){ abs_diff = std::abs( CbP.d_spm.at(mi).at(mj) / Cbd.d_spm.at(mi).at(mj) - 1.0); } // If one value is 0, and the other not if (( CbP.d_spm.at(mi).at(mj) == 0 and Cbd.d_spm.at(mi).at(mj) != 0 ) or ( CbP.d_spm.at(mi).at(mj) != 0 and Cbd.d_spm.at(mi).at(mj) == 0)) { std::stringstream ss; ss << mi << " " << mj << ", the first numerical read in value was " << Cbd.d_spm.at(mi).at(mj) << " and the Polynomial read in value was " << Cbd.P_spm.at(mi).at(mj) << "." << std::endl; outfile << ss.str(); std::cerr << "Pd_spm_read_test_num:" << ss.str(); } // Numerically check that values agree if (abs_diff > accu) { std::stringstream ss; ss << "The scalar product matrices at " << mi << " " << mj << " do not agree." << " The read numerical value was " << Cbd.d_spm.at(mi).at(mj) << " and the read in Polynomial version was " << CbP.d_spm.at(mi).at(mj) << std::endl; //std::cout << "Test_functions::Pd_spm_read_test_num: " << ss.str(); outfile << ss.str(); outfile << abs_diff; } //std::cout //<< "Test_functions::Pd_spm_read_test_num: abs_diff for " << mi << " " << mj << " " //<< abs_diff << std::endl; // Look for largest difference if (abs_diff > largest_diff) largest_diff = abs_diff; } } outfile << "Compared numerical spm " << d_spm_file_name << " and Polynomial spm " << P_spm_file_name << " and found largest numerical difference in abs(v1/v2 -1) " << largest_diff << "." << std::endl; outfile << std::endl; outfile.flush(); return largest_diff; } // Read in, write out, and read in complicated Polynomial in scalar product matrix // Test that they agree numerically. double Test_functions::P_spm_read_write_test_num( const char* basis_file_name, const char* out_filename, const char* logfile, const double accu ){ //std::cout << "P_spm_read_write_test_num: Entering" << std::endl; // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "P_spm_read_write_test_num:" << std::endl; double largest_diff=0; // Read in spm Col_basis Cbr; Cbr.read_in_P_spm( basis_file_name ); // Write out spm Cbr.write_out_spm( Cbr.P_spm, out_filename ); //std::cout << "P_spm_read_write_test_num: Wrote out spm" << std::endl; // Read in the written spm Col_basis Cbw; //Cbw.read_in_P_spm( basis_file_name ); Cbw.read_in_P_spm( out_filename ); // Check that size of matrices agree if ( Cbw.P_spm.size() != Cbr.P_spm.size() ) { std::stringstream ss; ss << "The first read in scalar product matrix has size " << Cbr.P_spm.size() << ". After writing out and reading in again the size is " << Cbw.P_spm.size() << ".Please check read/write functions." << std::endl; std::cerr << "Test_functions::basis_read_write_test_num: " << ss.str(); outfile << ss.str(); throw Cbw.P_spm.size(); } // Make numerical versions - dmatr num_P_smpr=d_num( Cbr.P_spm ); - dmatr num_P_smpw=d_num( Cbw.P_spm ); + dmatr num_P_smpr=double_num( Cbr.P_spm ); + dmatr num_P_smpw=double_num( Cbw.P_spm ); // Loop over all entries in matrices for (uint mi = 0; mi < Cbr.P_spm.size(); mi++) { for (uint mj = 0; mj < Cbr.P_spm.size(); mj++) { double abs_diff = 0.0; if ( num_P_smpr.at(mi).at(mj) != 0 and num_P_smpw.at(mi).at(mj) != 0 ){ abs_diff = std::abs( num_P_smpr.at(mi).at(mj) / num_P_smpw.at(mi).at(mj) - 1.0); } // If one value is 0, and the other not if (( num_P_smpr.at(mi).at(mj) != 0 and num_P_smpw.at(mi).at(mj) == 0) or ( num_P_smpw.at(mi).at(mj) != 0 and num_P_smpr.at(mi).at(mj) == 0)) { outfile << mi << " " << mj << ", the first read in value was " - << c_num(Cbr.P_spm.at(mi).at(mj)) + << cnum_num(Cbr.P_spm.at(mi).at(mj)) << " and after writing and reading in again it was " - << c_num(Cbw.P_spm.at(mi).at(mj)) << "." << std::endl; + << cnum_num(Cbw.P_spm.at(mi).at(mj)) << "." << std::endl; } // Numerically check that values agree if (abs_diff > accu) { std::cout << "Test_functions::P_spm_read_write_test_num: The scalar product matrices at " << mi << " " << mj << " do not agree." << "The first read in value was " << num_P_smpr.at(mi).at(mj) - << " numerically " << c_num( Cbr.P_spm.at(mi).at(mj)) + << " numerically " << cnum_num( Cbr.P_spm.at(mi).at(mj)) << " and after writing out and reading in again it was " << Cbw.P_spm.at(mi).at(mj) << " numerically " << num_P_smpr.at(mi).at(mj) << std::endl; outfile << "The scalar product matrices at " << mi << " " << mj << " do not agree." << "The first read in value was " << Cbr.P_spm.at(mi).at(mj) << " and after writing out and reading in agian it was " << Cbw.P_spm.at(mi).at(mj) << std::endl; } //std::cout //<< "Test_functions::P_spm_read_write_test_num: abs_diff for " << mi << " " << mj << " " //<< abs_diff << std::endl; // Look for largest difference if (abs_diff > largest_diff) largest_diff = abs_diff; } } // Write to logfile if( largest_diff < accu){ outfile << "The largest difference between first and second read was " << largest_diff << "." << std::endl;} else { outfile << "The largest difference between first and second read was " << largest_diff << " which is larger than desired accuracy " << accu << "." << std::endl; } outfile << std::endl; outfile.flush(); return largest_diff; } // Function for comparing the numerical version of a normal ordered, cf free matrix // compared to one where cf remains and which has not been simplified and normal ordered. // Tests remove_cf, normal_order and simplify for Polynomials. double Test_functions::P_spm_remove_cf_simplify_normal_order_test( const poly_matr & Pm, const char* logfile, const double accu ){ // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "P_spm_remove_cf_simplify_normal_order_test:" << std::endl; poly_matr Pm_new=Pm; double largest_diff=0.0; // remove cf - dmatr dspm=d_num( Pm ); + dmatr dspm=double_num( Pm ); Pm_new=remove_cf( Pm_new ); // we may have 0's, simplify Pm_new=simplify( Pm_new ); // then normal order Pm_new=normal_order( Pm_new ); - dmatr dspm_new=d_num( Pm_new ); + dmatr dspm_new=double_num( Pm_new ); for (uint i= 0; i < dspm.size(); i++ ){ for (uint j= 0; j < dspm.size(); j++ ){ if( dspm.at(i).at(j)==0 and abs( (dspm_new.at(i).at(j)-dspm.at(i).at(j)) ) > largest_diff ) largest_diff=abs( (dspm_new.at(i).at(j)-dspm.at(i).at(j)) ); else if( dspm_new.at(i).at(j)!=0 and abs( (dspm_new.at(i).at(j)-dspm.at(i).at(j))/dspm.at(i).at(j) ) > largest_diff ) largest_diff=abs( (dspm_new.at(i).at(j)-dspm.at(i).at(j))/dspm.at(i).at(j) ) > largest_diff; /* std::cout << "P_spm_remove_cf_simplify_normal_order_test: read in value " << dspm.at(i).at(j) << " and after removing cf and normal ordering " << dspm_new.at(i).at(j) << ", abs_diff for " << i << " and " << j << " " << abs( (dspm_new.at(i).at(j)-dspm.at(i).at(j))) << std::endl; std::cout << "P_spm_remove_cf_simplify_normal_order_test: original polynomial " << Pm.at(i).at(j) - << " numerically " << c_num( Pm.at(i).at(j) ) << std::endl; + << " numerically " << cnum_num( Pm.at(i).at(j) ) << std::endl; std::cout << "P_spm_remove_cf_simplify_normal_order_test: final polynomial " - << " numerically " << c_num( Pm.at(i).at(j) ) << std::endl; + << " numerically " << cnum_num( Pm.at(i).at(j) ) << std::endl; */ } } //write_out_spm( Pm_new, "ColorResults/TestOutput/Pm_no_cf" ); // Write to logfile if( largest_diff < accu){ outfile << "The largest numerical difference after removing cf and normal ordering was " << largest_diff << "." << std::endl;} else { outfile << "The largest numerical difference after removing cf and normal ordering was " << largest_diff << " which is larger than desired accuracy " << accu << "." << std::endl; } outfile << std::endl; outfile.flush(); return largest_diff; } // Function for checking a created trace basis against a read in basis. // Returns true if the bases are the same. // Terminates if bases have different lengths, // and write error messages if bases are different. bool Test_functions::Trace_basis_test( const int n_q, const int n_g, const int n_loop, const char* basis_file_name, const char* logfile) { // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::Trace_basis_test:" << std::endl; bool bases_same = true; // Read in basis Trace_basis Tbr; Tbr.read_in_basis(basis_file_name); // To contain the created basis Trace_basis Tbc; Tbc.create_basis(n_q, n_g, n_loop); // Check that the lengths are the same if (Tbc.size() != Tbr.size()) { std::stringstream ss; ss << "The created basis has size " << Tbc.size() << " and the read in basis " << Tbr.size() << " Check that bases are compatible." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::Trace_basis_test: " << ss.str(); throw Tbc; } // Check that each Col_amp is the same, if not write error message for (uint cai = 0; cai < Tbc.size(); cai++) { if (Tbc.at(cai) != Tbr.at(cai)) { std::stringstream ss; ss <<"The basis vector at place " << cai << " is not the same in the created and read in basis. " << "In the created it was " << Tbc.at(cai) << " and in the read in it was " << Tbr.at(cai) <<"." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::Trace_basis_test:" << ss; bases_same = false; } } // Write test result to logfile if (bases_same) { outfile << "Tested that all " << Tbc.size() << " basis vectors agree for nq= " << n_q << " ng=" << n_g << " and n_loop " << n_loop << "." << std::endl; } else { std::stringstream ss; ss << "Test failed for " << "for nq= " << n_q << ", =" << n_g << " and n_loop=" << n_loop << "while comparing to the file " << basis_file_name << "." << std::endl; std::cerr << ss.str(); outfile << ss.str(); } outfile << std::endl; outfile.flush(); return bases_same; } // Function for checking a created trace basis against a read in basis. // Returns true if the bases are the same. // Terminates if bases have different lengths, // and write error messages if bases are different. bool Test_functions::Tree_level_gluon_basis_test( const int n_g, const char* basis_file_name, const char* logfile) { // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::Tree_level_gluon_basis_test" << std::endl; bool bases_same = true; // Read in basis Tree_level_gluon_basis Tgbr; Tgbr.read_in_basis(basis_file_name); // To contain the created basis Tree_level_gluon_basis Tgbc; Tgbc.create_basis(n_g); // Check that the lengths are the same if (Tgbc.size() != Tgbr.size()) { std::stringstream ss; ss << "The created basis has size " << Tgbc.size() << " and the read in basis " << Tgbr.size() << " Check that bases are compatible." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::Tree_level_gluon_basis_test: " << ss.str(); throw Tgbc; } // Check that each Col_amp is the same, if not write error message for (uint cai = 0; cai < Tgbc.size(); cai++) { if (Tgbc.at(cai) != Tgbr.at(cai)) { std::stringstream ss; ss <<"The basis vector at place " << cai << " is not the same in the created and read in basis. " << "In the created it was " << Tgbc.at(cai) << " and in the read in it was " << Tgbr.at(cai) <<"." << std::endl; outfile << ss.str(); std::cerr << "Tree_level_gluon_basis_test::Trace_basis_test:" << ss; bases_same = false; } } // Write test result to logfile if (bases_same) { outfile << "Tested that all " << Tgbc.size() << " basis vectors agree for ng= " << n_g << " agree." << std::endl; } else { std::stringstream ss; ss << "Test failed for " << "for ng= " << n_g << " while comparing to the file " << basis_file_name << "." << std::endl; std::cerr << ss.str(); outfile << ss.str(); } outfile << std::endl; outfile.flush(); return bases_same; } double Test_functions::basis_read_write_test_num(const char* filename, const char* out_filename, const char* logfile, const double accu) { // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::Test_functions::basis_read_write_test_num: \nTesting file " << filename << std::endl; // To contain the largest numerical difference double largest_diff = 0; Col_basis Cb1, Cb2; // Read in the basis Cb1.read_in_basis(filename); // Write out the basis Cb1.write_out_basis(out_filename); // Read it in again Cb2.read_in_basis(out_filename); // Check that lengths agree if (!Cb1.size() == Cb2.size()) { std::stringstream ss; ss << "The first read in basis has size " << Cb1.size() << ". After writing out and reading in again the size is " << Cb2.size() << ".Please check read/write functions." << std::endl; std::cerr << "Test_functions::basis_read_write_test_num: " << ss.str(); outfile << ss.str(); throw Cb1; } // Convert all algebra to numbers // Loop over basis vectors (Col_amps) for (uint cai = 0; cai < Cb1.size(); cai++) { // Check that the basis vector has equally many Col_strs if (Cb1.cb.at(cai).size() != Cb2.cb.at(cai).size()) { std::stringstream ss; ss << "Test_functions::basis_read_write_test_num: The basis vector " << cai << " has size " << Cb1.cb.at(cai).size() << " in the initial read-in basis. After writing out and reading in again, the size is " << Cb2.cb.at(cai).size() << ". Please check read/write functions." << std::endl; std::cerr << ss.str(); outfile << ss.str(); throw Cb1.at(cai); } // Loop over Col_strs in the col_amps for (uint csi = 0; csi < Cb1.cb.at(cai).size(); csi++) { // Check that they agree // First check col_str if (Cb1.cb.at(cai).at(csi).cs != Cb2.cb.at(cai).at(csi).cs) { std::stringstream ss; ss << " The basis vector " << cai << " had a col_str which initially was " << Cb1.cb.at(cai).at(csi).cs << " and after writing out and reading in again was " << Cb1.cb.at(cai).at(csi).cs << ". Please check read/write functions." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::basis_read_write_test_num: " << ss.str(); } //std::cout << "Test_functions::basis_read_write_test_num: cai " << cai << " csi "<< csi<< std::endl; //std::cout << "Test_functions::basis_read_write_test_num: largest_diff " << largest_diff << std::endl; // Then check numerical version of multiplying Polynomial double abs_diff = abs( - c_num(Cb1.cb.at(cai).at(csi).Poly) - / c_num(Cb2.cb.at(cai).at(csi).Poly) - 1.0); + cnum_num(Cb1.cb.at(cai).at(csi).Poly) + / cnum_num(Cb2.cb.at(cai).at(csi).Poly) - 1.0); //std::cout << "Test_functions::basis_read_write_test_num: abs_diff " << abs_diff << "." <<std::endl; if (abs_diff > accu) { std::stringstream ss; ss << "The basis vector " << cai << " had a col_str " << csi << " =" << Cb1.cb.at(cai).at(csi) << " with multiplying Polynomial with value " - << c_num(Cb1.cb.at(cai).at(csi).Poly) + << cnum_num(Cb1.cb.at(cai).at(csi).Poly) << " and after writing out and reading in again the value was " - << c_num(Cb2.cb.at(cai).at(csi).Poly) + << cnum_num(Cb2.cb.at(cai).at(csi).Poly) << ". The first polynomial was " << Cb1.cb.at(cai).at(csi).Poly << " and the second " << Cb2.cb.at(cai).at(csi).Poly << "." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::basis_read_write_test_num: " << ss.str(); } // Look for largest difference if (abs_diff > largest_diff) largest_diff = abs_diff; //std::cout << "Test_functions::basis_read_write_test_num: largest_diff " << largest_diff << std::endl; } } //std::ofstream outfile(logfile, std::ios::app); outfile << "Tested file " << filename << " and found largest numerical difference abs(v1/v2-1) " << largest_diff << std::endl; outfile << std::endl; outfile.flush(); return largest_diff; } // Read in, write out, and read in complicated polynomial in scalar product matrix. // To have a standard form also remove_cf, normal_order, and simplify are used. // Test that matrices agree algebraically. bool Test_functions::P_spm_read_write_test_alg( const char* filename, const char* out_filename, const char* logfile ){ //std::cout << "Test_functions::P_spm_read_write_test_alg: Entering" << std::endl; // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "P_spm_read_write_test_alg:" << std::endl; // Read in spm Col_basis Cbr; Cbr.read_in_P_spm( filename ); // make it into a standard form Cbr.P_spm=remove_cf( Cbr.P_spm ); Cbr.P_spm=simplify( Cbr.P_spm ); Cbr.P_spm=normal_order( Cbr.P_spm ); // Write out spm Cbr.write_out_spm( Cbr.P_spm, out_filename ); //std::cout << "P_spm_read_write_test_alg: Wrote out spm" << std::endl; // Read in the written spm Col_basis Cbw; Cbw.read_in_P_spm( out_filename ); // Translate the new read in matrix to standard form Cbw.P_spm=remove_cf( Cbw.P_spm ); Cbw.P_spm=simplify( Cbw.P_spm ); Cbw.P_spm=normal_order( Cbw.P_spm ); // Check that size of matrices agree if ( Cbw.P_spm.size() != Cbr.P_spm.size() ) { std::stringstream ss; ss << "The first read in scalar product matrix has size " << Cbr.P_spm.size() << ". After writing out and reading in again the size is " << Cbw.P_spm.size() << ".Please check read/write functions." << std::endl; std::cerr << "Test_functions::basis_read_write_test_alg: " << ss.str(); outfile << ss.str(); throw Cbw.P_spm.size(); } bool all_same=true; // Loop over all entries in matrices for ( uint mi = 0; mi < Cbr.P_spm.size(); mi++) { for ( uint mj = 0; mj < Cbr.P_spm.size(); mj++ ) { if ( Cbw.P_spm.at(mi).at(mj) != Cbr.P_spm.at(mi).at(mj) ){ //if ( true ){ all_same=false; std::cout << "Test_functions::P_spm_read_write_test_alg: The scalar product matrices at " << mi << " " << mj << " do not agree." << "The first read in value was \n" << Cbr.P_spm.at(mi).at(mj) - << " numerically " << c_num( Cbr.P_spm.at(mi).at(mj)) + << " numerically " << cnum_num( Cbr.P_spm.at(mi).at(mj)) << " and after writing out and reading in again it was \n" << Cbw.P_spm.at(mi).at(mj) - << " numerically " << c_num( Cbw.P_spm.at(mi).at(mj) ) + << " numerically " << cnum_num( Cbw.P_spm.at(mi).at(mj) ) << std::endl; outfile << "Test_functions::P_spm_read_write_test_alg: The scalar product matrices at " << mi << " " << mj << " do not agree." << "The first read in value was \n" << Cbr.P_spm.at(mi).at(mj) - << " numerically " << c_num( Cbr.P_spm.at(mi).at(mj)) + << " numerically " << cnum_num( Cbr.P_spm.at(mi).at(mj)) << " and after writing out and reading in again it was \n" << Cbw.P_spm.at(mi).at(mj) - << " numerically " << c_num( Cbw.P_spm.at(mi).at(mj) ) + << " numerically " << cnum_num( Cbw.P_spm.at(mi).at(mj) ) << std::endl; } } } // Write to logfile if( all_same ){ outfile << "The read in and written out Polynomials agree." << std::endl;} else { outfile << "WARNING: The read in and written out Polynomials do not agree!" << std::endl; throw Cbr.P_spm; } outfile << std::endl; outfile.flush(); return all_same; } double Test_functions::scalar_product_matrix_test(Col_basis* Cb, const char* spm_file, const char* logfile, const double accu, bool use_mem) { //std::cout << "Test_functions::scalar_product_matrix_test: Entering with file " << spm_file << std::endl; // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::scalar_product_matrix_test:\n Checking spm-file " << spm_file<< std::endl; if( use_mem ) outfile << "Using memoization, "<< std::endl; // To contain largest numerical difference double largest_diff = 0.0; // Read in ColorFull results Col_basis Cbr; Cbr.read_in_d_spm(spm_file); //std::cout << "Test_functions::scalar_product_matrix_test: The read in spm is " // << Cbr.d_spm << "\n with size " << Cbr.d_spm.size(); // Check that the lengths are the same if (Cbr.d_spm.size() != Cb-> size()) { std::stringstream ss; ss << "The given basis has size " << Cb-> size() << " and the read in scalar product matrix " << Cbr.d_spm.size() << ". Please check that bases are compatible." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::scalar_product_matrix_test: " << ss.str(); throw Cbr; } //else std::cout << "size was " << Cb.size() << std::endl; // Calculate scalar products in given basis if(use_mem) Cb-> scalar_product_matrix_mem_num(); else Cb-> scalar_product_matrix_num(); //std::cout <<"Test_functions::scalar_product_matrix_test: size of calculated spm matrix " << Cb->d_spm.size() << std::endl; // Loop over all entries in matrices for ( uint mi = 0; mi < Cb-> size(); mi++ ) { for ( uint mj = 0; mj < Cb-> size(); mj++ ) { double abs_diff = 0.0; if (Cbr.d_spm.at(mi).at(mj) != 0 and Cb-> d_spm.at(mi).at(mj) != 0) abs_diff = std::abs( Cbr.d_spm.at(mi).at(mj) / Cb -> d_spm.at(mi).at(mj) - 1.0); // If one value is 0, and the other not if ((Cbr.d_spm.at(mi).at(mj) != 0 and Cb -> d_spm.at(mi).at(mj) == 0) or (Cbr.d_spm.at(mi).at(mj) == 0 and Cb -> d_spm.at(mi).at(mj) != 0)) { outfile //<< "Test_functions::scalar_product_matrix_test: For element " << mi << " " << mj << ", the read in value was " << Cbr.d_spm.at(mi).at(mj) << " and the calculated value was " << Cb -> d_spm.at(mi).at(mj) << std::endl; } /* std::cout << "Test_functions::scalar_product_matrix_test: read in value " << Cbr.d_spm.at(mi).at(mj) << " and calculated " << Cb-> d_spm.at(mi).at(mj) << ", abs_diff for " << mi << " and " << mj << " " << abs_diff << std::endl; */ // Numerically check that values agree if (abs_diff > accu) { std::stringstream ss; ss << "Test_functions::scalar_product_matrix_test: The scalar product matrices at " << mi << " " << mj << " do not agree." << "The read in value was " << Cbr.d_spm.at(mi).at(mj) << " and the calculated value was " << Cb -> d_spm.at(mi).at(mj) << std::endl; std::cerr << "Test_functions::scalar_product_matrix_test: " << ss.str(); outfile << ss.str(); } // Look for largest difference if (abs_diff > largest_diff) largest_diff = abs_diff; } } if ( largest_diff < accu ) { outfile //<< "Test_functions::scalar_product_matrix_test:" << "Tested spm for given basis against " << spm_file << " and found maximal abs(v1/v2 -1) = " << largest_diff << ", smaller than the given accuracy " << accu << " for all entries." << std::endl; } if ( largest_diff > accu ) { std::stringstream ss; ss //<< "Test_functions::scalar_product_matrix_test:" << "Tested spm for given basis against " << spm_file << " and found maximal abs(v1/v2 -1) = " << largest_diff << ", LARGER than the given accuracy " << accu << " for all entries." << std::endl; outfile << ss; std::cerr << "Test_functions::scalar_product_matrix_test:" << ss; } //std::cout << "Test_functions::scalar_product_matrix_test:Exiting with largest diff " << largest_diff << std::endl; outfile << std::endl; outfile.flush(); return largest_diff; } bool Test_functions::scalar_product_matrix_test_alg( Col_basis* Cb, const char* spm_file, const char* logfile, bool use_mem ) { //std::cout << "Test_functions::scalar_product_matrix_test_alg: Entering with file " << spm_file << std::endl; // Continue writing to logfile std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::scalar_product_matrix_test_alg:\n Checking spm-file " << spm_file<< std::endl; if( use_mem ) outfile << "Using memoization, "<< std::endl; // Do all entries agree? bool all_agree=true; // Read in spm matrix results, and convert it to standard form Col_basis Cbr; Cbr.read_in_P_spm(spm_file); Cbr.P_spm= remove_cf( Cbr.P_spm ); Cbr.P_spm= simplify( Cbr.P_spm ); Cbr.P_spm= normal_order( Cbr.P_spm ); //std::cout << "Test_functions::scalar_product_matrix_test: The read in spm is " // << Cbr.d_spm << "\n with size " << Cbr.d_spm.size(); // Check that the lengths are the same if (Cbr.P_spm.size() != Cb-> size()) { std::stringstream ss; ss << "The given basis has size " << Cb-> size() << " and the read in scalar product matrix " << Cbr.P_spm.size() << ". Please check that bases are compatible." << std::endl; outfile << ss.str(); std::cerr << "Test_functions::scalar_product_matrix_test_alg: " << ss.str(); throw Cbr; } //else std::cout << "size was " << Cb.size() << std::endl; // Calculate scalar products in given basis Cb->P_spm.clear(); if(use_mem) Cb-> scalar_product_matrix_mem(); else Cb-> scalar_product_matrix(); Cb->P_spm= remove_cf(Cb->P_spm); Cb->P_spm= simplify(Cb->P_spm); Cb->P_spm= normal_order(Cb->P_spm); //std::cout <<"Test_functions::scalar_product_matrix_test_alg: size of calculated spm matrix " << Cb->d_spm.size() << std::endl; // Loop over all entries in matrices for ( uint mi = 0; mi < Cb-> size(); mi++) { for ( uint mj = 0; mj < Cb-> size(); mj++) { //if ( true){ if ( Cbr.P_spm.at(mi).at(mj) != Cb-> P_spm.at(mi).at(mj) ){ all_agree=false; std::stringstream ss; ss << "Test_functions::scalar_product_matrix_test_alg: The scalar product matrices at " << mi << " " << mj << " do not agree." - << "The read in value was \n" << Cbr.P_spm.at(mi).at(mj) << " numerically " << c_num( Cbr.P_spm.at(mi).at(mj) ) + << "The read in value was \n" << Cbr.P_spm.at(mi).at(mj) << " numerically " << cnum_num( Cbr.P_spm.at(mi).at(mj) ) << " and the calculated value was \n" - << Cb -> P_spm.at(mi).at(mj) << " numerically " << c_num( Cb->P_spm.at(mi).at(mj) ) << std::endl; + << Cb -> P_spm.at(mi).at(mj) << " numerically " << cnum_num( Cb->P_spm.at(mi).at(mj) ) << std::endl; std::cerr << "Test_functions::scalar_product_matrix_test: " << ss.str(); outfile << ss.str(); throw Cbr.P_spm; } } } if ( all_agree ) { outfile << "Tested spm for given basis against " << spm_file << " for all entries and found algebraic agreement." << std::endl; } if ( ! all_agree ) { outfile << "WARNING: Tested spm for given basis against " << spm_file << " all entries did not agree! " << std::endl; } //std::cout << "Test_functions::scalar_product_matrix_test:Exiting with largest diff " << largest_diff << std::endl; outfile << std::endl; outfile.flush(); return all_agree; } // Function for generating a random amplitude in the basis Cb, useful for testing. // Coefficients are random numbers in [-1/2,1/2] Col_amp Test_functions::random_real_amplitude( const Col_basis* Cb ) const{ Col_amp Ca_res; dvec dv; for( uint i=0; i < Cb->cb.size(); i++ ) //Ca_res.ca.push_back( (rand()-0.5)*Cb->cb.at(i) ); { double my_rand=(rand()/float(RAND_MAX)-0.5); Ca_res = Ca_res+my_rand*Cb->cb.at(i); dv.push_back(my_rand); } //std::cout << "Test_functions::random_real_amplitude: The coefficients are" // << dv << std::endl; return Ca_res; } // Function for generating a random amplitude in the basis Cb, useful for testing. // Coefficients are random numbers in [-1/2,1/2] Col_amp Test_functions::random_complex_amplitude( const Col_basis* Cb ) const{ Col_amp Ca_res; for( uint i=0; i < Cb->cb.size(); i++ ) { double real_part= rand()/float(RAND_MAX)-0.5; double im_part= rand()/float(RAND_MAX)-0.5; cnum comp_rand; comp_rand.imag(im_part); comp_rand.real( real_part); Ca_res = Ca_res+comp_rand*Cb->cb.at(i); } return Ca_res; } bool Test_functions::test( ){ // Construct name time_t rawtime; struct tm * timeinfo; char buffer [100]; time ( &rawtime ); timeinfo = localtime ( &rawtime ); strftime (buffer,100,"ColorResults/TestLogs/%y%m%d_%H%M%S",timeinfo); // Make test return test( buffer ); } double Test_functions::scalar_product_test( Col_basis* Cb, const char* logfile, const double accu ) const { std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::scalar_product_test:\n Checking random amplitude for basis with " << Cb->n_q << " quarks and " << Cb->n_g << " gluons " << std::endl; double maxdiff=0.0; // TODO Deal with issue with complex amplitudes in gluon only case //Col_amp Ca1 = random_complex_amplitude( Cb ); //Col_amp Ca2 = random_complex_amplitude( Cb ); Col_amp Ca1 = random_real_amplitude( Cb ); Col_amp Ca2 = random_real_amplitude( Cb ); Col_amp Ca11=Ca1, Ca21=Ca2; if (Cb->is_tree_level_gluon_basis()== true ){ int sign = (Cb->n_g % 2 ? -1 : 1); Ca11=Ca1 + sign*conjugate( Ca1 ); Ca21=Ca2 + sign*conjugate( Ca2 ); //std::cout << "tree_level_gluon_basis " << std::endl; } //std::cout << "Test_functions::Random amps\n" // << Ca11 << " and\n " << Ca21 << std::endl; // This will use the Nc and cf of test functions and not of // basis and will therefore not give correct numerical answer - //cnum sp1 = c_num ( scalar_product(Ca11, Ca21) ); - cnum sp1 = Cb->CF->c_num ( scalar_product(Ca11, Ca21) ); + //cnum sp1 = cnum_num ( scalar_product(Ca11, Ca21) ); + cnum sp1 = Cb->CF->cnum_num ( scalar_product(Ca11, Ca21) ); //std::cout <<"Scalar prod 1 " << sp1 << std::endl; - //cnum sp2 = c_num( Cb->scalar_product( Ca1, Ca2 ) ); - cnum sp2 = Cb->CF->c_num( Cb->scalar_product( Ca1, Ca2 ) ); + //cnum sp2 = cnum_num( Cb->scalar_product( Ca1, Ca2 ) ); + cnum sp2 = Cb->CF->cnum_num( Cb->scalar_product( Ca1, Ca2 ) ); //std::cout <<"Scalar prod 2 " << sp2 << std::endl; maxdiff= abs( sp1 - sp2 ); cnum sp3 = Cb->scalar_product_num( Ca1, Ca2); //std::cout <<"Scalar prod 3 " << sp3 << std::endl; if ( abs( sp1 - sp3 ) > maxdiff ) maxdiff = abs( sp1 - sp3 ); poly_vec pv1 = Cb-> decompose( Ca1 ); poly_vec pv2 = Cb-> decompose( Ca2 ); //std::cout << "pv1 " << pv1 << std::endl; //std::cout << "pv2 " << pv2 << std::endl; - cvec cv1 = Cb->CF->c_num( pv1 ); - cvec cv2= Cb->CF->c_num( pv2 ); + cvec cv1 = Cb->CF->cnum_num( pv1 ); + cvec cv2= Cb->CF->cnum_num( pv2 ); cnum sp4 = Cb-> scalar_product_num_fast( cv1, cv2 ); //std::cout <<"Scalar prod 4 " << sp4 << std::endl; if ( abs( sp1 - sp4 ) > maxdiff ) maxdiff = abs( sp1 - sp4 ); if ( maxdiff < accu ){ outfile << "Found maximal difference " << maxdiff << " smaller than accuracy " << " accu" << std::endl; } else { outfile << "Found maximal difference " << maxdiff << " LARGER than accuracy " << " accu" << std::endl; outfile << "the scalar products were\n" << "direct " << sp1 << "\nCb->scalar_product( Ca1, Ca2 ) " << sp2 << "\nCb->scalar_product_num( Ca1, Ca2) " << sp3 << "\nCb-> scalar_product_num_fast( cv1, cv2 )" << sp4 << std::endl; std::cerr << "Found maximal difference " << maxdiff << " LARGER than accuracy " << " accu" << std::endl; } outfile << std::endl; return maxdiff; }; bool Test_functions::new_vector_numbers_Trace_basis_test( int n_q, int n_g, const char* logfile ) const{ std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::new_vector_numbers_Trace_basis_test:\n Checking radiated amplitudes " << "for " << n_q << " quarks and " << n_g <<" gluons." << std::endl; Trace_basis Tb_large, Tb_small; Tb_large.create_basis( n_q, n_g+1,0 ); // This should be the larger basis Tb_small.create_basis( n_q, n_g, 0 ); // Were all tensors correctly mapped? bool all_right = true; // Loop over all basis vectors and see that the emission from all partons gets right for (uint t_num = 0; t_num < Tb_small.size(); t_num++) { // Loop over possible emitters and see that the vector numbers after emission are correct for (int em = 1; em < n_q * 2 + n_g + 1; em++) { std::pair<int,int> nv1=Tb_large.new_vector_numbers( Tb_small.at(t_num).at(0), em ); std::pair<int,int> nv2=Tb_large.new_vector_numbers( t_num, em, n_q, n_g, 0); //std::cout << "Tensor number " << t_num << " emitter " << em <<std::endl; //std::cout << "nv1 " << nv1 << " nv2 " << nv2 << std::endl; if ( nv1!= nv2 ){ outfile << "WARNING: tensor numbers did not match for initial tensor " << Tb_small.at(t_num) << " with tensor number " << t_num << ", and emission from " << em << ". Direct method gave " << nv1 << " and indirect gave "<< nv2 << std::endl; all_right=false; } } } if ( all_right ){ outfile << "All tensors correctly mapped" << std::endl; } else { outfile << "There was at least one mistake in the mapping " << std::endl; std::cerr << "There was at least one mistake in the mapping " << std::endl; } return all_right; } bool Test_functions::new_vector_numbers_Tree_level_gluon_basis_test( int n_g, const char* logfile ) const{ std::ofstream outfile(logfile, std::ios::app); outfile << "Test_functions::new_vector_numbers_Tree_level_gluon_basis_test:\n Checking radiated amplitudes " << "for " << n_g <<" gluons." << std::endl; Tree_level_gluon_basis Tb_large, Tb_small; Tb_large.create_basis( n_g+1 ); // This should be the larger basis Tb_small.create_basis( n_g ); // Were all tensors correctly mapped? bool all_right = true; // Loop over all basis vectors and see that the emission from all partons gets right for (uint t_num = 0; t_num < Tb_small.size(); t_num++) { // Loop over possible emitters and see that the vector numbers after emission are correct for (int em = 1; em < n_g + 1; em++) { std::pair<int,int> nv1=Tb_large.new_vector_numbers( Tb_small.at(t_num).at(0), em ); std::pair<int,int> nv2=Tb_large.new_vector_numbers( t_num, em, 0, n_g, 0); //std::cout << "Tensor number " << t_num << " emitter " << em <<std::endl; //std::cout << "nv1 " << nv1 << " nv2 " << nv2 << std::endl; if ( nv1!= nv2 ){ outfile << "WARNING: tensor numbers did not match for initial tensor " << Tb_small.at(t_num) << " with tensor number " << t_num << ", and emission from " << em << ". Direct method gave " << nv1 << " and indirect gave "<< nv2 << std::endl; all_right=false; } } } if ( all_right ){ outfile << "All tensors correctly mapped" << std::endl; } else { outfile << "There was at least one mistake in the mapping " << std::endl; std::cerr << "There was at least one mistake in the mapping " << std::endl; throw Tb_small; } return all_right; } double Test_functions::Col_gamma_test( Col_basis* Cb, int p1, int p2, const char* file_name, const char* logfile, double accu ) const{ // Make consistency check if ( Cb->cb.empty() ){ std::cerr << "Test_functions::Col_gamma_test: Testing CGamma for empty basis is meaningless." << std::endl; throw Cb; } // Read in Col_Gamma from file // todo this is very ugy and should be changed! Col_basis CB_dum; CB_dum.read_in_P_spm(file_name); - dmatr CG_read= Cb->CF->d_num(CB_dum.P_spm); + dmatr CG_read= Cb->CF->double_num(CB_dum.P_spm); //std::cout << "Read in CGamma matrix \n" << CG_read << std::endl; if( Cb->size() != CG_read.size() ){ std::cerr << "Test_functions::Col_gamma_test: Size of basis "<< Cb->size() <<" and read in CGamma matrix " << CG_read.size() << " do not agree." << std::endl; throw Cb; } std::ofstream outfile(logfile, std::ios::app); // Calculate Col_gamma ij - dmatr CG_cal=Cb->CF->d_num( Cb->Col_gamma( p1,p2 ) ); + dmatr CG_cal=Cb->CF->double_num( Cb->Col_gamma( p1,p2 ) ); //std::cout << CG_cal; // Check that each component is numerically equal double largest_diff=0; // Loop over all entries and compare to read in value for ( uint mi = 0; mi < Cb-> size(); mi++ ) { for ( uint mj = 0; mj < Cb-> size(); mj++ ) { double abs_diff = 0.0; if ( CG_read.at(mi).at(mj) != 0 and CG_cal.at(mi).at(mj) != 0) abs_diff = std::abs( CG_read.at(mi).at(mj) / CG_cal.at(mi).at(mj) - 1.0); // If one value is 0, and the other not if ( ( CG_read.at(mi).at(mj) != 0 and CG_cal.at(mi).at(mj) == 0 ) or (CG_read.at(mi).at(mj) == 0 and CG_cal.at(mi).at(mj) != 0 ) ) { outfile //<< "Test_functions::scalar_product_matrix_test: For element " << mi << " " << mj << ", the read in value was " << CG_read.at(mi).at(mj) << " and the calculated value was " << CG_cal.at(mi).at(mj) << std::endl; } // Numerically check that values agree if (abs_diff > accu) { std::stringstream ss; ss << "Test_functions::Col_gamma_test: The Col_gamma matrix for partons " << p1 << " and " << p2 << " do not agree at matrix entry " << mi << " " << mj << "." << " The read in value was " << CG_read.at(mi).at(mj) << " and the calculated value was " << CG_cal.at(mi).at(mj) << std::endl; std::cerr << "Test_functions::scalar_product_matrix_test: " << ss.str(); std::cerr << CG_read; outfile << ss.str(); } // Look for largest difference if (abs_diff > largest_diff) largest_diff = abs_diff; } } if ( largest_diff < accu ) { outfile << "Tested Col_gamma" << p1 << p2 <<" for given basis against " << file_name << " and found maximal abs(v1/v2 -1) = " << largest_diff << ", smaller than the given accuracy " << accu << " for all entries." << std::endl; } if ( largest_diff > accu ) { std::stringstream ss; ss << "Tested Col_gamma" << p1 << p2 <<" for given basis against " << file_name << " and found maximal abs(v1/v2 -1) = " << largest_diff << ", LARGER than the given accuracy " << accu << " for all entries." << std::endl; outfile << ss; std::cerr << "Test_functions::Col_gamma_test:" << ss; } outfile << std::endl; return largest_diff; } bool Test_functions::test( const char* logfile ){ double test_accu=0.0000000000001, res_accu=0.0, max_accu=0.0; bool basis_check=true, basis_res=true; // Writing time of test to logfile std::time_t result = std::time(NULL); std::ofstream outfile(logfile); outfile << "Test_functions::test: Making tests on " << std::asctime(std::localtime(&result)) << std::endl; outfile.flush(); - outfile << "Using Nc=" << Nc << ", TR=" << TR << " and cf=" << cf << std::endl; // Compare a read in Polynomial matrix and a read in numerical matrix // to discover mistakes in reading in Polynomials // Test file(s) created by Mathematica std::cout << "Pd_spm_read_test_num(ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q3g,ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g, logfile, test_accu);" << std::endl; res_accu=Pd_spm_read_test_num("ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q3g", "ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g", logfile, test_accu); if(res_accu > max_accu) max_accu=res_accu; // Test files created by ColorFull std::cout << "Pd_spm_read_test_num( ColorResults/TestInput/TB_d_spm_q3_g2, ColorResults/TestInput/TB_P_spm_q3_g2_v2, logfile, test_accu )" << std::endl; res_accu=Pd_spm_read_test_num( "ColorResults/TestInput/TB_d_spm_q3_g2", "ColorResults/TestInput/TB_P_spm_q3_g2_v2", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Read in, write out, and read in again std::cout << "P_spm_read_write_test_num( ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g, ColorResults/TestOutput/ColorMathSPMs/CMPqSPMTB1q3g, logfile, 0.0000000000001 )"<< std::endl; res_accu=P_spm_read_write_test_num( "ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g", "ColorResults/TestOutput/ColorMathSPMs/CMPqSPMTB1q3g", logfile, 0.0000000000001 ); if(res_accu > max_accu) max_accu=res_accu; // Test reading in and writing out and reading in Polynomial matrix again std::cout << "P_spm_read_write_test_alg(ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g, ColorResults/TestOutput/ColorMathSPMs/CM_TB_P_spm_q1_g3, logfile ); " << std::endl; P_spm_read_write_test_alg( "ColorResults/TestInput/ColorMathSPMs/CMPSPMTB1q3g", "ColorResults/TestOutput/ColorMathSPMs/CM_TB_P_spm_q1_g3", logfile ); // Test reading in and writing out bases // Testing a trace basis std::cout << "basis_read_write_test_num( ColorResults/TestInput/Trace_basis_q1_g4,ColorResults/TestOutput/Trace_basis_q1_g4, logfile, test_accu)" << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/Trace_basis_q1_g4", "ColorResults/TestOutput/Trace_basis_q1_g4", logfile, test_accu); if(res_accu > max_accu) max_accu=res_accu; // Testing a mathematica basis std::cout << "basis_read_write_test_num( ColorResults/TestInput/CMHP2gNc3,ColorResults/TestOutput/CMHP2gNc3, logfile, test_accu )" << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CMHP2gNc3", "ColorResults/TestOutput/CMHP2gNc3", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Testing a Tree_level_gluon basis with odd gluons std::cout << "basis_read_write_test_num( ColorResults/TestInput/CF_TGB_q_0_g_5,ColorResults/TestOutput/CF_TGB_q_0_g_5, logfile, test_accu )" << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CF_TGB_q_0_g_5", "ColorResults/TestOutput/CF_TGB_q_0_g_5", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Testing a Tree_level_gluon basis with even gluons std::cout << "basis_read_write_test_num( ColorResults/TestInput/CF_TGB_q_0_g_6,ColorResults/TestOutput/CF_TGB_q_0_g_6, logfile, test_accu )" << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CF_TGB_q_0_g_6", "ColorResults/TestOutput/CF_TGB_q_0_g_6", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Test reading in Mathematica created base std::cout << "basis_read_write_test_num( ColorResults/TestInput/CMHP2gNc3,ColorResults/TestOutput/CMHP2gNc3, logfile, test_accu );" << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CMHP2gNc3", "ColorResults/TestOutput/CMHP2gNc3", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; std::cout << "basis_read_write_test_num( ColorResults/TestInput/CMTB2q2g,ColorResults/TestOutput/CMTB2q2g, logfile, test_accu ); " << std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CMTB2q2g", "ColorResults/TestOutput/CMTB2q2g", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Testing a Tree_level_gluon_basis std::cout << "basis_read_write_test_num( ColorResults/TestInput/CF_TGB_q_0_g_7,ColorResults/TestOutput/CF_TGB_q_0_g_7, logfile, test_accu ) "<< std::endl; res_accu=basis_read_write_test_num( "ColorResults/TestInput/CF_TGB_q_0_g_7", "ColorResults/TestOutput/CF_TGB_q_0_g_7", logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; // Test remove_cf and normal ordering of Polynomial // read in a basis which should contain cf std::cout << "P_spm_remove_cf_simplify_normal_order_test( Tb04.P_spm, logfile, test_accu ); " << std::endl; Trace_basis Tb04; Tb04.create_basis(0,4); Tb04.scalar_product_matrix_mem(); P_spm_remove_cf_simplify_normal_order_test( Tb04.P_spm, logfile, test_accu ); // Test the creation of trace bases std::cout << "Trace_basis_test( 3, 3, 5, ColorResults/TestInput/Trace_basis_q3_g3, logfile ); " << std::endl; basis_res=Trace_basis_test( 3, 3, 5, "ColorResults/TestInput/Trace_basis_q3_g3", logfile ); if(!basis_res ) basis_check=false; std::cout << "Trace_basis_test( 2, 4, 5, ColorResults/TestInput/Basis_q2_g4, logfile ); "<< std::endl; basis_res=Trace_basis_test( 2, 4, 5, "ColorResults/TestInput/Basis_q2_g4", logfile ); if(!basis_res ) basis_check=false; std::cout << "Trace_basis_test( 3, 3, 0, ColorResults/TestInput/Trace_basis_q3_g3_loop_0, logfile ) "<< std::endl; basis_res=Trace_basis_test( 3, 3, 0, "ColorResults/TestInput/Trace_basis_q3_g3_loop_0", logfile ); if(!basis_res ) basis_check=false; // Test the creation of Tree_level_gluon_basis std::cout << "Tree_level_gluon_basis_test( 5, ColorResults/TestInput/CF_TGB_q_0_g_5, logfile ); "<< std::endl; basis_res=Tree_level_gluon_basis_test( 5, "ColorResults/TestInput/CF_TGB_q_0_g_5", logfile ); if(!basis_res ) basis_check=false; std::cout << "Tree_level_gluon_basis_test( 6, ColorResults/TestInput/CF_TGB_q_0_g_6, logfile ); "<< std::endl; basis_res=Tree_level_gluon_basis_test( 6, "ColorResults/TestInput/CF_TGB_q_0_g_6", logfile ); if(!basis_res ) basis_check=false; // Test scalar product matrices against old ColorFull results Trace_basis Tb22; Tb22.create_basis(2, 2, 0); Tb22.write_out_basis(); std::cout << "scalar_product_matrix_test( &Tb22, ColorResults/TestInput/SPM_q2_g2_Nc3.dat_mod , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb22, "ColorResults/TestInput/SPM_q2_g2_Nc3.dat_mod" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; Trace_basis Tb24; Tb24.create_basis(2, 4, 0); std::cout << "scalar_product_matrix_test( &Tb24, ColorResults/TestInput/SPM_q2_g4_Nc_3.dat , logfile, 0.000000001, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb24, "ColorResults/TestInput/SPM_q2_g4_Nc_3.dat" , logfile, 0.000000001, false ); if(res_accu > max_accu) max_accu=res_accu; // Test scalar product matrices against ColorMath results for matrices needed for H+jets Trace_basis Tb10; Tb10.create_basis(1, 0, 0); std::cout << "scalar_product_matrix_test( &Tb10, ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q0g , logfile, test_accu, false ) "<< std::endl; res_accu=scalar_product_matrix_test( &Tb10, "ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q0g" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; Trace_basis Tb11; Tb11.create_basis(1, 1, 0); std::cout << "scalar_product_matrix_test( &Tb11, ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q1g , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb11, "ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q1g" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; Trace_basis Tb12; Tb12.create_basis(1, 2, 0); std::cout << "scalar_product_matrix_test( &Tb12, ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q2g , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb12, "ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q2g" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; Trace_basis Tb13; Tb13.create_basis(1, 3, 0); std::cout << "scalar_product_matrix_test( &Tb13, ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q3g , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb13, "ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q3g" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Testing tree_level_gluon_basis spm against ColorMath when treated as a Col_basis Col_basis Ctgb05; Ctgb05.read_in_basis("ColorResults/TestInput/CF_TGB_q_0_g_5"); std::cout << "scalar_product_matrix_test( &Ctgb05, ColorResults/TestInput/CM_TGB_d_spm_q_0_g_5 , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Ctgb05, "ColorResults/TestInput/CM_TGB_d_spm_q_0_g_5" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Testing scalar product for Nc=6 Col_basis Hp06Nc6; - Hp06Nc6.CF->Nc=6; - Hp06Nc6.CF->cf=35.0/12.0; + Hp06Nc6.CF->N_c(6); Hp06Nc6.read_in_basis("ColorResults/TestInput/CM_HP_q0_g4_Nc_6"); std::cout << "scalar_product_matrix_test( &Hp06Nc6, ColorResults/TestInput/CM_HP_d_spm_q0_g4_Nc_6 , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Hp06Nc6, "ColorResults/TestInput/CM_HP_d_spm_q0_g4_Nc_6" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Testing scalar products against ColorMath for TR=1 Col_basis Tb22Tr1; - Tb22Tr1.CF->TR=1.0; - Tb22Tr1.CF->cf=8.0/3.0; + Tb22Tr1.CF->T_R(1.0); Tb22Tr1.read_in_basis("ColorResults/TestInput/CM_TB_q2_g2_TR_1"); std::cout << "scalar_product_matrix_test( &Tb22Tr1, ColorResults/TestInput/CM_TB_d_spm_q2_g2_TR_1 , logfile, test_accu, false ) "<< std::endl; res_accu=scalar_product_matrix_test( &Tb22Tr1, "ColorResults/TestInput/CM_TB_d_spm_q2_g2_TR_1" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Testing scalar products of Tree_level_gluon bases Tree_level_gluon_basis Tgb05; Tgb05.create_basis(5); std::cout << "scalar_product_matrix_test( &Tgb05, ColorResults/TestInput/CF_TGB_d_spm_q0_g5 , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tgb05, "ColorResults/TestInput/CF_TGB_d_spm_q0_g5" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; Tree_level_gluon_basis Tgb06; Tgb06.create_basis(6); std::cout << "scalar_product_matrix_test( &Tgb06, ColorResults/TestInput/CF_TGB_d_spm_q0_g6 , logfile, test_accu, false ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tgb06, "ColorResults/TestInput/CF_TGB_d_spm_q0_g6" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Test against ColorMath for Multiplet bases // Testing Hermitian 4g projectors Col_basis Mb04; std::cout << "scalar_product_matrix_test( &Mb04, ColorResults/TestInput/HandMade_SPM_MB_q0_g4_Nc3 , logfile, test_accu, false ); "<< std::endl; Mb04.read_in_basis("ColorResults/TestInput/CMHP2gNc3"); res_accu=scalar_product_matrix_test( &Mb04, "ColorResults/TestInput/HandMade_SPM_MB_q0_g4_Nc3" , logfile, test_accu, false ); if(res_accu > max_accu) max_accu=res_accu; // Would be nice to test something with both quarks and gluons as well // Testing memoization version // A standard trace basis std::cout << "scalar_product_matrix_test( &Tb12, ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q2g , logfile, test_accu, true ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tb12, "ColorResults/TestInput/ColorMathSPMs/CMSPMTB1q2g" , logfile, test_accu, true ); if(res_accu > max_accu) max_accu=res_accu; // Testing a Tree_level_gluon_basis spm against ColorMath std::cout << "scalar_product_matrix_test( &Tgb05, ColorResults/TestInput/CM_TGB_d_spm_q_0_g_5 , logfile, test_accu, true ); "<< std::endl; res_accu=scalar_product_matrix_test( &Tgb05, "ColorResults/TestInput/CM_TGB_d_spm_q_0_g_5" , logfile, test_accu, true ); if(res_accu > max_accu) max_accu=res_accu; // Test against ColorMath for Multiplet bases, Hermitian 4g projectors std::cout << "scalar_product_matrix_test( &Mb04, ColorResults/TestInput/HandMade_SPM_MB_q0_g4_Nc3 , logfile, test_accu, true ); "<< std::endl; res_accu=scalar_product_matrix_test( &Mb04, "ColorResults/TestInput/HandMade_SPM_MB_q0_g4_Nc3" , logfile, test_accu, true ); if(res_accu > max_accu) max_accu=res_accu; // Test algebraic scalar product matrix std::cout << "scalar_product_matrix_test_alg( &Tb04, ColorResults/TestInput/CM_TB_P_SPM_q0g4.m , logfile, true );"<< std::endl; scalar_product_matrix_test_alg( &Tb04, "ColorResults/TestInput/CM_TB_P_SPM_q0g4.m" , logfile, true ); // Test scalar product functions Tb13.scalar_product_matrix(); std::cout << "scalar_product_test( &Tb13, logfile, test_accu ) "<< std::endl; res_accu=scalar_product_test( &Tb13, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Tgb05.scalar_product_matrix(); std::cout << "scalar_product_test( &Tgb05, logfile, test_accu ) "<< std::endl; res_accu=scalar_product_test( &Tgb05, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Tree_level_gluon_basis Tgb04; Tgb04.create_basis(4); Tgb04.scalar_product_matrix(); std::cout << "scalar_product_test( &Tgb04, logfile, test_accu ) "<< std::endl; res_accu=scalar_product_test( &Tgb04, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Trace_basis Tb23; Tb23.create_basis(2, 3, 2); Tb23.scalar_product_matrix(); std::cout << "scalar_product_test( &Tb23, logfile, test_accu ) "<< std::endl; res_accu=scalar_product_test( &Tb23, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Tree_level_gluon_basis Tgb03; Tgb03.create_basis(3); Tgb03.scalar_product_matrix(); std::cout << "scalar_product_test( &Tgb03, logfile, test_accu ) "<< std::endl; res_accu=scalar_product_test( &Tgb03, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Orthogonal_basis Mbq2g2; Mbq2g2.read_in_basis("ColorResults/TestInput/CM_MB_q2_g2_Nc_6"); - Mbq2g2.CF->Nc=6; - Mbq2g2.CF->cf=(6.0*6.0-1)/(2* 6.0); + Mbq2g2.CF->N_c(6); + //Mbq2g2.CF->cf=(6.0*6.0-1)/(2* 6.0); res_accu=scalar_product_test( &Mbq2g2, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; Orthogonal_basis Hp2g; Hp2g.read_in_basis("ColorResults/TestInput/CMHP2gNc3"); res_accu=scalar_product_test( &Hp2g, logfile, test_accu ); if(res_accu > max_accu) max_accu=res_accu; basis_res=new_vector_numbers_Trace_basis_test( 1, 5, logfile ); if(!basis_res ) basis_check=false; basis_res=new_vector_numbers_Trace_basis_test( 2, 4, logfile ); if(!basis_res ) basis_check=false; basis_res=new_vector_numbers_Tree_level_gluon_basis_test( 5, logfile ); if(!basis_res ) basis_check=false; Tb22.create_basis(2, 2); Col_gamma_test( &Tb22, 4, 3, "ColorResults/TestInput/CM_TB_CGamma34_q2_g2", logfile, test_accu ); Col_gamma_test( &Tb22, 6, 2, "ColorResults/TestInput/CM_TB_CGamma26_q2_g2", logfile, test_accu ); Col_gamma_test( &Tb22, 6, 5, "ColorResults/TestInput/CM_TB_CGamma56_q2_g2", logfile, test_accu ); Orthogonal_basis Mb4g; - Mb4g.CF->Nc=6.0; - Mb4g.CF->TR=0.5; - Mb4g.CF->cf=0.5*(36.0-1)/6.0; + Mb4g.CF->N_c(6.0); Mb4g.read_in_basis("ColorResults/ColorMathResults/CM_HP_q0_g4_Nc_6"); Col_gamma_test( &Mb4g, 2, 1, "ColorResults/TestInput/CF_HP_CGamma12_q0_g4_Nc6", logfile, test_accu ); Col_gamma_test( &Mb4g, 3, 1, "ColorResults/TestInput/CF_HP_CGamma13_q0_g4_Nc6", logfile, test_accu ); Col_gamma_test( &Mb4g, 4, 2, "ColorResults/TestInput/CF_HP_CGamma24_q0_g4_Nc6", logfile, test_accu ); result = std::time(NULL); outfile << "Test_functions::test: All tests done on " << std::asctime(std::localtime(&result)) << " with accuracy at least "<< max_accu << std::endl; if(!basis_check) outfile << "Test_functions::test: WARNING AT LEAST ONE BASIS CHECK FAILED "; if ( max_accu > test_accu ){ outfile << "Test_functions::test: WARNING: NUMERICAL ACCURACY NOT OK, was "; outfile << max_accu << " aimed at " << test_accu; } else{ outfile << "Test_functions::test: All numerical tests ok "; } return false; } } /* namespace ColorFull */ diff --git a/Core/Test_functions.h b/Core/Test_functions.h --- a/Core/Test_functions.h +++ b/Core/Test_functions.h @@ -1,130 +1,129 @@ // -*- C++ -*- /* Test_functions.h * Contains the declarations of the class Test_functions related types and operators. * Created on: Nov 29, 2012 * Author: Malin Sjodahl */ #ifndef COLORFULL_TEST_FUNCTIONS_H_ #define COLORFULL_TEST_FUNCTIONS_H_ #include "Col_functions.h" #include "Trace_basis.h" #include "Tree_level_gluon_basis.h" #include "Orthogonal_basis.h" namespace ColorFull { class Test_functions: public ColorFull::Col_functions { public: /// Default constructor, keep values the way they are in Col_functions. Test_functions():Col_functions(){ }; /// Checks that the numerical and Polynomial versions of a matrix are the same, /// to some accuracy accu. /// This checks that the reading in of Polynomials is correctly handled, /// at least numerically. /// Write result, and report deviations to logfile. double Pd_spm_read_test_num( const char* d_spm_file_name, const char* P_spm_file_name, const char* logfile, const double accu ); /// Read in, write out, and read in Polynomial in scalar product matrix /// Test that they agree numerically to accuracy accu. /// Write result, and report deviations to logfile. double P_spm_read_write_test_num( const char* basis_file_name, const char* out_filename, const char* logfile, const double accu ); /// Read in, write out, and read in complicated polynomial in scalar product matrix. /// To have a standard form also remove_cf, normal_order, and simplify are used. /// Test that matrices agree algebraically. bool P_spm_read_write_test_alg( const char* filename, const char* out_filename, const char* logfile ); /// Function for comparing the numerical version of a normal ordered, cf free matrix /// compared to one where cf remains and which has not been simplified and normal ordered. /// Tests remove_cf, normal_order and simplify for Polynomials. double P_spm_remove_cf_simplify_normal_order_test( const poly_matr & Pm, const char* logfile, const double accu ); /// Function for checking a created trace basis against a read in basis. /// Returns true if the bases are exactly the same. /// Terminates if bases have different lengths, /// and write error messages if bases are different. /// Write result, and report deviations to logfile. bool Trace_basis_test(const int n_q, const int n_g, const int n_loop, const char* basis_file_name, const char* logfile); /// Function for checking a created Tree_level_gluon basis against a read in basis. /// Returns true if the bases are exactly the same. /// Terminates if bases have different lengths, /// and writes error messages if bases are different. /// Write result, and report deviations to logfile. bool Tree_level_gluon_basis_test( const int n_g, const char* basis_file_name, const char* logfile); /// Function for reading in a basis file, /// writing it out again, and numerically testing that /// it is the same to accuracy accu. /// The col_strs in the Col_strs in the Col_amps are /// compared exactly, and the Polynomials multiplying the /// Col_strs are compared numerically. double basis_read_write_test_num( const char* filename, const char* out_filename, const char* logfile, const double accu ); /// Function for numerically comparing (for example ColorMath and ColorFull) scalar product matrices. /// If difference of an element is larger than accu, an error message is written. /// Two values v1 and v2 are considered equal if abs(v1/v2-1) < accu. /// The largest numerical difference of abs(v1/v2-1) is returned. double scalar_product_matrix_test( Col_basis* Cb, const char* spm_file , const char* logfile, const double accu, bool use_mem ); /// Function for algebraically comparing ColorMath and ColorFull spms /// Function for algebraically comparing (for example ColorMath and ColorFull) scalar product matrices. bool scalar_product_matrix_test_alg( Col_basis* Cb, const char* spm_file , const char* logfile, bool use_mem ); /// Function for generating a random real amplitude in the basis Cb, useful for testing. /// The coefficients are random numbers in [-1/2,1/2]. Col_amp random_real_amplitude( const Col_basis* Cb ) const; /// Function for generating a random complex amplitude in the basis Cb, useful for testing. /// The coefficients are random numbers in [-1/2,1/2]. Col_amp random_complex_amplitude( const Col_basis* Cb ) const; /// Function for testing the various scalar product functions /// TODO fix problem with tree_level_gluon_basis double scalar_product_test( Col_basis* Cb, const char* logfile, const double accu ) const; // TODO Numerical conversion in different order - // Function for testing the functions: - // std::pair<int, int> new_vector_numbers( int old_num, int emitter, int n_q, int n_g, int n_loop ) const; - // int Trace_type_basis::new_vector_number( int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; - // std::pair<int,int> find_parton( int parton, int vec_num, int n_q, int n_g, int n_loop ) const; - // in the case of a Trace_basis. + /// Function for testing the functions: + /// std::pair<int, int> new_vector_numbers( int oldouble_num, int emitter, int n_q, int n_g, int n_loop ) const; + /// int Trace_type_basis::new_vector_number( int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; + /// std::pair<int,int> find_parton( int parton, int vecnum_num, int n_q, int n_g, int n_loop ) const; + /// in the case of a Trace_basis. // These functions are tested by testing the direct and indirect versions of new_vector_numbers. bool new_vector_numbers_Trace_basis_test( int n_q, int n_g, const char* logfile ) const; - // Function for testing the functions: - // std::pair<int, int> new_vector_numbers( int old_num, int emitter, int n_q, int n_g, int n_loop ) const; - // int Trace_type_basis::new_vector_number( int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; - // std::pair<int,int> find_parton( int parton, int vec_num, int n_q, int n_g, int n_loop ) const; - // in the case of a Tree_level_gluon_basis - // These functions are tested by testing the direct and indirect versions of new_vector_numbers. + /// Function for testing the functions: + /// std::pair<int, int> new_vector_numbers( int oldouble_num, int emitter, int n_q, int n_g, int n_loop ) const; + /// int Trace_type_basis::new_vector_number( int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; + /// std::pair<int,int> find_parton( int parton, int vecnum_num, int n_q, int n_g, int n_loop ) const; + /// in the case of a Tree_level_gluon_basis + /// These functions are tested by testing the direct and indirect versions of new_vector_numbers. bool new_vector_numbers_Tree_level_gluon_basis_test( int n_g, const char* logfile ) const; - // Function for comparing the results of Col_gamma, against read in results - // from file file_name. - // TODO check comment + /// Function for comparing the results of Col_gamma, against results + /// from file file_name. Checks gluon exchange between the partons p1 and p2. double Col_gamma_test( Col_basis* Cb, int p1, int p2, const char* file_name, const char* logfile, double accu ) const; bool test( const char* logfile ); // Make tests and save to default logfile bool test( ); // TODO make large test, a function performing careful tests }; } /* namespace ColorFull */ #endif /* TEST_FUNCTIONS_H_ */ diff --git a/Core/Trace_type_basis.cc b/Core/Trace_type_basis.cc --- a/Core/Trace_type_basis.cc +++ b/Core/Trace_type_basis.cc @@ -1,882 +1,882 @@ // -*- C++ -*- /* * Trace_type_basis.cc * Contains definition of the class Trace_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjödahl */ #include "Trace_type_basis.h" namespace ColorFull { //poly_vec Trace_type_basis::decompose( const Col_amp & Ca ) const{ //poly_vec Trace_type_basis::decompose( const Col_amp & Ca ) { poly_vec Trace_type_basis::decompose( Col_amp & Ca ) { //std::cout << "Trace_type_basis::decompose: Incoming Col_amp " << Ca << std::endl; Col_amp Ca_copy=Ca; Ca_copy.simplify(); // To contain the decomposed vector poly_vec Decv; Polynomial Zero; Zero=Zero*0; if(cb.size()==0){ std::cerr << "Trace_type_basis::decompose: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; throw -1; } else if(Ca_copy.size()>0 ){ if(Ca_copy.at(0).n_quark() != n_q) { std::cerr << "Trace_type_basis::decompose: The number of quarks in the argument Col_amp, " << Ca << ", does not fit the number of quarks in the basis " << n_q << "."<< std::endl;} if(Ca_copy.at(0).n_gluon() != n_g ) { std::cerr << "Trace_type_basis::decompose: The number of gluons in the argument Col_amp, " << Ca << ", does not fit the number of gluons in the basis " << n_g << "."<< std::endl; } } //std::cout << "Trace_type_basis::decompose: Ca size "<< Ca_copy.size() << " basis size "<< cb.size() << std::endl; // Initially set all components of the vectors to 0 for (uint m2 = 0; m2 < cb.size(); m2++){ Decv.push_back(Zero); } //std::cout << "Trace_type_basis::decompose: initial Decv "<< Decv << "with size "<< Decv.size() << std::endl; // Loop over Cs in Ca and check which basis vector they equal for (uint m1 = 0; m1 < Ca_copy.ca.size(); m1++) { //std::cout << m1 << " testing Ca_copy part " <<Ca.at(m1) << std::endl; for ( uint m2 = 0; m2 < cb.size(); m2++ ) { bool found=false; //std::cout << m1 << " testing aginst " << cb.at(m2) << std::endl; // For a trace type basis it is enough to compare to see if the basis vector // is equal to the Col_str in the Ca. if (Ca_copy.ca.at(m1).cs == cb.at(m2).at(0).cs) { found=true; //std::cout << m2 << " Parts " << Decv.at(m2) << " and " << Ca.ca.at(m1).Poly << std::endl; //if( Ca.ca.at(m1).Poly.empty() ) cout << m2 << "Poly was empty" << endl; Decv.at(m2)=Decv.at(m2)+Ca_copy.ca.at(m1).Poly; //std::cout.flush(); - //if(found) std::cout << m2 << " result " << Decv.at(m2) << " num " << CF->d_num(Decv.at(m2)) << std::endl; + //if(found) std::cout << m2 << " result " << Decv.at(m2) << " num " << CF->double_num(Decv.at(m2)) << std::endl; //std::cout.flush(); } if(found) break; // The tensor is ONE of the tensors in the Basis //else std::cout << "Not yet found " << std::endl; } } //std::cout << "Trace_type_basis::decompose: final Decv "<< Decv << "with size "<< Decv.size() << std::endl; return Decv; } // Function for finding the new vector numbers in the new basis (this trace basis) // after radiating a new gluon from the parton emitter. // The old color structure is Cs, and after emission a linear combination // of new basis vectors is obtained. // For emission from q or qbar there is only one resulting color structure, // and -1 is returned in the place of the absent color structure. // The second vector, where the new parton is inserted before the emitter // comes with a minus sign in the new total amplitude. //std::pair<int, int> Trace_type_basis::new_vector_numbers( const Col_str & Cs, int emitter ) const{ std::pair<int, int> Trace_type_basis::new_vector_numbers( const Col_str & Cs, int emitter ) { // Number of new gluon int new_g=Cs.n_gluon()+2*Cs.n_quark()+1; // First check that basis is not empty if( cb.empty() ){ std::cerr << "Trace_type_basis::new_vector_numbers: The basis has no vectors, " << "consider using create_basis or read_in_basis." << std::endl; throw cb; } // Then check that number of quarks is the same as for the Cs if( n_q != Cs.n_quark() ){ std::cerr << "Trace_type_basis::new_vector_numbers: The number of quarks in the (new) basis, " << n_q << " is not the same " << "as the number of quarks in Cs, " << Cs.n_quark() << ", in Cs."<< std::endl; throw cb; } // Then check that number of gluons is one more than for the Cs if( n_g != Cs.n_gluon()+1 ){ std::cerr << "Trace_type_basis::new_vector_numbers: The number of gluons in the (new) basis, " << n_g << ", is not one plus the number of gluons in Cs << " << Cs.n_gluon()<< std::endl; throw cb; } // To contain the numbers of the new non-zero vectors int plus_comp=-1; int minus_comp=-1; // The vector after emission Col_amp Ca=CF->emit_gluon(Cs, emitter, new_g); //cout << "new_vector_numbers: Ca " << Ca << endl; poly_vec vec=decompose( Ca ); // Loop over vector entries to identify the non-zero ones for ( uint veci= 0; veci < cb.size(); veci++) { - if( CF-> d_num(vec.at(veci))!=0 ){ - if(CF->d_num(vec.at(veci)) > 0){plus_comp=veci;} - if(CF->d_num(vec.at(veci)) < 0){minus_comp=veci;} + if( CF-> double_num(vec.at(veci))!=0 ){ + if(CF->double_num(vec.at(veci)) > 0){plus_comp=veci;} + if(CF->double_num(vec.at(veci)) < 0){minus_comp=veci;} } } std::pair<int, int> res= std::make_pair(plus_comp,minus_comp); return res; } // This function is only for tree-level processes with at most 2 qqbar pairs. // Function for finding the new vector numbers in the basis for n_p+1 partons // after radiating a new parton from the parton emitter. // This function doesn't actually use the cb, but only calculates the // the basis vector number. -// The old vector has number old_num, and there were, before emission +// The old vector has number oldouble_num, and there were, before emission // n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. // For emission from q or qbar there is only one resulting color structure, // and -1 is returned in the place of the absent color structure. // The second vector, where the new parton is inserted before the emitter // comes with a minus sign in the new total amplitude. -std::pair<int, int> Trace_type_basis::new_vector_numbers( int old_num, int emitter, int n_q, int n_g, int n_loop ) const{ +std::pair<int, int> Trace_type_basis::new_vector_numbers( int oldouble_num, int emitter, int n_q, int n_g, int n_loop ) const{ //cout << "new_vector_numbers: emission from " << emitter <<" for n_g=" << n_g << " and n_q=" << n_q<< endl; if( !(n_q==0 or n_q==1 or n_q==2) or n_loop!=0 ){ std::cerr << "Trace_type_basis:new_vector_numbers: Function only intended for special case of 0-2 q qbar pair at tree level. For the general case use the general version." << std::endl; throw -1; } // Find the place of the parton - std::pair<int,int> parton_place=find_parton( emitter, old_num, n_q, n_g, n_loop); + std::pair<int,int> parton_place=find_parton( emitter, oldouble_num, n_q, n_g, n_loop); //cout << "new_vector_numbers: Parton " << emitter << " found at place (" << parton_place.first << " " << parton_place.second << ")" << endl; // If the q or qbar is radiating, there is only one term if(parton_place.second==0 && n_q>=1)// If the q is radiating - return std::make_pair(new_vector_number(old_num, std::make_pair(parton_place.first,parton_place.second+1), n_q, n_g, n_loop),-1); + return std::make_pair(new_vector_number(oldouble_num, std::make_pair(parton_place.first,parton_place.second+1), n_q, n_g, n_loop),-1); else if(parton_place.second==n_g+1 && n_q==1) - return std::make_pair(-1,new_vector_number(old_num, parton_place, n_q, n_g, n_loop)); + return std::make_pair(-1,new_vector_number(oldouble_num, parton_place, n_q, n_g, n_loop)); // If the emitter is a g there are two different terms std::pair<int,int> first_insertion_place, second_insertion_place; if( n_q==0 && parton_place.second!=0) { first_insertion_place=std::make_pair(parton_place.first, parton_place.second + 1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second ); } // Special case that the emitter is standing first else if(n_q==0 && parton_place.second==0){ first_insertion_place=std::make_pair(0, 1); second_insertion_place=std::make_pair(0, n_g ); } else if(n_q==1){ // special cases already excluded first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } else if(n_q==2){ // special case of quark already excluded // If the emitter is an anti-quark if(emitter==2 or emitter==4){ - return std::make_pair(-1,new_vector_number(old_num, parton_place, n_q, n_g, n_loop)); + return std::make_pair(-1,new_vector_number(oldouble_num, parton_place, n_q, n_g, n_loop)); } // Emitter is a gluon, and is thus not standing in the ends first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } - int first_tens = new_vector_number(old_num, first_insertion_place, n_q, n_g, n_loop); - int second_tens = new_vector_number(old_num, second_insertion_place, n_q, n_g, n_loop); + int first_tens = new_vector_number(oldouble_num, first_insertion_place, n_q, n_g, n_loop); + int second_tens = new_vector_number(oldouble_num, second_insertion_place, n_q, n_g, n_loop); //cout << "new_vector_numbers: First tensor was " << first_tens << endl; return std::make_pair( first_tens, second_tens ); } // Function for finding the new vector number in the basis with n_p+1 partons (this basis) // after inserting a new parton with larger parton number at the place place. // This function is only intended for special case of: // 0 qqbar pairs and Tree_level_gluon_basis // or Trace_basis and 1-2 qqbar pairs at tree level. // This function doesn't actually use the cb, but only calculates the // the basis vector number using find_parton. -// The old vector has number old_num, and there were, before emission +// The old vector has number oldouble_num, and there were, before emission // n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. // The function has been explicitly tested for initial states with 2qqbar pairs and up to 5 gluons, // 1qqbar pair and up to 7 gluons, 0 qqbar pairs and up to 8 gluons. -int Trace_type_basis::new_vector_number( int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const{ +int Trace_type_basis::new_vector_number( int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const{ //cout << "new_vector_number: inserting at place (" << place.first <<", " << place.second << ") for n_g=" << n_g << " and n_q=" << n_q<< endl; std::cout.flush(); if( !( n_q==0 or n_q==1 or n_q==2 ) ){ std::cerr << "Trace_type_basis::new_vector_number: Function only intended for special case of 1 quark line or 2 open quark lines." << std::endl; std::cerr.flush(); throw -1; } if( n_q==0 and !tree_level_gluon_basis ){ std::cerr << "Trace_type_basis:new_vector_number: For 0 qqbar-pairs this function is only available for Tree_level_gluon_basis." << std::endl; throw -1; } if( n_q>0 and !trace_basis ){ std::cerr << "Trace_type_basis:new_vector_number: The basis type should be Trace_basis for processes with quarks, not Tree_level_gluon_basis." << std::endl; throw -1; } /* if( n_q==2 && n_g>5 ){ std::cerr << "Trace_type_basis::new_vector_number: WARNING: function only tested for 2 q qbar pairs and at most 5 gluons." << std::endl; } if( n_q==1 && n_g>7 ){ std::cerr << "Trace_type_basis::new_vector_number: WARNING: function only tested for 1 q qbar pair and at most 7 gluons." << std::endl; } if( n_q==0 && (n_g>8 or n_g==1) ){ std::cerr << "Trace_type_basis::new_vector_number: WARNING: function only tested 2-8 gluons when no quarks." << std::endl; } */ if(place.second == 0 && n_q>0 ){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon at place 0, reserved for quark." << std::endl; std::cerr.flush(); throw -1; } else if(place.second==n_g+2 && n_q==1){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon at place n_g+2, reserved for qbar." << std::endl; std::cerr.flush(); throw -1;} else if(n_q==2 && (0 > place.second or place.second > n_g+1+1) ){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon outside range 1...n_g." << std::endl; std::cerr.flush(); throw -1; } else if( 0 > place.second or place.second > n_g+1+n_q){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon outside range 1...n_g ." << std::endl; std::cerr.flush(); throw -1; } // If asked to insert a gluon at place 0, it should be inserted beyond the last place instead if( n_q==0 && place.second==0 ){ place.second=n_g; //cerr << "new_vector_number: Cannot insert gluon at place 0 inserting at last place " << place.second << " instead." << endl; //cerr.flush(); } // In the gluons only case we need to keep track of the next position relative to gluon 2 int place2=0; - int fnext=old_num; // To contain the remaining part of tensor number + int fnext=oldouble_num; // To contain the remaining part of tensor number // If there are 2 quarks, there is an effective scalar place in {1,g1...gm,2}{3,gm+1...4} // obtained by crossing 2}{3 out. This place is given by // (place in the first ql) if it is in the first ql // (number of gluons in first ql) + place in second if it's in the second ql // If n_q=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*2*n_g! options for each breakpoints, // where the factor 2*2 comes from the options for the quarks // we thus have int break_after=0; int fsplit=0; // Contribution from where the ql is split int fq1=0; // Contribution from first quark if (n_q == 2) { // To contain the number of gluon in the first ql // First the number of gluons in 2nd ql // For each gluon in 2nd ql there was // 2 (from n_q)*2 (from n_qbar)*factorial(n_g) (from the gluons) // tensors with lower numbers from other breaks // Note that for all other splitting the factor from the first q is relevant - break_after = old_num /( 2*2*CF->factorial(n_g) ) ; + break_after = oldouble_num /( 2*2*CF->factorial(n_g) ) ; - fnext= old_num % (2*2*CF->factorial(n_g)); + fnext= oldouble_num % (2*2*CF->factorial(n_g)); // The factor from the split - fsplit=old_num-fnext; + fsplit=oldouble_num-fnext; break_after=n_g-break_after; //cout << "new_vector_number: break_after " << break_after << endl; //cout << "new_vector_number: fsplit " << fsplit << endl; //cout << "new_vector_number: fnext after finding break " << fnext << endl; // Check if the first quark is 1 or 3 // The factor from the first quark fq1=(fnext/( 2*CF->factorial(n_g) ))*2*CF->factorial(n_g); // Special case of 2 quark lines of equal length, then there is no option for q1 (it is 1) if( break_after==n_g-break_after ) fq1=0; // 1 is first, no factor from first quark //cout << "new_vector_number: fq1 " << fq1 << endl; fnext= fnext % (2*CF->factorial(n_g)); //cout << "new_vector_number: fnext after counting first quark " << fnext << endl; // First treat the special case when the ql's switch place // This can happen when // 1) The 2nd ql gets longer than the first such as // emission from 6 in {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // 2) The 2nd is one shorter than the first but has the q=1 standing first // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} if( ((break_after==n_g-break_after) && place.first==1) or ((break_after==n_g+1-break_after) && fq1>0 && place.first==1)) { //cout << "WARNING: this is the hard case" << endl; // Locate all partons in old ql std::vector< std::pair<int, std::pair<int,int> > > parton_and_place; for( int p=1; p<=n_g+2*n_q; p++ ){ - std::pair<int,int> p_place=find_parton(p, old_num, n_q, n_g, n_loop); + std::pair<int,int> p_place=find_parton(p, oldouble_num, n_q, n_g, n_loop); make_pair(p,p_place); // note that parton p stands at place p-1 parton_and_place.push_back( make_pair(p,p_place) ) ; } //cout << "new_vector_number: reconstructed all parton places" <<endl; // To contain new tensor number int new_num=0; // First calculate the number from the split // After the split the number of gluons in the qls are // break_after in the 2nd ql // This is true for both special cases of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} new_num=(break_after)*2*2*CF->factorial(n_g+1); //cout << "new_vector_number: new_num after adding split "<< new_num <<endl; // Then calculate the number from the first quark // There is a factor 2 from the anti-quark and a factor (n_g+1)! from the gluons // if the first factor is 3, which it is in swapings of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} if( (break_after==n_g-break_after) && place.first==1 ) new_num=new_num+2*CF->factorial(n_g+1); // In cases of form {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} // the factor from the first quark is 0, and //cout << "new_vector_number: new_num after first quark "<< new_num <<endl; // For the anti-quark qbar=2, check if it stands in first or 2nd ql // If it stands in first ql, there is no contribution to the tensor numbers // otherwise there is a contribution from all orders of gluons in 2nd ql if( parton_and_place.at(2-1).second.first==0) new_num+=CF->factorial(break_after); //cout << "new_vector_number: new_num after first anti-quark "<< new_num <<endl; // The 2nd anti-quark and 2nd quark gives no contribution // Then calculate the contribution to the new tensor number from the gluons // Loop over gluons for( int p=5; p<=n_g+2*n_q; p++ ){ // The NEW scalar place of the gluon int p_place=parton_and_place.at(p-1).second.second; // contribution from pos in ql // If a gluon was standing in the first ql it is afterwards standing in the 2nd // All gluons in the new first (old 2nd ql) are therefore standing in front // they are n_g-break_after +1 (the +1 is added below) if(parton_and_place.at(p-1).second.first==0) p_place+=(n_g-break_after); // If the new gluon was inserted before the parton, then the p_place should be increased with one // This happens when the parton stands in the new 2nd ql= old first ql (always) // or when the gluon is in the new first ql, but after the emitter if( parton_and_place.at(p-1).second.first==0 or place.second <=parton_and_place.at(p-1).second.second ) p_place++;// ?? is this right //cout << "new_vector_number: looping over gluons, gluon "<< p <<" at new place " << p_place <<endl; // See how many smaller stand to the right int smaller_right_of_p=0; // Check all gluons with smaller number than p to see how many stand to the right for( int ps=5; ps<p; ps++ ){ int ps_place=parton_and_place.at(ps-1).second.second; // Contribution from pos in ql if(parton_and_place.at(ps-1).second.first==0) ps_place+=(n_g-break_after); // From first ql if( parton_and_place.at(ps-1).second.first==0 or place.second <=parton_and_place.at(ps-1).second.second ) ps_place++; // From new g // If the smaller parton ps stand to the right of the parton p if( ps_place>p_place ) smaller_right_of_p++; } //cout << "new_vector_number: Found number of smaller to the right "<< smaller_right_of_p <<endl; // Increase the new tensor number for partons to the right if( smaller_right_of_p>0 ){ // If the gluon stands in the first ql (former 2nd), there is also a factor of 2 from the // choice of anti-quarks // All smaller_right_of_p ways of replacing p with a smaller number contributes a factor // CF->factorial(gluons to the right), and there are smaller_right_of_p options if(parton_and_place.at(p-1).second.first==1) new_num+=2*smaller_right_of_p*CF->factorial(n_g+1-p_place); else new_num+=smaller_right_of_p*CF->factorial(n_g+1-p_place); } //cout << "new_vector_number: new_num after adding info from gluon " << p << " at place " << p_place<< " " << new_num <<endl; } // End of loop over gluons // Add contribution from the new gluon. The gluon number is larger than every other gluon number, // so all gluons standing to the right are smaller // In the new first ql the new gluon is at place place.second // and there are in total n_g-break_after+1 gluons, so the total number of gluons to the right is // break_after+(n_g-break_after+1) -place.second int n_right_new=(n_g+1)-place.second; // The overall contribution also has a factor 2 from the qbar choice // The factor 2 is from the qbar, CF->factorial(n_right_new) from ordering gluons new_num+=2*n_right_new*CF->factorial(n_right_new); //cout << "new_vector_number: new_num after adding info from new gluon " << new_num <<endl; return new_num; } // End of hard case } - if ( n_q==0 ) place2=find_parton(2, old_num, n_q, n_g, n_loop).second; + if ( n_q==0 ) place2=find_parton(2, oldouble_num, n_q, n_g, n_loop).second; //cout << "The place of 2 was " << place2 << endl; //cout.flush(); // Initially we had q=1, g1,....gn, qbar=2 // After the insertion at place place we have q=1, g1, ...g(place-1), g_new, g(place), ...gn, qbar=2 // The number of the new vector can be seen as a sum of 3 factors: // f1 = the contribution from q=1, g1, ...g(place-1) // fnew= the contribution from the newly inserted gluon // f2= the contribution from g(place), ...gn, qbar=2 (this contribution is the same as before the insertion) // Of these contributions fnew is easiest to calculate it is simply: int fnew=0; // If one ql if( n_q==1 ) fnew=(n_g - place.second + 1)*CF->factorial(n_g - place.second + 1); // Special case of place after last gluon else if( n_q==0 && place.second == n_g+1 ) fnew=0; // If one closed ql and the parton is to the right of 2 else if( n_q==0 && place.second > place2 && place.second < n_g+1) fnew=(n_g - place.second )*CF->factorial(n_g - place.second ); // If the parton is before 2 else if( n_q==0 && place2 >= place.second ) fnew=(n_g - place.second )*(CF->factorial(n_g - place.second )/2); //Rel order 2 3 fixed // as all indices standing to the right are smaller //cout << "fnew " << fnew << endl; // i.e. the (number of partons with smaller number to the left)(number of partons to the left)! // The factor f1 is trickier to obtain. Its initial value (before emission) is // sum_{i=1}^(place-1) n_sri (n_p-i)! // where n_sri denotes the number of smaller partons standing to the rigt at place i // these factors are not known, but can be calculated. // Once these factors are known the final value of f1 can also be obtained from // sum_{i=1}^(place-1) n_sri (n_p-i+1)! // where the extra 1 in the factorial is present as after the emission there is // one more parton standing to the right. int f1new=0; // To contain the new f1 int f1old=0; // To contain the old f1 int scalar_place=0; // The scalar place if( place.first!=0 ) scalar_place=break_after; scalar_place+=place.second; //cout << "Scalar place " << scalar_place << endl; if( n_q==2 ) { fnew=(n_g + 1- scalar_place )*CF->factorial(n_g + 1- scalar_place ); // If g inserted in first ql, there is an extra factor of 2 from interchanging quarks //if( break_after>=scalar_place-1 ) fnew*=2; if( place.first==0 ) fnew*=2; //cout << "new_vector_number: scalar_place " << scalar_place << endl; //cout << "new_vector_number: fnew " << fnew << endl; } // end of if( n_q==2 ) // The last relevant parton for calculating the contribution to f1 int last_rel=scalar_place-1; // If it's a quark_line and the new gluon is to be inserted after the last gluon // then the last place with a q, is not important //if(scalar_place==n_g+1) last_rel--; // If the g is on the first ql //if(place.second==break_after+1) last_rel--; // if the g is in the second ql //if( place.second+break_after==n_g+1 ) last_rel--;??? //cout << "last_rel " << last_rel << endl; //cout.flush(); for (int i=1; i<=last_rel; i++){ //cout << "i " << i << endl; // number of partons to the right int n_r=n_g-i; if( n_q==0 ) n_r--; // nsri is the number of smaller partons to the right int nsri=0; if (n_q==1 ) { // Note integer division, how many times do we find the factorial nsri=fnext/(CF->factorial(n_r)); // The contribution to the old f1 f1old+=nsri*CF->factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(CF->factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(CF->factorial(n_r+1)); } else if (n_q==2) { int q_part=1; // Keeps track of a possible multiplicative factor from first qbar option if ( i <= break_after) q_part=n_q; //if (place.first==0) q_part=2; // Note integer division, how many times do we find the factorial nsri=fnext/(q_part*CF->factorial(n_r)); // The contribution to the old f1 f1old+=nsri*q_part*CF->factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(q_part*CF->factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(q_part*CF->factorial(n_r+1)); //2*2! //cout << "i " << i << " nsri " << nsri << endl; // Take care of factor from first qbar "when we reach it" // if( i == break_after && place.second!=break_after+1 && break_after=n_g-break_after) { if( i == break_after ) { // Note integer division, how many times do we find the factorial nsri=fnext/(CF->factorial(n_r)); int f1old_before_qbar=f1old; // The contribution to the old f1 f1old+=nsri*CF->factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(CF->factorial(n_r)); // if the gluon is inserted just before the qbar the factor from // the qbar should be taken care of in the f2 instead // (f1old still has to be recalculated as it is used for finding f2 which is unchanged) // just added 11 02 03 //if( !break_after==n_g-break_after ){ // If the g is NOT inserted just before the qbar // i.e. if it is either inserted in 2nd ql or at other place than break_after+1 if ( !(scalar_place == break_after+1) or place.first ==1){ // The contribution to the new f1 from the anti-quark //f1new+=nsri*(factorial(n_r+1)); // There are (n_r+1)! options to have 2 before 4 // and we should only have a contribution if 4 is before 2 f1new+=nsri*(CF->factorial(n_r+1)); } else { // If the gluon is inserted just before the qbar // The factor should be accounted for in fnew instead // If the second qbar was 4, this should be compensated for in fnew if( f1old_before_qbar!=f1old && place.first ==0){ // The factor is the number of ways of ordering the gluons in 2nd ql // The q and qbar are fixed and do not contribute // There is no factor if no gluons to the right ?? // There are n_r gluons in the second ql (also after emission) //if (n_r!=0) Why? even with 0 gluons to the right there is one lower option from qbar fnew=fnew+CF->factorial(n_r); } } } } // end if (n_q==2) // If i is after the place of 2 we have the standard case else if(n_q==0 && i >= place2) { //cout << "i " << i << " after= 2 " << " n_r " << n_r << endl; nsri=fnext/(CF->factorial(n_r)); f1old+=nsri*CF->factorial(n_r); fnext=fnext%(CF->factorial(n_r)); f1new+=nsri*(CF->factorial(n_r+1)); } // If i is before the place of 2 ordering of 2 and 3 irrelevant else if(n_q==0 && i < place2) { //cout << "i " << i << " before 2 " << " n_r " << n_r <<endl; nsri=fnext/(CF->factorial(n_r)/2); f1old+=nsri*(CF->factorial(n_r)/2); fnext=fnext%(CF->factorial(n_r)/2); f1new+=nsri*(CF->factorial(n_r+1)/2); } /* cout << "f1new " << f1new << endl; cout << "f1old " << f1old << endl; cout << "fnext " << fnext << endl; cout << "f1new " << f1new << endl; */ } /* cout << "f1old " << f1old << endl; cout << "f1new " << f1new << endl; cout << "fnew " << fnew << endl; */ // Once f1 is known then f2 is simply, int f2=0; if( n_q==2 ) { // For 2 qqbar pairs we have to take care of factor from split - f2=old_num-f1old-fsplit-fq1; + f2=oldouble_num-f1old-fsplit-fq1; } - else f2=old_num-f1old; + else f2=oldouble_num-f1old; //cout << "f2 " << f2 << endl; // and the contribution from f2 is the same as before (irrespectively of position relative 2) // In the case of 2 quarks, if there was a contribution to the tensor number from the split // this has to be added back - // break_after = old_num / (2*factorial(n_g)); + // break_after = oldouble_num / (2*factorial(n_g)); // The contribution was (n_g-break_after)*(2*factorial(n_g)) // The special case of ql swapping should already have been taken care of if( n_q==2 ) { // Calculating new factor from the split int fsplitnew=0; //fsplitnew=fsplit*(n_g+1); // New factor from split after insertion in first ql if(place.first==0) { // The length of 2nd ql is then still (n_g-break_after) // and for each break there are 2*factorial(n_g+1) options // The lengths of first and 2nd ql are never equal as 1st was at least as long as 2nd // -> factor 2 from option of first q fsplitnew=(n_g-break_after)*2*2*CF->factorial(n_g+1); } // If the insertion was in the second ql (of new length n_g-break_after+1) if(place.first==1) { // The length of the 2nd ql is now (n_g+1-break_after) // all gluon orders matter, and the qbar matters // Also the factor fomr the split, coming from all "lower splits" // has a factor 2 as the q matters for the lower splits fsplitnew=(n_g+1-break_after)*2*2*CF->factorial(n_g+1); // 1*2*2! // Unless the lengths are equal there is also a factor from the q //if( break_after!=n_g-break_after+1 ) fsplitnew*=2; // This is not right one has to loop over splitings some may be eual and some not.... } // New factor from first q in the normal case //cout << "fq1 " << fq1 << endl; int fq1new=fq1*(n_g+1); //cout << "fq1new " << fq1new << endl; // If after emission the 2nd ql has the same length as the first if(place.first==1 && break_after==n_g+1-break_after){ fq1new=fq1new/2; // How can this be right?? There should be no factor from first quark then } f1new=fsplitnew+fq1new+f1new; /* cout << "fsplitnew " << fsplitnew << endl; cout << "fq1new " << fq1new << endl; cout << "f1new " << f1new << endl; cout << "fnew " << fnew << endl; */ } // End of n_q==2 // The total vector number is just the sum of the parts //cout << "total " << f1new+fnew+f2 << endl; return f1new+fnew+f2; } // This function is only intended for special case of: // 0 qqbar pairs and Tree_level_gluon_basis // or Trace_basis and 1-2 qqbar pairs at tree level. // It locates the parton parton in the normal ordered basis, -// given the number of the vector vec_num, and the number of quarks and gluons in the basis. +// given the number of the vector vecnum_num, and the number of quarks and gluons in the basis. // The function has been explicitly tested for Trace_basis with 1 qqbar pair and up to 8 gluons // and 2 qqbar pairs and up to 7 gluons, and for Tree_level_gluon_bases with up to 9 gluons. -std::pair<int,int> Trace_type_basis::find_parton( int parton, int vec_num, int n_q, int n_g, int n_loop ) const{ +std::pair<int,int> Trace_type_basis::find_parton( int parton, int vecnum_num, int n_q, int n_g, int n_loop ) const{ if( !(n_q==1 or n_q==0 or n_q==2) or n_loop >0 ){ std::cerr << "Trace_type_basis:find_parton: Function only intended for special case of 0-2 qqbar pairs, and a tree level bases." << std::endl; throw -1; } if( n_q==0 and !tree_level_gluon_basis ){ std::cerr << "Trace_type_basis:find_parton: For 0 qqbar-pairs this function is only available for Tree_level_gluon_basis." << std::endl; throw -1; } if( n_q>0 and !trace_basis ){ std::cerr << "Trace_type_basis:find_parton: The basis type should be Trace_basis for processes with quarks, not Tree_level_gluon_basis." << std::endl; throw -1; } if( parton==1 && n_q == 1) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==1 && n_q == 0) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==2 && n_q==1 ) return std::make_pair( 0, n_g+1 ); // qbar has last pos // If n_q=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*n_g! options for each breakpoints, // where the factor 2 comes from the 2 options for the first anti-quark // we thus have int break_after=n_g; if (n_q == 2) { // There are n_q!n_q!n_g! assignments for each break - break_after = break_after-(vec_num) / (2*2*CF->factorial(n_g)); - vec_num= vec_num -(n_g-break_after)*(2*2*CF->factorial(n_g)); + break_after = break_after-(vecnum_num) / (2*2*CF->factorial(n_g)); + vecnum_num= vecnum_num -(n_g-break_after)*(2*2*CF->factorial(n_g)); //cout << "find_parton: break_after " << break_after << endl; - //cout << "find_parton: vec_num after break compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num after break compensation " << vecnum_num << endl; // Check if first parton is 1 or 3, there are 2 options for each from the qbar being 2 or 4 bool first3=false; - if ( ( vec_num / ( 2*CF->factorial(n_g)) ) ) first3=true; + if ( ( vecnum_num / ( 2*CF->factorial(n_g)) ) ) first3=true; //cout << "The first is 3? "<< first3; - vec_num= vec_num % (2*CF->factorial(n_g)); + vecnum_num= vecnum_num % (2*CF->factorial(n_g)); // Special cases of asked for quark if(parton == 1){ if(!first3) return std::make_pair(0,0); else return std::make_pair(1,0); } if(parton == 3){ if(first3) return std::make_pair(0,0); else return std::make_pair(1,0); } - //cout << "find_parton: vec_num after first quark compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num after first quark compensation " << vecnum_num << endl; } // The number of partons gluons with lower parton number than the parton // (and possibly standing to the right, hence -1) int lower=parton-2-n_q; //if (n_q==2 ) lower=parton -2*n_q-1; if ( n_q==2 ) lower=parton -2*n_q-1; // quarks don't count // To contain the number of gluons standing to the right of the parton under consideration // and being smaller than 2, noting to the right is smaller than 2, hence 0 // needed for special case of gluons only int lower2=0; // Number of partons to the right, when checking the various positions int n_r=0; // For quark lines(s) all gluons except the one under consideration matter if ( n_q>0 ) n_r=n_g-1; // For a gluon line we have to subtract 1 extra as we start read at place 1 anyway else if ( n_q==0 ) n_r=n_g-2; // Was the gluon 2 found or not, needed for special case of gluons only bool found_2=false; // The resulting quark_line number (can change only in case 2 q qbar pairs) int ql_num=0; // Loop over possible places of the gluon, start with checking place 1 // n_l_r contains the number of partons lower than the parton at the position under consideration int n_l_r=0; // To make compiler not warn int looped_over=0; while(true){ if( n_q==1 ) { - n_l_r=vec_num/CF->factorial(n_r); // The result of integer division - vec_num=vec_num%CF->factorial(n_r); // The rest after integer division + n_l_r=vecnum_num/CF->factorial(n_r); // The result of integer division + vecnum_num=vecnum_num%CF->factorial(n_r); // The rest after integer division } if( n_q==2 ) { // If both qbars to right extra factor 2! if( break_after > looped_over) { //cout << "break_after > looped_over=" << looped_over << endl; - n_l_r=vec_num/(CF->factorial(n_r)*2); // The result of integer division - vec_num=vec_num%(CF->factorial(n_r)*2); // The rest after integer division + n_l_r=vecnum_num/(CF->factorial(n_r)*2); // The result of integer division + vecnum_num=vecnum_num%(CF->factorial(n_r)*2); // The rest after integer division } else{ // Check if it is time to compensate for first qbar and second q if ( break_after==looped_over ){ //cout << "break_after == looped_over=" << looped_over << endl; - //cout << "qbar compensation starting with vector number " << vec_num << endl; + //cout << "qbar compensation starting with vector number " << vecnum_num << endl; //cout.flush(); // If we have already looped over all gluons if(n_r==-1) n_r=0; //cout << "find_parton: n_r " << n_r << endl; //cout.flush(); // Special cases if asked for first qbar = 2 or 4 if( parton==2){ // All gluons which are not looped over are to right - if (vec_num/(CF->factorial(n_r+1))==0) return std::make_pair(0,break_after+1); + if (vecnum_num/(CF->factorial(n_r+1))==0) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_g-break_after+1); } if( parton==4){ - if (vec_num/(CF->factorial(n_r+1))==1) return std::make_pair(0,break_after+1); + if (vecnum_num/(CF->factorial(n_r+1))==1) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_g-break_after+1); } - //cout << "find_parton: vec_num before qbar compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num before qbar compensation " << vecnum_num << endl; //cout << "find_parton: n_r " << n_r << endl; - vec_num=vec_num%(CF->factorial(n_r+1)); // compensated for first qbar + vecnum_num=vecnum_num%(CF->factorial(n_r+1)); // compensated for first qbar - //cout << "find_parton: vec_num after first qbar compensation " << vec_num << endl; + //cout << "find_parton: vecnum_num after first qbar compensation " << vecnum_num << endl; // After looking at first qbar the ql_num should be 1 ql_num=1; } //else cout << "break_after < looped_over=" << looped_over << endl; // Otherwise the situation is as for gluons only - n_l_r=vec_num/(CF->factorial(n_r)); // The result of integer division - vec_num=vec_num%CF->factorial(n_r); // The rest after integer division + n_l_r=vecnum_num/(CF->factorial(n_r)); // The result of integer division + vecnum_num=vecnum_num%CF->factorial(n_r); // The rest after integer division } //cout << "n_l_r " << n_l_r << endl; } else if (n_q==0){ //cout << "found 2? " << found_2 << endl; if( found_2 ){ //cout << "New n_r " << n_r << endl; - n_l_r=vec_num/CF->factorial(n_r); - vec_num=vec_num%CF->factorial(n_r); + n_l_r=vecnum_num/CF->factorial(n_r); + vecnum_num=vecnum_num%CF->factorial(n_r); //cout << "n_l_r " << n_l_r << endl; } else{ // First check if 2 was at the position //cout << "Before finding 2 n_r " << n_r << endl; - //cout << "Before finding 2 vec_num " << vec_num << endl; - n_l_r=vec_num/(CF->factorial(n_r)); // If 2 was at the place, then all orders matter + //cout << "Before finding 2 vecnum_num " << vecnum_num << endl; + n_l_r=vecnum_num/(CF->factorial(n_r)); // If 2 was at the place, then all orders matter //cout << "Before finding 2 n_l_r " << n_l_r << endl; // If 2 was found if( n_l_r==lower2 ) { found_2=true; - vec_num=vec_num%(CF->factorial(n_r)); } + vecnum_num=vecnum_num%(CF->factorial(n_r)); } else{ // Recalculate knowing that we did not find 2 - n_l_r=vec_num/(CF->factorial(n_r)/2); // There is only one relative order of gluon 2 and 3 - vec_num=vec_num%(CF->factorial(n_r)/2); // The rest after integer division + n_l_r=vecnum_num/(CF->factorial(n_r)/2); // There is only one relative order of gluon 2 and 3 + vecnum_num=vecnum_num%(CF->factorial(n_r)/2); // The rest after integer division } } } if( n_l_r==lower ) break; // Number smaller to right = number smaller than parton to right if( n_l_r < lower ) lower--; n_r--; looped_over++; // needed to compare with break in 2q case //cout << "n_l_r " << n_l_r << endl; } //if( n_q==1) return make_pair( 0, n_g-n_r ); //else if( n_q==0 ) return make_pair( 0, n_g-n_r-1 ); // If we have 2 quark lines we have to first figure out the quark line number //int ql_num=n_g-break_after; // and then the number in the quark_line int pos_num=n_g-n_r; // n_q==1 case if( n_q==0) pos_num=n_g-n_r-1; else if( n_q==2 && ql_num==0) pos_num=n_g-n_r; else if(n_q==2 && ql_num==1) pos_num=n_g-break_after-n_r; return std::make_pair( ql_num, pos_num ); } } // end namespace ColorFull diff --git a/Core/Trace_type_basis.h b/Core/Trace_type_basis.h --- a/Core/Trace_type_basis.h +++ b/Core/Trace_type_basis.h @@ -1,101 +1,101 @@ // -*- C++ -*- /* * Trace_type_basis.h * Contains the declarations of the class Trace_type_basis, related types and operators * Created on: Aug 9, 2012 * Author: Malin Sjödahl */ #ifndef COLORFULL_Trace_type_basis_h #define COLORFULL_Trace_type_basis_h #include "Col_basis.h" namespace ColorFull { /// A basis is a set of color amplitudes, Col_amps. class Trace_type_basis:public Col_basis{ public: /// Default constructor Trace_type_basis():Col_basis(){ CF= new Col_functions(); n_q=0; n_g=0; max_ql=n_q+n_g/2; // Integer division to get maximal number of Quark_lines trace_basis = false; tree_level_gluon_basis = false; orthogonal_basis = false; }; int max_ql; /// A function for decomposing the color amplitude ca in the basis, /// returning the result as a Polynomial. // poly_vec decompose( const Col_amp & Ca ) const; // poly_vec decompose( const Col_amp & Ca ); poly_vec decompose( Col_amp & Ca ); /// Function for finding the new vector numbers in the new basis (this trace basis) /// after radiating a new gluon from the parton emitter. /// The old color structure is Cs, and after emission a linear combination /// of new basis vectors is obtained. /// For emission from a quark or anti-quark there is only one resulting color structure, /// and -1 is returned in the place of the absent color structure. /// The second vector, where the new parton is inserted before the emitter /// comes with a minus sign in the new total amplitude. //std::pair<int, int> new_vector_numbers( const Col_str & Cs, int emitter ) const; std::pair<int, int> new_vector_numbers( const Col_str & Cs, int emitter ) ; /// This function is only for tree-level processes with at most 2 qqbar pairs. /// Function for finding the new vector numbers in the basis for n_p+1 partons /// after radiating a new parton from the parton emitter. /// This function doesn't actually use the cb, but only calculates the /// the basis vector number. - /// The old vector has number old_num, and there were, before emission + /// The old vector has number oldouble_num, and there were, before emission /// n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. /// For emission from q or qbar there is only one resulting color structure, /// and -1 is returned in the place of the absent color structure. /// The second vector, where the new parton is inserted before the emitter /// comes with a minus sign in the new total amplitude. /// The function has been explicitly tested against its sister function /// for initial states with 2 qqbar pairs and up to 5 gluons, /// 1qqbar pair and up to 7 gluons, 0 qqbar pairs and up to 8 gluons. - std::pair<int, int> new_vector_numbers( int old_num, int emitter, int n_q, int n_g, int n_loop ) const; + std::pair<int, int> new_vector_numbers( int oldouble_num, int emitter, int n_q, int n_g, int n_loop ) const; /// Function for finding the new vector number in the basis with n_p+1 partons (this basis) /// after inserting a new parton with larger parton number at the place place. /// This function is only intended for special case of: /// 0 qqbar pairs and Tree_level_gluon_basis /// or Trace_basis and 1-2 qqbar pairs at tree level. /// This function doesn't actually use the cb, but only calculates the /// the basis vector number using find_parton. - /// The old vector has number old_num, and there were, before emission + /// The old vector has number oldouble_num, and there were, before emission /// n_q quarks (+ n_q anti-quarks) and n_g gluons, i.e. n_p=2 n_q+ n_g. /// The function has been explicitly tested for initial states with 2qqbar pairs and up to 5 gluons, /// 1qqbar pair and up to 7 gluons, 0 qqbar pairs and up to 8 gluons. - int new_vector_number(int old_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; + int new_vector_number(int oldouble_num, std::pair<int,int> place, int n_q, int n_g, int n_loop) const; /// This function is only intended for special case of: /// 0 qqbar pairs and Tree_level_gluon_basis /// or Trace_basis and 1-2 qqbar pairs at tree level. /// It locates the parton parton in the normal ordered basis, - /// given the number of the vector vec_num, and the number of quarks and gluons in the basis. + /// given the number of the vector vecnum_num, and the number of quarks and gluons in the basis. /// The function has been explicitly tested for Trace_basis with 1 qqbar pair and up to 8 gluons /// and 2 qqbar pairs and up to 7 gluons, and for Tree_level_gluon_bases with up to 9 gluons. - std::pair<int,int> find_parton( int parton, int vec_num, int n_q, int n_g, int n_loop ) const; + std::pair<int,int> find_parton( int parton, int vecnum_num, int n_q, int n_g, int n_loop ) const; protected: }; } #endif /* COLBASIS_H_ */ diff --git a/Core/Tree_level_gluon_basis.cc b/Core/Tree_level_gluon_basis.cc --- a/Core/Tree_level_gluon_basis.cc +++ b/Core/Tree_level_gluon_basis.cc @@ -1,283 +1,283 @@ // -*- C++ -*- /* * Tree_level_gluon_basis.h * Contains definition of the class Tree_level_gluon_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #include "Tree_level_gluon_basis.h" namespace ColorFull { void Tree_level_gluon_basis::create_basis(int n_gluon) { // Setting basis variable n_g=n_gluon; // Remove a potentially already calculated basis cb.clear(); // The Col_amp containing the basis Col_amp Ca_basis; // Create the basis using the old function for a maximal number of loops Ca_basis = create_trace_basis(n_g); // Sort the resulting Col_amp into the Col_basis cb for ( uint i = 0; i < Ca_basis.ca.size(); i++ ) { // A Col_amp to contain a basis vector Col_amp Ca_vec; Ca_vec.ca.push_back(Ca_basis.ca.at(i)); cb.push_back(Ca_vec); }; } void Tree_level_gluon_basis::read_in_basis(const char* filename){ // First read in basis as normally // Read in file std::ifstream fin(filename); // Copy info from file to string std::string str((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()); Col_basis_of_str( str ); // Then check that it's really a tree level gluon basis // Check that length of each Col_amp is 2 for( uint bv=0; bv < cb.size(); bv++ ){ if(! cb.at(bv).size()==2 ){ std::cerr << "The basis read in from file " << filename << " has basis vectors with length > 2, and is thus not a tree level gluon basis." << std::endl; throw cb; } // Check that the first term is the conjugate of the second modulo sign // Check col_str exactly Col_str Cs_conj=CF-> conjugate( cb.at(bv).at(1) ); Cs_conj.simplify(); if( cb.at(bv).at(0).cs != Cs_conj.cs ){ std::cerr << "The basis read in from file " << filename << " has basis vector " << bv << " with non self-conjugate color structure " << cb.at(bv) <<". The col_str " << cb.at(bv).at(0).cs << " is not the same as " << Cs_conj.cs << std::endl; throw cb; } // Check Polynomial numerically - if( std::abs( CF-> c_num(cb.at(bv).at(0).Poly - (pow(-1.0, n_g))* Cs_conj.Poly) ) > accuracy ){ + if( std::abs( CF-> cnum_num(cb.at(bv).at(0).Poly - (pow(-1.0, n_g))* Cs_conj.Poly) ) > accuracy ){ std::cerr << "The basis read in from file " << filename << " has basis vector " << bv << " which is not real due to multiplying Polynomial." << std::endl; throw cb; } // Then, remove implicit part cb.at(bv).erase(1); } } // Function for writing out the basis to default name. void Tree_level_gluon_basis::write_out_basis( ) const{ write_out_basis( basis_file_name() ); } // Function for writing out the basis to default name. void Tree_level_gluon_basis::read_in_basis( ) { read_in_basis( basis_file_name() ); } // Function for writing out the basis. void Tree_level_gluon_basis::write_out_basis( const char* filename ) const{ if( (cb.size()==0 ) ) { std::cout << "Tree_level_gluon_basis::write_out_basis(string): There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); return ; } std::ofstream outfile(filename); int sign=pow(-1,cb.at(0).n_gluon()); for (uint m = 0; m < cb.size(); m++) { outfile << m << " "<< cb.at(m); if(sign==1 ) outfile << " + "; else outfile << " - "; Col_amp Ca_conj=CF->conjugate(cb.at(m)); Ca_conj.normal_order(); outfile << Ca_conj << std::endl; } outfile.flush(); } // Function for writing out the basis using cout. void Tree_level_gluon_basis::write_out_basis_to_cout() const{ if( (cb.size()==0 ) ) { std::cout << "Col_basis::write_out_basis(): There are no basis vectors in this basis, consider using create_basis." << std::endl; std::cout.flush(); return ; } int sign=pow(-1,cb.at(0).n_gluon()); for (uint m = 0; m < cb.size(); m++) { std::cout << m << " "<< cb.at(m); if(sign==1 ) std::cout << " + "; else std::cout << " - "; Col_amp Ca_conj=CF->conjugate(cb.at(m)); Ca_conj.normal_order(); std::cout << Ca_conj << std::endl; //<< conjugate(cb.at(m)) << std::endl; } } // Calculate element ij in scalar product matrix Polynomial Tree_level_gluon_basis::ijEntry(const int i, const int j) const{ // Loop over basis vectors in Basis Polynomial ijEntry; uint Ng=cb.at(i).n_gluon(); // The sign of the interference, (-1)^Ng int sign=(Ng % 2 ? -1:1); //std::cout << "Tree_level_gluon_basis::ijEntry: The sign is" // << sign << std::endl; ijEntry=2*CF->scalar_product( cb.at(i), cb.at(j) ) +sign*2*CF->scalar_product( cb.at(j), CF->conjugate( cb.at(i) ) ); ijEntry.simplify(); return ijEntry; } Col_amp Tree_level_gluon_basis::create_trace_basis(int n_g) { //cout << "Tree_level_gluon_basis:: create_trace_basis: Called with n_g=" << n_g << " and n_q=" << n_q << endl; // To contain the resulting basis Col_amp Basis; // There has to be at least two gluons if (n_g <= 1) { std::cerr << "Tree_level_gluon_basis::create_trace_basis: For 0 quarks there is no basis with only " << n_g << " gluons" << std::endl; throw -1; } // If 2 or more gluons, build from the 2-gluon basis else if (n_g >= 2) { Col_str Cs_OnlyState("[(1,2)]"); Col_amp Ca_tmp; Ca_tmp.ca.push_back(Cs_OnlyState); Basis = Ca_tmp; // For 2 gluons, the work is done if (n_g == 2) return Basis; //cout << Basis; } // Then, add the gluons one at the time // As there are only gluons the generation should start from gluon 3, // otherwise from 2*n_q+1; for (int g_new = 3; g_new <= n_g; g_new++) { // If only gluons start from the 2-gluon state, so add gluon 3 //cout << "create_trace_basis: gluon to be added "<< g_new<< endl; Basis = add_one_gluon(Basis, n_g, g_new); //add_one_gluon( Col_amp Old_bas, int n_g, int n_q, int g_new) } // Normal order the Col_str's Basis.normal_order(); return Basis; } Col_amp Tree_level_gluon_basis::add_one_gluon(const Col_str & Cs, int g_new) { //cout << " Tree_level_gluon_basis::add_one_gluon( Col_str, int): Entering with arg" << Old_tens // << endl; // For storing the new basis Col_amp New_tensors; // Add the new gluon in all possible ways to the old Color structure // Loop over the Quark_lines for (uint ql = 0; ql < Cs.cs.size(); ql++) { // The old Quark_line, before insertion of the new gluon index Quark_line Old_Ql = Cs.cs.at(ql); Col_str New_tensor = Cs; // Special case of insertion of a gluon in a 2-ring in a gluons only basis // in this case only "half" the basis states for rings with >= 3 gluons are // generated. The rest are obtained by taking the indices in anti-cyclic order // i.e. by complex conjugating if ((Old_Ql.ql.size() == 2 && Cs.gluons_only())) { // Insert the new gluon after the existing gluons Quark_line New_Ql = Old_Ql; New_Ql.ql.push_back(g_new); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } else { // ordinary case // Loop over (potential) insertion places in the Quark_lines, starting from the end for (int j = Old_Ql.ql.size(); j > 0; j--) { //cout << "add_one_gluon( Col_str, int): ql " << ql << " j " << j // << endl; // Special treatment of last place, insert here only if the ring is open // (the gluon index cannot take the place of the a quark index) if (Old_Ql.open && j == static_cast<int>(Old_Ql.ql.size())) j--; Quark_line New_Ql = Old_Ql; quark_line::iterator it = New_Ql.ql.begin() + j; New_Ql.ql.insert(it, g_new); //// Inserting element at place given by iterator itj //quark_line::iterator itj = cs.at(i).ql.begin() + j; //cs.at(i).ql.insert(itj, part_num); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } } } //cout << "add_one_gluon( Col_str, int): to return " << New_tensors << endl; return New_tensors; } ; // Compute the basis states coming from the whole Col_amp if one gluon is added // If n_loop==0, only tree level states are constructed. Col_amp Tree_level_gluon_basis::add_one_gluon(const Col_amp & Old_basis, int n_g, int g_new) { //cout << "Tree_level_gluon_basis::add_one_gluon( Col_amp, int): Entering with arg " << Old_bas // << endl; // For storing the new basis Col_amp New_bas; // Add the new gluon to each of the previous color tensors for (uint t = 0; t < Old_basis.ca.size(); t++) { New_bas = New_bas + add_one_gluon(Old_basis.ca.at(t), g_new); //cout << "add_one_gluon( Col_amp, int): current state " << New_bas << endl; } return New_bas; } }