KMatrix rescaleRows(const KMatrix& m1, const double vMin, const double vMax) { assert(vMin < vMax); const unsigned int nr = m1.numR(); const unsigned int nc = m1.numC(); KMatrix m2 = KMatrix(nr, nc); for (unsigned int i = 0; i < nr; i++) { double rowMin = m1(i, 0); double rowMax = m1(i, 0); for (unsigned int j = 0; j < nc; j++) { const double mij = m1(i, j); if (mij < rowMin) { rowMin = mij; } if (mij > rowMax) { rowMax = mij; } } const double rowRange = rowMax - rowMin; assert(0 < rowRange); for (unsigned int j = 0; j < nc; j++) { const double mij = m1(i, j); const double nij = (mij - rowMin) / rowRange; // normalize into [0, 1] const double rij = vMin + (vMax - vMin)*nij; // rescale into [vMin, vMax] m2(i, j) = rij; } } return m2; }
KMatrix Model::bigRfromProb(const KMatrix & p, BigRRange rr) { double pMin = 1.0; double pMax = 0.0; for (double pi : p) { assert(0.0 <= pi); pMin = (pi < pMin) ? pi : pMin; pMax = (pi > pMax) ? pi : pMax; } const double pTol = 1E-8; assert(fabs(1 - KBase::sum(p)) < pTol); function<double(unsigned int, unsigned int)> rfn = nullptr; switch (rr) { case BigRRange::Min: rfn = [pMin, pMax, p](unsigned int i, unsigned int j) { return (p(i, j) - pMin) / (pMax - pMin); }; break; case BigRRange::Mid: rfn = [pMin, pMax, p](unsigned int i, unsigned int j) { return (3 * p(i, j) - (pMax + 2 * pMin)) / (2 * (pMax - pMin)); }; break; case BigRRange::Max: rfn = [pMin, pMax, p](unsigned int i, unsigned int j) { return (2 * p(i, j) - (pMax + pMin)) / (pMax - pMin); }; break; } auto rMat = KMatrix::map(rfn, p.numR(), p.numC()); return rMat; }
// -------------------------------------------------------------------------------------------------------- void KPerspectiveProjection::rotate ( const GLfloat x, const GLfloat y, const GLfloat z ) { KVector savePos = getLookAtPosition(); translate(-getPosition()); KVector up = getYVector(); KVector look = getZVector(); KMatrix rotxz; rotxz.rotate (x, 0.0, z); KMatrix roty; roty.rotate (0.0, y, 0.0); KVector yunit(0.0, 1.0, 0.0), zunit (0.0, 0.0, 1.0); KVector lookperp = look.perpendicular (yunit); // y-axis rotation if (lookperp.length() > 0) { look = roty * lookperp + look.parallel(yunit); up = roty * up.perpendicular(yunit) + up.parallel(yunit); } // x & z-axis rotation KMatrix transmat(up.cross(look), up, look); KVector uprotxz = rotxz * yunit; KVector lookrotxz = rotxz * zunit; up = transmat * uprotxz; look = transmat * lookrotxz; *((KMatrix*)this) = KMatrix(up.cross(look), up, look); setPosition( savePos + eye_distance * getZVector()); }
void RP2Model::setRP2(const KMatrix & pm0) { const unsigned int nr = pm0.numR(); const unsigned int nc = pm0.numC(); if (0 < numAct) { assert(nr == numAct); } else { numAct = nr; } if (0 < numOptions()) { assert(nc == numOptions()); } else { theta.resize(nc); // was size zero for (unsigned int i = 0; i < nc; i++) { theta[i] = i; } } assert(minNumActor <= numAct); assert(numAct <= maxNumActor); assert(minNumOptions <= numOptions()); for (auto u : pm0) { assert(0.0 <= u); assert(u <= 1.0); } // if all OK, set it polUtilMat = pm0; return; }
void MVS::DecomposeProjectionMatrix(const PMatrix& P, RMatrix& R, CMatrix& C) { #ifndef _RELEASE KMatrix K; DecomposeProjectionMatrix(P, K, R, C); ASSERT(K.IsEqual(Matrix3x3::IDENTITY)); #endif // extract camera center as the right null vector of P const Vec4 hC(P.RightNullVector()); C = (const CMatrix&)hC * INVERT(hC[3]); // get rotation const cv::Mat mP(3,4,cv::DataType<REAL>::type,(void*)P.val); mP(cv::Rect(0,0, 3,3)).copyTo(R); ASSERT(R.IsValid()); } // DecomposeProjectionMatrix
// Given square matrix of Prob[i>j] returns a column vector for Prob[i]. // Uses Markov process, not 1-step conditional probability. // Challenges have uniform probability 1/N KMatrix Model::markovUniformPCE(const KMatrix & pv) { const double pTol = 1E-6; unsigned int numOpt = pv.numR(); auto p = KMatrix(numOpt, 1, 1.0) / numOpt; // all 1/n auto q = p; unsigned int iMax = 1000; // 10-30 is typical unsigned int iter = 0; double change = 1.0; while (pTol < change) { change = 0; for (unsigned int i = 0; i < numOpt; i++) { double pi = 0.0; for (unsigned int j = 0; j < numOpt; j++) { pi = pi + pv(i, j)*(p(i, 0) + p(j, 0)); } assert(0 <= pi); // double-check q(i, 0) = pi / numOpt; double c = fabs(q(i, 0) - p(i, 0)); change = (c > change) ? c : change; } // Newton method improves convergence. p = (p + q) / 2.0; iter++; assert(fabs(sum(p) - 1.0) < pTol); // double-check } assert(iter < iMax); // no way to recover return p; }
tuple <KMatrix, VUI> RPState::pDist(int persp) const { /// Calculate the probability distribution over states from this perspective // TODO: convert this to a single, commonly used setup function const unsigned int numA = model->numAct; const unsigned int numP = numA; // for this demo, the number of positions is exactly the number of actors // get unique indices and their probability assert (0 < uIndices.size()); // should have been set with setUENdx(); //auto uNdx2 = uniqueNdx(); // get the indices to unique positions const unsigned int numU = uIndices.size(); assert(numU <= numP); // might have dropped some duplicates cout << "Number of aUtils: " << aUtil.size() << endl << flush; const KMatrix u = aUtil[0]; // all have same beliefs in this demo auto uufn = [u, this](unsigned int i, unsigned int j1) { return u(i, uIndices[j1]); }; auto uMat = KMatrix::map(uufn, numA, numU); auto vpm = VPModel::Linear; assert(uMat.numR() == numA); // must include all actors assert(uMat.numC() == numU); // vote_k ( i : j ) auto vkij = [this, uMat](unsigned int k, unsigned int i, unsigned int j) { auto ak = (RPActor*)(model->actrs[k]); auto v_kij = Model::vote(ak->vr, ak->sCap, uMat(k, i), uMat(k, j)); return v_kij; }; // the following uses exactly the values in the given euMat, // which may or may not be square const KMatrix c = Model::coalitions(vkij, uMat.numR(), uMat.numC()); const KMatrix pv = Model::vProb(vpm, c); // square const KMatrix p = Model::probCE(PCEModel::ConditionalPCM, pv); // column const KMatrix eu = uMat*p; // column assert(numA == eu.numR()); assert(1 == eu.numC()); return tuple <KMatrix, VUI>(p, uIndices); }
// returns a square matrix of prob(OptI > OptJ) // these are assumed to be unique options. // w is a [1,actor] row-vector of actor strengths, u is [act,option] utilities. KMatrix Model::vProb(VotingRule vr, VPModel vpm, const KMatrix & w, const KMatrix & u) { // u_ij is utility to actor i of the position advocated by actor j unsigned int numAct = u.numR(); unsigned int numOpt = u.numC(); // w_j is row-vector of actor weights, for simple voting assert(numAct == w.numC()); // require 1-to-1 matching of actors and strengths assert(1 == w.numR()); // weights must be a row-vector auto vfn = [vr, &w, &u](unsigned int k, unsigned int i, unsigned int j) { double vkij = vote(vr, w(0, k), u(k, i), u(k, j)); return vkij; }; auto c = coalitions(vfn, numAct, numOpt); // c(i,j) = strength of coaltion for i against j KMatrix p = vProb(vpm, c); // p(i,j) = prob Ai defeats Aj return p; }
// note that while the C_ij can be any arbitrary positive matrix // with C_kk = 0, the p_ij matrix has the symmetry pij + pji = 1 // (and hence pkk = 1/2). KMatrix Model::vProb(VPModel vpm, const KMatrix & c) { unsigned int numOpt = c.numR(); assert(numOpt == c.numC()); auto p = KMatrix(numOpt, numOpt); for (unsigned int i = 0; i < numOpt; i++) { for (unsigned int j = 0; j < i; j++) { double cij = c(i, j); assert(0 <= cij); double cji = c(j, i); assert(0 <= cji); assert((0 < cij) || (0 < cji)); auto ppr = vProb(vpm, cij, cji); p(i, j) = get<0>(ppr); // set the lower left probability: if Linear, cij / (cij + cji) p(j, i) = get<1>(ppr); // set the upper right probability: if Linear, cji / (cij + cji) } p(i, i) = 0.5; // set the diagonal probability } return p; }
// calculate the [option,1] column vector of option-probabilities. // w is a [1,actor] row-vector of actor strengths, u is [act,option] utilities. // This assumes scalar capabilities of actors (w), so that the voting strength // is a direct function of difference in utilities.Therefore, we can use // Model::vProb(VotingRule vr, const KMatrix & w, const KMatrix & u) KMatrix Model::scalarPCE(unsigned int numAct, unsigned int numOpt, const KMatrix & w, const KMatrix & u, VotingRule vr, VPModel vpm, PCEModel pcem, ReportingLevel rl) { // auto pv = Model::vProb(vr, vpm, w, u); // auto p = Model::probCE(pcem, pv); auto vfn = [vr, &w, &u](unsigned int k, unsigned int i, unsigned int j) { double vkij = vote(vr, w(0, k), u(k, i), u(k, j)); return vkij; }; const auto c = coalitions(vfn, numAct, numOpt); // c(i,j) = strength of coaltion for i against j const auto pv2 = Model::probCE2(pcem, vpm, c); const auto p = get<0>(pv2); //column const auto pv = get<1>(pv2); // square if (ReportingLevel::Low < rl) { printf("Num actors: %i \n", numAct); printf("Num options: %i \n", numOpt); if ((numAct <= 20) && (numOpt <= 20)) { cout << "Actor strengths: " << endl; w.mPrintf(" %6.2f "); cout << endl << flush; cout << "Voting rule: " << vr << endl; // printf(" aka %s \n", KBase::vrName(vr).c_str()); cout << flush; cout << "Utility to actors of options: " << endl; u.mPrintf(" %+8.3f "); cout << endl << flush; cout << "Coalition strengths of (i:j): " << endl; c.mPrintf(" %8.3f "); cout << endl; cout << "Probability Opt_i > Opt_j" << endl; pv.mPrintf(" %.4f "); cout << "Probability Opt_i" << endl; p.mPrintf(" %.4f "); } cout << "Found stable PCE distribution" << endl << flush; } return p; }
vector<KMatrix> VHCSearch::vn1(const KMatrix & m0, double s) { unsigned int n = m0.numR(); auto nghbrs = vector<KMatrix>(); double pms[] = { -1, +1 }; for (unsigned int i = 0; i < n; i++) { for (double si : pms) { KMatrix m1 = m0; m1(i, 0) = m0(i, 0) + (si*s); nghbrs.push_back(m1); } } return nghbrs; }
void RP2Model::setWeights(const KMatrix & w0) { const unsigned int nr = w0.numR(); const unsigned int nc = w0.numC(); assert(1 == nr); if (0 < numAct) { assert(nc == numAct); } else { numAct = nc; } for (auto w : w0) { assert(0.0 <= w); } assert(minNumActor <= numAct); assert(numAct <= maxNumActor); // if it is OK, set it wghtVect = w0; return; }
// Given square matrix of Prob[i>j] returns a column vector for Prob[i]. // Uses 1-step conditional probabilities, not Markov process KMatrix Model::condPCE(const KMatrix & pv) { unsigned int numOpt = pv.numR(); auto p = KMatrix(numOpt, 1); for (unsigned int i = 0; i < numOpt; i++) { double pi = 1.0; for (unsigned int j = 0; j < numOpt; j++) { pi = pi * pv(i, j); } // double-check assert(0 <= pi); assert(pi <= 1); p(i, 0) = pi; // probability that i beats all alternatives } double probOne = sum(p); // probability that one option, any option, beats all alternatives p = (p / probOne); // conditional probability that i is that one. return p; }
tuple<KMatrix, KMatrix> Model::probCE2(PCEModel pcm, VPModel vpm, const KMatrix & cltnStrngth) { const double pTol = 1E-8; unsigned int numOpt = cltnStrngth.numR(); auto p = KMatrix(numOpt, 1); const auto victProb = Model::vProb(vpm, cltnStrngth); // prob of victory, square switch (pcm) { case PCEModel::ConditionalPCM: p = condPCE(victProb); break; case PCEModel::MarkovIPCM: p = markovIncentivePCE(cltnStrngth, vpm); break; case PCEModel::MarkovUPCM: p = markovUniformPCE(victProb); break; default: throw KException("Model::probCE unrecognized PCEModel"); break; } assert(numOpt == p.numR()); assert(1 == p.numC()); assert(fabs(sum(p) - 1.0) < pTol); return tuple<KMatrix, KMatrix>(p, victProb); }
Parallel::Parallel(std::vector <MolState>& molStates, std::vector<int>& nm_list, double fcf_threshold, double temperature, int max_n_initial, int max_n_target, bool if_comb_bands, bool if_use_target_nm, bool if_print_fsfs, bool if_web_version, const char* nmoverlapFName, double energy_threshold_initial, double energy_threshold_target) { int N = nm_list.size(); double intens_threshold=fcf_threshold*fcf_threshold; //initial state index int iniN=0; // full space size int n_norm_modes = molStates[iniN].NNormModes(); // std::vector <int> state_ini, state_targ, state_ini_subspace, state_targ_subspace; for (int i=0; i<n_norm_modes; i++) { state_ini.push_back(-1); state_targ.push_back(-1); } //"excite subspace" size for (int i=0; i<nm_list.size(); i++) { state_ini_subspace.push_back(-1); state_targ_subspace.push_back(-1); } // stores set of the inital and target vibrational states (for each electronic state) -- after energy threshold applied std::vector < std::vector <int> > selected_states_ini, selected_states_targ; // matrix with FC factors (use the same for every target state) KMatrix FCFs_tmp(max_n_initial+1,max_n_target+1); std::vector <KMatrix> FCFs; // matrix with intensities of FC transitions = FCFs*population_of_initial_vibrational_levels(Temperature distrib.) KMatrix I_tmp (max_n_initial+1,max_n_target+1); std::vector <KMatrix> I; // energy position of each transition for a given normal mode; 1D; ofset by IP; when add for N dimensiond, substract IP from each energy; KMatrix E_position_tmp (max_n_initial+1,max_n_target+1); std::vector <KMatrix> E_position; // reduced mass for FCF calcualtions -- mass weighted coordinates double reducedMass=1.0; // ========================================================================================== // Calculate transformation matrix "cartesian->normal mode" coordinates (for each state): std::vector <KMatrix> Cart2Normal_Rs; for (int state=0; state<molStates.size(); state++) { KMatrix NormModes( CARTDIM*(molStates[state].NAtoms()), molStates[state].NNormModes() ); // NOT mass weighted normal modes i.e. (L~)=T^(0.5)*L NormModes.Set(0.0); //Get L (normal modes in cartesian coordinates mass unweighted (in Angstroms) ): for (int j=0; j < molStates[state].NAtoms(); j++) for (int i = 0; i < molStates[state].NNormModes(); i++) for (int k=0; k < CARTDIM; k++) NormModes.Elem2(j*CARTDIM+k, i) = molStates[state].getNormMode(i).getDisplacement()[j*CARTDIM+k]; //Make squrt(T)-matrix (diagonal matrix with sqrt(atomic masses) in cartesian coordinates): KMatrix SqrtT( CARTDIM*(molStates[state].NAtoms()), CARTDIM*(molStates[state].NAtoms()), true); SqrtT.Set(0.0); for(int i=0; i<molStates[state].NAtoms(); i++) SqrtT.Elem2(i*CARTDIM,i*CARTDIM) = SqrtT.Elem2(i*CARTDIM+1,i*CARTDIM+1)= SqrtT.Elem2(i*CARTDIM+2,i*CARTDIM+2)=sqrt(molStates[state].getAtom(i).Mass()); // Cart->NormalModes transformation matrix R=L^T*sqrt(T); // q' = q+d = q+R*(x-x') (for the parallel normal mode approximation); // units of d are Angstr*sqrt(amu) NormModes.Transpose(); NormModes*=SqrtT; Cart2Normal_Rs.push_back(NormModes); } // Initial geometry in cartesian coordinates: KMatrix InitialCartCoord( CARTDIM*(molStates[iniN].NAtoms()), 1); for (int i=0; i<molStates[iniN].NAtoms(); i++) for (int k=0; k<CARTDIM; k++) InitialCartCoord[i*CARTDIM+k]=molStates[iniN].getAtom(i).Coord(k); InitialCartCoord.Scale(ANGSTROM2AU); // initialize spectrlPoint: create vector of VibrQuantNumbers for the initital and target state // (keep only non-zero qunta, i.e. from the "excite subspace") // Also add subspace mask (the rest of nmodes do not have excitations, and would not be printers in the spectrum) SpectralPoint tmpPoint; for (int nm=0; nm<nm_list.size(); nm++) { tmpPoint.getVibrState1().addVibrQuanta(0, nm_list[nm]); tmpPoint.getVibrState2().addVibrQuanta(0, nm_list[nm]); } // For each target state, for each normal mode: calculate shoft, FCFs, prints spectrum for (int targN=1; targN < molStates.size(); targN++) { std::cout << "===== Target state #" << targN << " =====\n\n"<< std::flush; // clear the FC factors and related data FCFs.clear(); I.clear(); E_position.clear(); //---------------------------------------------------------------------- // calculate dQ: // Target state geometry in cartesian coordinates: KMatrix TargetCartCoord( CARTDIM*(molStates[iniN].NAtoms()), 1); for(int i=0; i<molStates[targN].NAtoms(); i++) for (int k=0; k <CARTDIM; k++) TargetCartCoord[i*CARTDIM+k]=molStates[targN].getAtom(i).Coord(k); //input is in angstroms TargetCartCoord.Scale(ANGSTROM2AU); // Shift of the target state relative to the initial (In cart. coord.) TargetCartCoord -= InitialCartCoord; // Transform Cart_>normal Mode coordinates // Geometry differences in normal coordinates KMatrix NormModeShift_ini(molStates[iniN].NNormModes(), 1), NormModeShift_targ(molStates[iniN].NNormModes(), 1); NormModeShift_ini.Multiply( Cart2Normal_Rs[iniN], TargetCartCoord); NormModeShift_targ.Multiply(Cart2Normal_Rs[targN] , TargetCartCoord); // Rescale it back & print: NormModeShift_ini*=(AU2ANGSTROM); NormModeShift_targ*=(AU2ANGSTROM); std::cout<<"Difference (dQ) between the initial and the target state geometries.\n" <<"Angstrom*sqrt(amu):\n\n"; std::cout << "normal mode dQ in initial dQ in target frequency frequency comments\n"; std::cout << " number state coord. state coord. initial target\n\n"; for (int nm=0; nm<n_norm_modes; nm++) { std::cout << " " << std::fixed << std::setw(3) << nm <<" " << std::setprecision(6) << std::setw(9) << NormModeShift_ini[nm] << " " << std::setw(9) << NormModeShift_targ[nm] << " " << std::setprecision(2) << std::setw(7) << molStates[iniN].getNormMode(nm).getFreq() << " " << std::setw(7) << molStates[targN].getNormMode(nm).getFreq() << "\n"; } std::cout<<"\n\n"; //---------------------------------------------------------------------- // save the spectrumnm overlap to file (for normal mode reordering tool in the web interface) if (if_web_version) { std::vector <int> nondiagonal_list; KMatrix NMoverlap; bool if_overlap_diagonal=molStates[targN].getNormalModeOverlapWithOtherState(molStates[iniN], NMoverlap, nondiagonal_list); std::ofstream nmoverlapF; nmoverlapF.open(nmoverlapFName, std::ios::out); nmoverlapF << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" << "<nmoverlap\n nm_initial=\"" <<n_norm_modes <<"\"\n nm_target =\""<<n_norm_modes << "\">\n\n<nm_order_initial>\n"; //inital state's nm order -- 0,1,2... -- always! for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<oi" << nm << ">"<< molStates[iniN].getNormModeIndex(nm) << "</oi" << nm << ">\n"; nmoverlapF << "</nm_order_initial>\n\n<frequencies_initial>\n"; for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<fi" << nm << ">"<< std::fixed << std::setprecision(1) << molStates[iniN].getNormMode(nm).getFreq()<< "</fi" << nm << ">\n"; nmoverlapF << "</frequencies_initial>\n\n<displacements_initial>\n"; for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<dqi" << nm << ">"<< std::fixed << std::setprecision(2) << NormModeShift_ini[nm]<< "</dqi" << nm << ">\n"; nmoverlapF << "</displacements_initial>\n\n<nm_order_target>\n"; for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<ot" << nm << ">"<< molStates[targN].getNormModeIndex(nm) << "</ot" << nm << ">\n"; nmoverlapF << "</nm_order_target>\n\n<frequencies_target>\n"; for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<ft" << nm << ">"<< std::fixed << std::setprecision(1) << molStates[targN].getNormMode(nm).getFreq()<< "</ft" << nm << ">\n"; nmoverlapF << "</frequencies_target>\n\n<displacements_target>\n"; for (int nm=0; nm<n_norm_modes; nm++) nmoverlapF << "<dqt" << nm << ">"<< std::fixed << std::setprecision(2) << NormModeShift_targ[nm]<< "</dqt" << nm << ">\n"; nmoverlapF << "</displacements_target>\n\n<overlap_matrix>\n"; for (int nmt=0; nmt<n_norm_modes; nmt++) { nmoverlapF << "<row"<<nmt<<">\n"; for (int nmi=0; nmi<n_norm_modes; nmi++) nmoverlapF << "<c"<<nmi<<">"<< NMoverlap.Elem2(nmt,nmi)<<"</c"<<nmi<<">"; nmoverlapF << "\n</row"<<nmt<<">\n"; } nmoverlapF << "</overlap_matrix>\n\n</nmoverlap>"; nmoverlapF.close(); } //---------------------------------------------------------------------- if (if_use_target_nm) std::cout<<"TARGET state normal coordinates (displacements dQ) will be used.\n\n"; else std::cout<<"INITIAL state normal coordinates are used.\n\n"; // ---------------------------------------------------------------------- if (if_print_fsfs) std::cout << "Peak positions, Franck-Condon factors, and intensities along each normal mode:\n\n"; // for each normal mode for (int nm=0; nm<n_norm_modes; nm++) //for each normal mode of the initial state { // Calculate the energy positions: for (int i=0; i<max_n_initial+1; i++) for (int j=0; j<max_n_target+1; j++) // energy position of each transition for a given normal mode; 1D; ofset by IP; when add for N dimensiond, substract IP from each energy; E_position_tmp.Elem2(i,j)=-molStates[targN].Energy() +(molStates[iniN].getNormMode(nm).getFreq() * i -molStates[targN].getNormMode(nm).getFreq() * j) * WAVENUMBERS2EV; E_position.push_back(E_position_tmp); // Calculate the Franck-Condon factors if (if_use_target_nm) // which normal modes (displacements) to use: of the initial or the target state? harmonic_FCf(FCFs_tmp,reducedMass, NormModeShift_targ[nm], molStates[iniN].getNormMode(nm).getFreq(), molStates[targN].getNormMode(nm).getFreq()); else harmonic_FCf(FCFs_tmp,reducedMass, NormModeShift_ini[nm], molStates[iniN].getNormMode(nm).getFreq(), molStates[targN].getNormMode(nm).getFreq()); FCFs.push_back(FCFs_tmp); //Calculate the line intensities (FCF^2 * the temperature distribution in the initial state): for (int i=0; i<max_n_initial+1; i++) { double IExponent=100*i; //(intensity < 10e-44 for non ground states if temperature=0) if (temperature>0) { IExponent= molStates[iniN].getNormMode(nm).getFreq()*WAVENUMBERS2EV * i / (temperature * KELVINS2EV ); if (IExponent > 100) IExponent=100; // keep the intensity >= 10e-44 == exp(-100) } for (int j=0; j<max_n_target+1; j++) I_tmp.Elem2(i,j)=FCFs_tmp.Elem2(i,j) * FCFs_tmp.Elem2(i,j) * exp ( -IExponent); } I.push_back(I_tmp); if (if_print_fsfs) { std::cout << "NORMAL MODE #" << nm+0 <<" (" << std::fixed << std::setw(8) << std::setprecision(2) << molStates[iniN].getNormMode(nm).getFreq() << "cm-1 --> " <<molStates[targN].getNormMode(nm).getFreq() << "cm-1, dQ=" << std::setw(6) << std::setprecision(4); if (if_use_target_nm) std::cout << NormModeShift_targ[nm]; else std::cout << NormModeShift_ini[nm]; std::cout << ")\n"; E_position[nm].Print("Peak positions, eV"); FCFs[nm].PrintScientific("1D Harmonic Franck-Condon factors"); I[nm].Print("Intensities (FCFs^2)*(initial vibrational states termal population)"); } } // end for each normal mode //================================================================================ // evaluate the overal intensities as all possible products of 1D intensities (including combination bands) std::cout << "Maximum number of vibrational excitations: " << max_n_initial << " and "<< max_n_target << "\n in the initial and each target state, respectively.\n\n"; unsigned long total_combs_ini=0, total_combs_targ=0; for (int curr_max_ini=0; curr_max_ini<=max_n_initial; curr_max_ini++) total_combs_ini += Combination( (curr_max_ini + nm_list.size() - 1), (nm_list.size() - 1) ); for (int curr_max_targ=0; curr_max_targ<=max_n_target; curr_max_targ++) total_combs_targ += Combination( (curr_max_targ + nm_list.size() -1), (nm_list.size() - 1) ); std::cout << "Maximum number of combination bands = " << total_combs_ini*total_combs_targ << "\n = " << total_combs_ini << " (# of vibrational states in the initial electronic state)" << "\n * " << total_combs_targ << " (# of vibrational states in the target electronic state)\n\n" << std::flush; for (int i=0; i<state_ini_subspace.size(); i++) state_ini_subspace[i]=-1; for (int i=0; i<state_targ_subspace.size(); i++) state_targ_subspace[i]=-1; // find INITIAL states with up to 'max_n_initial' vibrational quanta and with energy below 'energy_threshold_initial': std::cout << "A set of initial vibrational states is being created...\n"; if (energy_threshold_initial < DBL_MAX) std::cout << " energy threshold = " << std::fixed << energy_threshold_initial <<" eV ("<< energy_threshold_initial/KELVINS2EV <<" K)\n" << std::flush; else std::cout << " energy threshold is not specified in the input\n"<<std::flush; // std::cout << "NOTE: ezSpectrum may crash at this point if memory is insufficient to store\n" // <<" all vibrational states. If so, please reduce the initial state's energy\n" // <<" threshold or max_vibr_excitations_in_initial_el_state\n\n" << std::flush; selected_states_ini.clear(); for (int curr_max_ini=0; curr_max_ini<=max_n_initial; curr_max_ini++) { while (enumerateVibrStates(state_targ_subspace.size(), curr_max_ini, state_ini_subspace, if_comb_bands)) { // reset the index list state_ini for (int nm=0; nm < state_ini.size(); nm++) state_ini[nm]=0; //copy indexes from the subspace state_ini_subspace into the full space state_ini (the rest stays=0): int index=0; for (int nm=0; nm<nm_list.size(); nm++) state_ini[ nm_list[nm] ] = state_ini_subspace[nm]; double energy = 0; for (int nm=0; nm < n_norm_modes; nm++) energy += E_position[nm].Elem2(state_ini[nm],0)+molStates[targN].Energy(); if (energy < energy_threshold_initial) { // check memory available (dirty, but anyway one copying of the state is requared...) //int * buffer = (int*) malloc ( sizeof(int)*n_norm_modes*100 ); //if (buffer==NULL) // { // std::cout << "\nError: not enough memory available to store all initial vibrational states\n\n"; // exit(2); // } //free(buffer); selected_states_ini.push_back(state_ini); } } //reset the initial state's vibration "population" state_ini_subspace[0]=-1; } std::cout << " " << selected_states_ini.size() << " vibrational states below the energy threshold\n\n"<<std::flush; // find TARGET states with up to 'max_n_target' vibrational quanta and with energy below 'energy_threshold_target': std::cout << "A set of target vibrational states is being created...\n"; if (energy_threshold_target < DBL_MAX) std::cout << " energy threshold = " << std::fixed <<energy_threshold_target <<" eV ("<< energy_threshold_target/WAVENUMBERS2EV <<" cm-1)\n"<<std::flush; else std::cout << " energy threshold is not specified in the input\n"<<std::flush; // std::cout << "NOTE: ezSpectrum may crash at this point if memory is insufficient to store\n" // <<" all vibrational states. If so, please reduce the target state's energy\n" // <<" threshold or max_vibr_excitations_in_target_el_state\n\n" << std::flush; selected_states_targ.clear(); for (int curr_max_targ=0; curr_max_targ<=max_n_target; curr_max_targ++) { while (enumerateVibrStates(state_targ_subspace.size(), curr_max_targ, state_targ_subspace, if_comb_bands)) { // reset the index list state_targ for (int nm=0; nm < state_targ.size(); nm++) state_targ[nm]=0; //copy indexes from the subspace state_targ_subspace into the full space state_targ (the rest stays=0): int index=0; for (int nm=0; nm<nm_list.size(); nm++) state_targ[ nm_list[nm] ] = state_targ_subspace[nm]; double energy = 0; for (int nm=0; nm < n_norm_modes; nm++) // threshold -- energy above the ground state: energy += E_position[nm].Elem2(0,state_targ[nm])+molStates[targN].Energy(); if ( -energy < energy_threshold_target ) { // check memory available (dirty, but anyway one copying of the state is requared...) //VibronicState * state_tmp = (VibronicState*) malloc ( sizeof(VibronicState)*2 ); //if (state_tmp==NULL) // { // std::cout << "\nError: not enough memory available to store all target vibrational states\n\n"; // exit(2); // } //free (state_tmp); selected_states_targ.push_back(state_targ); } } //reset the target state's vibration "population" state_targ_subspace[0]=-1; } std::cout << " " << selected_states_targ.size() << " vibrational states below the energy threshold\n\n"; std::cout << "Total number of combination bands with thresholds applied: " << selected_states_ini.size()*selected_states_targ.size() <<"\n\n"<<std::flush; std::cout << "Intensities of combination bands are being calculated...\n" <<std::flush; for (int curr_ini=0; curr_ini<selected_states_ini.size(); curr_ini++) for (int curr_targ=0; curr_targ<selected_states_targ.size(); curr_targ++) { double intens = 1; double FCF = 1; double energy = -molStates[targN].Energy(); double E_prime_prime = 0; for (int nm=0; nm < n_norm_modes; nm++) { intens *= I[nm].Elem2(selected_states_ini[curr_ini][nm],selected_states_targ[curr_targ][nm]); FCF *= FCFs[nm].Elem2(selected_states_ini[curr_ini][nm],selected_states_targ[curr_targ][nm]); energy += E_position[nm].Elem2(selected_states_ini[curr_ini][nm],selected_states_targ[curr_targ][nm])+molStates[targN].Energy(); E_prime_prime += E_position[nm].Elem2(selected_states_ini[curr_ini][nm],0)+molStates[targN].Energy(); // [ cancell the IE in each energy, which is stupid but inexpensive; probably should be eliminated ] } // add the point to the spectrum if its intensity is above the threshold if (intens > intens_threshold) { tmpPoint.getIntensity() = intens; tmpPoint.getEnergy() = energy; tmpPoint.getE_prime_prime()= E_prime_prime; tmpPoint.getFCF()= FCF; tmpPoint.getVibrState1().reset(); tmpPoint.getVibrState1().setElStateIndex(0); tmpPoint.getVibrState2().reset(); tmpPoint.getVibrState2().setElStateIndex(targN); for (int nm=0; nm<nm_list.size(); nm++) { tmpPoint.getVibrState1().setVibrQuanta(nm, selected_states_ini[curr_ini][ nm_list[nm] ]); tmpPoint.getVibrState2().setVibrQuanta(nm, selected_states_targ[curr_targ][ nm_list[nm] ]); } spectrum.AddSpectralPoint( tmpPoint ); } } } // end for each target state }
RPState* RPState::doSUSN(ReportingLevel rl) const { RPState* s2 = nullptr; const unsigned int numA = model->numAct; assert(numA == rpMod->actrs.size()); const unsigned int numU = uIndices.size(); assert ((0 < numU) && (numU <= numA)); assert (numA == eIndices.size()); // TODO: filter out essentially-duplicate positions //printf("RPState::doSUSN: numA %i \n", numA); //printf("RPState::doSUSN: numP %i \n", numP); //cout << endl << flush; const KMatrix u = aUtil[0]; // all have same beliefs in this demo auto vpm = VPModel::Linear; const unsigned int numP = pstns.size(); // Given the utility matrix, uMat, calculate the expected utility to each actor, // as a column-vector. Again, this is from the perspective of whoever developed uMat. auto euMat = [rl, numA, numP, vpm, this](const KMatrix & uMat) { // BTW, be sure to lambda-bind uMat *after* it is modified. assert(uMat.numR() == numA); // must include all actors assert(uMat.numC() <= numP); // might have dropped some duplicates auto uRng = [uMat](unsigned int i, unsigned int j) { if ((uMat(i, j) < 0.0) || (1.0 < uMat(i, j))) { printf("%f %i %i \n", uMat(i, j), i, j); cout << flush; cout << flush; } assert(0.0 <= uMat(i, j)); assert(uMat(i, j) <= 1.0); return; }; KMatrix::mapV(uRng, uMat.numR(), uMat.numC()); // vote_k ( i : j ) auto vkij = [this, uMat](unsigned int k, unsigned int i, unsigned int j) { auto ak = (RPActor*)(rpMod->actrs[k]); auto v_kij = Model::vote(ak->vr, ak->sCap, uMat(k, i), uMat(k, j)); return v_kij; }; // the following uses exactly the values in the given euMat, // which may or may not be square const KMatrix c = Model::coalitions(vkij, uMat.numR(), uMat.numC()); const KMatrix pv = Model::vProb(vpm, c); // square const KMatrix p = Model::probCE(PCEModel::ConditionalPCM, pv); // column const KMatrix eu = uMat*p; // column assert(numA == eu.numR()); assert(1 == eu.numC()); auto euRng = [eu](unsigned int i, unsigned int j) { // due to round-off error, we must have a tolerance factor const double tol = 1E-10; const double euij = eu(i, j); assert(0.0 <= euij+tol); assert(euij <= 1.0+tol); return; }; KMatrix::mapV(euRng, eu.numR(), eu.numC()); if (ReportingLevel::Low < rl) { printf("Util matrix is %i x %i \n", uMat.numR(), uMat.numC()); cout << "Assessing EU from util matrix: " << endl; uMat.mPrintf(" %.6f "); cout << endl << flush; cout << "Coalition strength matrix" << endl; c.mPrintf(" %12.6f "); cout << endl << flush; cout << "Probability Opt_i > Opt_j" << endl; pv.mPrintf(" %.6f "); cout << endl << flush; cout << "Probability Opt_i" << endl; p.mPrintf(" %.6f "); cout << endl << flush; cout << "Expected utility to actors: " << endl; eu.mPrintf(" %.6f "); cout << endl << flush; } return eu; }; // end of euMat auto euState = euMat(u); cout << "Actor expected utilities: "; KBase::trans(euState).mPrintf("%6.4f, "); cout << endl << flush; if (ReportingLevel::Low < rl) { printf("--------------------------------------- \n"); printf("Assessing utility of actual state to all actors \n"); for (unsigned int h = 0; h < numA; h++) { cout << "not available" << endl; } cout << endl << flush; printf("Out of %u positions, %u were unique: ", numA, numU); cout << flush; for (auto i : uIndices) { printf("%2i ", i); } cout << endl; cout << flush; } auto uufn = [u, this](unsigned int i, unsigned int j1) { return u(i, uIndices[j1]); }; auto uUnique = KMatrix::map(uufn, numA, numU); // Get expected-utility vector, one entry for each actor, in the current state. const KMatrix eu0 = euMat(uUnique); // 'u' with duplicates, 'uUnique' without duplicates s2 = new RPState(model); //s2->pstns = vector<KBase::Position*>(); for (unsigned int h = 0; h < numA; h++) { s2->pstns.push_back(nullptr); } // TODO: clean up the nesting of lambda-functions. // need to create a hypothetical state and run setOneAUtil(h,Silent) on it // // The newPosFn does a GA optimization to find the best next position for actor h, // and stores it in s2. To do that, it defines three functions for evaluation, neighbors, and show: // efn, nfn, and sfn. auto newPosFn = [this, rl, euMat, u, eu0, s2](const unsigned int h) { s2->pstns[h] = nullptr; auto ph = ((const MtchPstn *)(pstns[h])); // Evaluate h's estimate of the expected utility, to h, of // advocating position mp. To do this, build a hypothetical utility matrix representing // h's estimates of the direct utilities to all other actors of h adopting this // Position. Do that by modifying the h-column of h's matrix. // Then compute the expected probability distribution, over h's hypothetical position // and everyone else's actual position. Finally, compute the expected utility to // each actor, given that distribution, and pick out the value for h's expected utility. // That is the expected value to h of adopting the position. auto efn = [this, euMat, rl, u, h](const MtchPstn & mph) { // This correctly handles duplicated/unique options // We modify the given euMat so that the h-column // corresponds to the given mph, but we need to prune duplicates as well. // This entails some type-juggling. const KMatrix uh0 = aUtil[h]; assert(KBase::maxAbs(u - uh0) < 1E-10); // all have same beliefs in this demo if (mph.match.size() != rpMod->numItm) { cout << mph.match.size() << endl << flush; cout << rpMod->numItm << endl << flush; cout << flush << flush; } assert(mph.match.size() == rpMod->numItm); auto uh = uh0; for (unsigned int i = 0; i < rpMod->numAct; i++) { auto ai = (RPActor*)(rpMod->actrs[i]); double uih = ai->posUtil(&mph); uh(i, h) = uih; // utility to actor i of this hypothetical position by h } // 'uh' now has the correct h-column. Now we need to see how many options // are unique in the hypothetical state, and keep only those columns. // This entails juggling back and forth between the all current positions // and the one hypothetical position (mph at h). // Thus, the next call to euMat will consider only unique options. auto equivHNdx = [this, h, mph](const unsigned int i, const unsigned int j) { // this little function takes care of the different types needed to compare // dynamic pointers to positions (all but h) with a constant position (h itself). // In other words, the comparisons for index 'h' use the hypothetical mph, not pstns[h] bool rslt = false; auto mpi = ((const MtchPstn *)(pstns[i])); auto mpj = ((const MtchPstn *)(pstns[j])); assert(mpi != nullptr); assert(mpj != nullptr); if (i == j) { rslt = true; // Pi == Pj, always } else if (h == i) { rslt = (mph == (*mpj)); } else if (h == j) { rslt = ((*mpi) == mph); } else { rslt = ((*mpi) == (*mpj)); } return rslt; }; auto ns = KBase::uiSeq(0, model->numAct - 1); const VUI uNdx = get<0>(KBase::ueIndices<unsigned int>(ns, equivHNdx)); const unsigned int numU = uNdx.size(); auto hypUtil = KMatrix(rpMod->numAct, numU); // we need now to go through 'uh', copying column J the first time // the J-th position is determined to be equivalent to something in the unique list for (unsigned int i = 0; i < rpMod->numAct; i++) { for (unsigned int j1 = 0; j1 < numU; j1++) { unsigned int j2 = uNdx[j1]; hypUtil(i, j1) = uh(i, j2); // hypothetical utility in column h } } if (false) { cout << "constructed hypUtil matrix:" << endl << flush; hypUtil.mPrintf(" %8.2f "); cout << endl << flush; } if (ReportingLevel::Low < rl) { printf("--------------------------------------- \n"); printf("Assessing utility to %2i of hypo-pos: ", h); printPerm(mph.match); cout << endl << flush; printf("Hypo-util minus base util: \n"); (uh - uh0).mPrintf(" %+.4E "); cout << endl << flush; } const KMatrix eu = euMat(hypUtil); // uh or hypUtil // BUG: If we use 'uh' here, it passes the (0 <= delta-EU) test, because // both hypothetical and actual are then calculated without dropping duplicates. // If we use 'hypUtil' here, it sometimes gets (delta-EU < 0), because // the hypothetical drops duplicates but the actual (computed elsewhere) does not. // FIX: fix the 'elsewhere' const double euh = eu(h, 0); assert(0 < euh); //cout << euh << endl << flush; //printPerm(mp.match); //cout << endl << flush; //cout << flush; return euh; }; // end of efn /* // I do not actually use prevMP, but it is still an example for std::set auto prevMP = [](const MtchPstn & mp1, const MtchPstn & mp2) { bool r = std::lexicographical_compare( mp1.match.begin(), mp1.match.end(), mp2.match.begin(), mp2.match.end()); return r; }; std::set<MtchPstn, bool(*)(const MtchPstn &, const MtchPstn &)> mpSet(prevMP); */ // return vector of neighboring 1-permutations auto nfn = [](const MtchPstn & mp0) { const unsigned int numI = mp0.match.size(); auto mpVec = vector <MtchPstn>(); mpVec.push_back(MtchPstn(mp0)); // one-permutations for (unsigned int i = 0; i < numI; i++) { for (unsigned int j = i + 1; j < numI; j++) { unsigned int ei = mp0.match[i]; unsigned int ej = mp0.match[j]; auto mij = MtchPstn(mp0); mij.match[i] = ej; mij.match[j] = ei; mpVec.push_back(mij); } } // two-permutations for (unsigned int i = 0; i < numI; i++) { for (unsigned int j = i + 1; j < numI; j++) { for (unsigned int k = j + 1; k < numI; k++) { unsigned int ei = mp0.match[i]; unsigned int ej = mp0.match[j]; unsigned int ek = mp0.match[k]; auto mjki = MtchPstn(mp0); mjki.match[i] = ej; mjki.match[j] = ek; mjki.match[k] = ei; mpVec.push_back(mjki); auto mkij = MtchPstn(mp0); mkij.match[i] = ek; mkij.match[j] = ei; mkij.match[k] = ej; mpVec.push_back(mkij); } } } //unsigned int mvs = mpVec.size() ; //cout << mvs << endl << flush; //cout << flush; return mpVec; }; // end of nfn // show some representation of this position on cout auto sfn = [](const MtchPstn & mp0) { printPerm(mp0.match); return; }; auto ghc = new KBase::GHCSearch<MtchPstn>(); ghc->eval = efn; ghc->nghbrs = nfn; ghc->show = sfn; auto rslt = ghc->run(*ph, // start from h's current positions ReportingLevel::Silent, 100, // iter max 3, 0.001); // stable-max, stable-tol if (ReportingLevel::Low < rl) { printf("---------------------------------------- \n"); printf("Search for best next-position of actor %2i \n", h); //printf("Search for best next-position of actor %2i starting from ", h); //trans(*aPos).printf(" %+.6f "); cout << flush; } double vBest = get<0>(rslt); MtchPstn pBest = get<1>(rslt); unsigned int iterN = get<2>(rslt); unsigned int stblN = get<3>(rslt); delete ghc; ghc = nullptr; if (ReportingLevel::Medium < rl) { printf("Iter: %u Stable: %u \n", iterN, stblN); printf("Best value for %2i: %+.6f \n", h, vBest); cout << "Best position: " << endl; cout << "numCat: " << pBest.numCat << endl; cout << "numItm: " << pBest.numItm << endl; cout << "perm: "; printPerm(pBest.match); cout << endl << flush; } MtchPstn * posBest = new MtchPstn(pBest); s2->pstns[h] = posBest; // no need for mutex, as s2->pstns is the only shared var, // and each h is different. double du = vBest - eu0(h, 0); // (hypothetical, future) - (actual, current) if (ReportingLevel::Low < rl) { printf("EU improvement for %2i of %+.4E \n", h, du); } //printf(" vBest = %+.6f \n", vBest); //printf(" eu0(%i, 0) for %i = %+.6f \n", h, h, eu0(h,0)); //cout << endl << flush; // Logically, du should always be non-negative, as GHC never returns a worse value than the starting point. // However, actors plan on the assumption that all others do not change - yet they do. const double eps = 0.05; // 0.025; // enough to avoid problems with round-off error assert(-eps <= du); return; }; // end of newPosFn const bool par = true; auto ts = vector<thread>(); // Each actor, h, finds the position which maximizes their EU in this situation. for (unsigned int h = 0; h < numA; h++) { if (par) { // launch all, concurrent ts.push_back(thread([newPosFn, h]() { newPosFn(h); return; })); } else { // do each, sequential newPosFn(h); } } if (par) { // now join them all before continuing for (auto& t : ts) { t.join(); } } assert(nullptr != s2); assert(numP == s2->pstns.size()); assert(numA == s2->model->numAct); for (auto p : s2->pstns) { assert(nullptr != p); } s2->setUENdx(); return s2; }
// return the list of the most self-interested position of each actor, // with the CP last. // As a side-affect, set each actor's min/max permutation values so as to // compute normalized utilities later. vector<VUI> scanAllPossiblePositions(const RPModel * rpm) { unsigned int numA = rpm->numAct; unsigned int numRefItem = rpm->numItm; assert(numRefItem == rpm->numCat); LOG(INFO) << "There are" << numA << "actors and" << numRefItem << "reform items"; KMatrix aCap = KMatrix(1, numA); for (unsigned int i = 0; i < numA; i++) { auto ri = ((const RPActor *)(rpm->actrs[i])); aCap(0, i) = ri->sCap; } LOG(INFO) << "Actor capabilities: "; aCap.mPrintf(" %.2f "); LOG(INFO) << "Effective gov cost of items:"; (rpm->govCost).mPrintf("%.3f "); LOG(INFO) << "Government budget: " << rpm->govBudget; assert(0 < rpm->govBudget); string log("Value to actors (rows) of individual reform items (columns):"); for (unsigned int i = 0; i < rpm->actrs.size(); i++) { auto rai = ((const RPActor*)(rpm->actrs[i])); for (unsigned int j = 0; j < numRefItem; j++) { double vij = rai->riVals[j]; log += KBase::getFormattedString(" %6.2f", vij); } } LOG(INFO) << log; LOG(INFO) << "Computing positions ... "; vector<VUI> allPositions; // list of all possiblepositions VUI pstn; // build the first permutation: 0,1,2,3,... for (unsigned int i = 0; i < numRefItem; i++) { pstn.push_back(i); } allPositions.push_back(pstn); while (next_permutation(pstn.begin(), pstn.end())) { allPositions.push_back(pstn); } const unsigned int numPos = allPositions.size(); LOG(INFO) << "For" << numRefItem << "reform items there are" << numPos << "positions"; // ------------------------------------------------- // The next section sets up actor utilities. // First, we compute the unnormalized, raw utilities. The 'utilActorPos' checks // to see if pvMin/pvMax have been set, and returns the raw scores if not. // Then we scan across rows to find that actor's pvMin/pvMax, and record that // so utilActorPos can use it in the future. Finally, we normalize the rows and // display the normalized utility matrix. auto ruFn = [allPositions, rpm](unsigned int ai, unsigned int pj) { auto pstn = allPositions[pj]; double uip = rpm->utilActorPos(ai, pstn); return uip; }; LOG(INFO) << "Computing utilities of positions ... "; // rows are actors, columns are all possible positions auto rawUij = KMatrix::map(ruFn, numA, numPos); // set the min/max for each actor for (unsigned int i = 0; i < numA; i++) { double pvMin = rawUij(i, 0); double pvMax = rawUij(i, 0); for (unsigned int j = 0; j < numPos; j++) { double rij = rawUij(i, j); if (rij < pvMin) { pvMin = rij; } if (rij > pvMax) { pvMax = rij; } } assert(0 <= pvMin); assert(pvMin < pvMax); auto ai = ((RPActor*)(rpm->actrs[i])); ai->posValMin = pvMin; ai->posValMax = pvMax; } LOG(INFO) << "Normalizing utilities of positions ... "; KMatrix uij = KBase::rescaleRows(rawUij, 0.0, 1.0); // von Neumann utility scale string utilMtx("Complete (normalized) utility matrix of all possible positions (rows) versus actors (columns) \n"); for (unsigned int pj = 0; pj < numPos; pj++) { utilMtx += KBase::getFormattedString("%3u ", pj); auto pstn = allPositions[pj]; //printVUI(pstn); utilMtx += KBase::stringVUI(pstn); utilMtx += " "; for (unsigned int ai = 0; ai < numA; ai++) { double uap = uij(ai, pj); utilMtx += KBase::getFormattedString("%6.4f, ", uap); } utilMtx += KBase::getFormattedString("\n"); } LOG(INFO) << utilMtx; // ------------------------------------------------- // The next section determines the most self-interested positions for each actor, // as well as the 'central position' over all possible reform priorities // (which 'office seeking politicans' would adopt IF proportional voting). LOG(INFO) << "Computing best position for each actor"; vector<VUI> bestAP; // list of each actor's best position (followed by CP) for (unsigned int ai = 0; ai < numA; ai++) { unsigned int bestJ = 0; double bestV = 0; for (unsigned int pj = 0; pj < numPos; pj++) { if (bestV < uij(ai, pj)) { bestJ = pj; bestV = uij(ai, pj); } } string bestMtx("Best position for "); string ais = std::to_string(ai); //string bjs = std::to_string(bestJ); string ps = KBase::stringVUI(allPositions[bestJ]); bestMtx += ais + " is " + ps; LOG(INFO) << bestMtx; //LOG(INFO) << "Best for" << ai << "is "; //printVUI(positions[bestJ]); bestAP.push_back(allPositions[bestJ]); } LOG(INFO) << "Computing zeta ... "; KMatrix zeta = aCap * uij; assert((1 == zeta.numR()) && (numPos == zeta.numC())); LOG(INFO) << "Sorting positions from most to least net support ..."; auto betterPR = [](tuple<unsigned int, double, VUI> pr1, tuple<unsigned int, double, VUI> pr2) { double v1 = get<1>(pr1); double v2 = get<1>(pr2); bool better = (v1 > v2); return better; }; auto pairs = vector<tuple<unsigned int, double, VUI>>(); for (unsigned int i = 0; i < numPos; i++) { auto pri = tuple<unsigned int, double, VUI>(i, zeta(0, i), allPositions[i]); pairs.push_back(pri); } sort(pairs.begin(), pairs.end(), betterPR); const unsigned int maxDisplayed = 720; // factorial(6) unsigned int numPr = (pairs.size() < maxDisplayed) ? pairs.size() : maxDisplayed; LOG(INFO) << "Displaying highest" << numPr; for (unsigned int i = 0; i < numPr; i++) { auto pri = pairs[i]; unsigned int ni = get<0>(pri); double zi = get<1>(pri); VUI pi = get<2>(pri); string ps = KBase::stringVUI(pi); LOG(INFO) << KBase::getFormattedString(" %3u: %4u %.2f %s", i, ni, zi, ps.c_str()); //printVUI(pi); } VUI bestPerm = get<2>(pairs[0]); bestAP.push_back(bestPerm); return bestAP; }
// Given square matrix of strengths, Coalition[i over j] returns a column vector for Prob[i]. // Uses Markov process, not 1-step conditional probability. // Challenge probabilities are proportional to influence promoting a challenge KMatrix Model::markovIncentivePCE(const KMatrix & coalitions, VPModel vpm) { using KBase::sqr; using KBase::qrtc; const bool printP = false; const double pTol = 1E-8; const unsigned int numOpt = coalitions.numR(); assert(numOpt == coalitions.numC()); const auto victProbMatrix = vProb(vpm, coalitions); // given coalitions, calculate the total incentive for i to challenge j // This is n[ i -> j] in the "Markov Voting with Incentives in KTAB" paper auto iFn = [victProbMatrix, coalitions](unsigned int i, unsigned int j) { const double epsSupport = 1E-10; const double sij = coalitions(i, j); double inctv = sij * victProbMatrix(i,j); if (i == j) { inctv = inctv + epsSupport; } return inctv; }; const auto inctvMatrix = KMatrix::map(iFn, numOpt, numOpt); // Using the incentives, calculate the probability of i challenging j, // given that j is the current favorite proposal. // This is P[ i -> j] in the "Markov Voting with Incentives in KTAB" paper // Note that if every actor prefers j to every other option, // then all incentive(i,j) will be zero, except incentive(j,j) = eps. // Even in this case, we will not get a division by zero error, // and it will correctly return that the only "challenger" is j itself, // with guaranteed success. // auto cpFn = [inctvMatrix, numOpt](unsigned int i, unsigned int j) { double sum = 0.0; for (unsigned int k = 0; k < numOpt; k++) { sum = sum + inctvMatrix(k, j); } const double pij = inctvMatrix(i, j) / sum; return pij; }; const auto chlgProbMatrix = KMatrix::map(cpFn, numOpt, numOpt); // probability starts as uniform distribution (column vector) auto p = KMatrix(numOpt, 1, 1.0) / numOpt; // all 1/n auto q = p; unsigned int iMax = 1000; // 10-30 is typical unsigned int iter = 0; double change = 1.0; // do the markov calculation while (pTol < change) { // && (iter < iMax) if (printP) { printf("Iteration %u / %u \n", iter, iMax); cout << "pDist:" << endl; trans(p).mPrintf(" %.4f"); cout << endl; printf("change: %.4e \n", change); cout << endl << flush; } auto ct = KMatrix(numOpt, numOpt); for (unsigned int i = 0; i < numOpt; i++) { for (unsigned int j = 0; j < numOpt; j++) { // See "Markov Voting with Incentives in KTAB" paper ct(i, j) = p(i, 0) * chlgProbMatrix(j, i); } } if (printP) { cout << "Ct:" << endl; ct.mPrintf(" %.3f"); cout << endl << flush; } change = 0.0; for (unsigned int i = 0; i < numOpt; i++) { double qi = 0.0; for (unsigned int j = 0; j < numOpt; j++) { double vij = victProbMatrix(i, j); double cj = ct(i, j) + ct(j, i); qi = qi + vij* cj; } assert(0 <= qi); // double-check q(i, 0) = qi; double c = fabs(q(i, 0) - p(i, 0)); change = (c > change) ? c : change; } // Newton method improves convergence. p = (p+q)/2.0; iter++; assert(fabs(sum(p) - 1.0) < pTol); // double-check } assert(iter < iMax); // no way to recover return p; }