void initScen(uint64_t sd) { using KBase::quadUfromV; auto rng = new PRNG(sd); const unsigned int numA = 40; const unsigned int numItm = 7; const auto govCost = KMatrix::uniform(rng, 1, numItm, 25, 100); //col-vec const double govBudget = 0.625 * sum(govCost); // cannot afford everything const double obFactor = 0.125; // The 'riVals' matrix shows the value to the actor (row) of each reform item (clm), // not the utility of entire positions (which are permutations of items) const KMatrix riVal = KMatrix::uniform(rng, numA, numItm, 10, 100); const double pDecline = 0.8750; vector<double> aCap = {}; for (unsigned int i = 0; i < numA; i++) { double ac = rng->uniform(2.0, 4.0); // [4.0, 16.0] with median at 9.0 ac = rng->uniform(17.0, 18.0)*ac*ac; aCap.push_back(ac); } vector<double> prob = {}; double pj = 1.0; printf("pDecline factor: %.3f \n", pDecline); printf("obFactor: %.3f \n", obFactor); // rate of decline has little effect on the results. for (unsigned int j = 0; j < numItm; j++) { prob.push_back(pj); pj = pj * pDecline; } cout << "Computing positions ... " << endl; vector<VUI> positions; // list of all positions VUI pstn; // build the first permutation: 1,2,3,... for (unsigned int i = 0; i < numItm; i++) { pstn.push_back(i); } positions.push_back(pstn); while (next_permutation(pstn.begin(), pstn.end())) { positions.push_back(pstn); } const unsigned int numPos = positions.size(); cout << "For " << numItm << " reform items there are "; cout << numPos << " positions" << endl; cout << "Building position utility matrix ... " << flush; auto rpValFn = [positions, riVal, aCap, govCost, govBudget, obFactor, prob](unsigned int ai, unsigned int pj) { VUI pstn = positions[pj]; double rvp = rawValPos(ai, pstn, riVal, aCap, govCost, govBudget, obFactor, prob); return rvp; }; const auto rpRawVal = KMatrix::map(rpValFn, numA, numPos); const auto rpNormVal = KBase::rescaleRows(rpRawVal, 0.0, 1.0); const double bigR = 0.5; auto curve = [bigR](double v) { return quadUfromV(v, bigR); }; const auto rpUtil = KMatrix::map(curve, rpNormVal); cout << "done." << endl << flush; // This is an attempt to define a domain-independent measure of similarity, // by looking at the difference in outcomes to actors. // Notice that if we sort the columns by difference from #i, // those with small differences in outcome might do it by very different // means, so that columns from all over the matrix are placed near i. auto diffFn = [](unsigned int i, unsigned int j, const KMatrix& uMat) { const auto colI = KBase::vSlice(uMat, i); const auto colJ = KBase::vSlice(uMat, j); for (unsigned int k=0;k<colI.numR(); k++){ } double dij = KBase::norm(colI - colJ); return dij; }; auto diffVUI = [&rpUtil, numPos, diffFn](unsigned int n, unsigned int nSim) { vector<TDI> vdk = {}; for (unsigned int k = 0; k < numPos; k++) { double dkn = diffFn(k, n, rpUtil); auto dk = TDI(dkn, k); vdk.push_back(dk); } auto tupleLess = [](TDI t1, TDI t2) { double d1 = get<0>(t1); double d2 = get<0>(t2); return (d1 < d2); }; std::sort(vdk.begin(), vdk.end(), tupleLess); vector<TDI> sdk = {}; for (unsigned int i = 0; ((i < nSim) && (i < numPos)); i++) { sdk.push_back(vdk[i]); } return sdk; }; // The permutations with outcomes most similar to the given // one are those which make the most minor changes // (somewhat similar to a vector hill climbing search where the // points in a nearby neighborhood make small changes in objective). // So looking at a few dozen (out of thousands) might be enough to // get significant variation to keep the search progressing. unsigned int numClose = 50; unsigned int rand1 = rng->uniform(0, numPos); auto rslt1 = diffVUI(rand1, numClose); cout << "Permutations with outcomes most similar to " << rand1 << endl; for (unsigned int i = 0; i < rslt1.size(); i++) { auto dp = rslt1[i]; double d = get<0>(dp); unsigned int p = get<1>(dp); auto posP = positions[p]; printf("%2u: %4u %.4f ", i, p, d); KBase::printVUI(posP); cout << endl; } delete rng; rng = nullptr; return; }
// 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; }