/// <summary> /// Creates a cost system. Assigns resources to pools and selects /// drivers for each pool. /// </summary> /// <param name="ip">An input parameters object.</param> /// <param name="firm">The firm upon which this cost system is based.</param> /// <param name="a">The number of activity cost pools to form.</param> /// <param name="p">A flag indicating method for assigning resources to cost pools. /// See input file cheat sheet for details.</param> /// <param name="r">A flag indicating which resources in the pools /// will be used to form drivers. See input file cheat sheet for details.</param> public CostSys( InputParameters ip, Firm firm, int a, int p, int r) { this.firm = firm; RowVector RCC = firm.Initial_RCC; int[] RANK = firm.Initial_RANK; SymmetricMatrix CORR = firm.PEARSONCORR; this.a = a; this.p = p; this.r = r; if (a != 1) { #region Code shared in flowchart 6.1, 6.2, and 6.3 // Segregate resources into big ones that will each // seed a pool, and miscellaneous resources. // The first (a-1) resources get their own pools. List <int> bigResources = RANK.Take(a - 1).ToList(); List <int> miscResources = RANK.Skip(a - 1).ToList(); // Create the set B and initialize the first // elements with the big pool resources. // Seeding big resources // Take each resource from bigPools, ane make it into a list // of length 1. Convert to an array of lists, and assign to B. B = bigResources.Select(elem => new List <int> { elem }).ToArray(); // Increase the length by 1, to make room for the miscellaneous // pool. Array.Resize(ref B, B.Length + 1); B[B.Length - 1] = new List <int>(); #endregion // p == 0: // Seed (a-1) pools with the largest (a-1) resources. // All remaining resources assigned to miscellaneous pool if (p == 0) { #region Flowchart 6.1 B[a - 1] = new List <int>(miscResources); #endregion } // p == 1: // Seed acp-1 pools based on size. Check to see // the highest correlation for the remaining resources. Assign the // unassigned resource with the highest correlation to // the relevant ACP. Check to see if the value of remaining // ACP > MISCPOOLSIZE. If so, continue to find the next highest // correlation, assign and check. When remaining value < 20%, // then pool everything into misc. else if (p == 1) { #region Flowchart 6.2 // This query iterates over miscResources. For each one, it // computes the correlation with every bigResource, and forms // a record {smallResourceIndex, index of big pool (in B), correlation }. // Order this list of records in descending order and keep the first one. // This first one is the pool to which the small resources will be allocated // if the correlation is sufficiently high. var query = miscResources.Select(smallRes => bigResources.Select((bigRes, i) => new { smallRes, BigPoolNum = i, correl = CORR[bigRes, smallRes] }).OrderByDescending(x => x.correl).First()); // Order the small resources by correlation with big resources. Thus, // if resource 7 is most correlated with big pool resource 0 (92%), // and resource 12 is most correlated with big pool resource 1 (83%), // 7 will be ahead of 12 in myArray. var myArray = query.OrderByDescending(x => x.correl).ToArray(); // The following block makes sure that at least one nonzero // resource is allocated to the last pool. The only time this // fails is if all miscellaneous resources are zero. int lastResourceToAllocate; { // Convert each record in myArray to the value of the resource // cost pool represented by that resource var moo = myArray.Select(x => RCC[x.smallRes]); // Convert each element of moo to the value of the remaining // resources in the array at this point. var moo2 = moo.Select((_, i) => moo.Skip(i).Sum()); List <double> ld = moo2.ToList(); // If the list contains a 0, that means there are one or // more zero resources. Find the index of the first one, // or if there isn't one, use the end of the array. if (ld.Contains(0.0)) { lastResourceToAllocate = ld.IndexOf(0.0) - 1; } else { lastResourceToAllocate = myArray.Length; } } double TR = RCC.Sum(); double notYetAllocated = miscResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); bool cutoffReached = (notYetAllocated / TR) < ip.MISCPOOLSIZE; for (int k = 0; (k < lastResourceToAllocate) && !cutoffReached; ++k) { var q = myArray[k]; if (q.correl >= ip.CC) { B[q.BigPoolNum].Add(q.smallRes); miscResources.Remove(q.smallRes); } else { break; } notYetAllocated = miscResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); cutoffReached = (notYetAllocated / TR) < ip.MISCPOOLSIZE; } // Check if there is anything left in miscResources // If yes, throw it in the miscellaneous pool (B.Last()). if (miscResources.Count > 0) { B.Last().AddRange(miscResources); } // If not, remove the last allocated resource (myArray.Last()) // from the pool to which it was allocated, and place it in the // miscellaneous pool. else { var q = myArray.Last(); B[q.BigPoolNum].Remove(q.smallRes); B.Last().Add(q.smallRes); } #endregion } // p == 2: // Seed each of the (a-1) cost pools with the largest resources. // Allocate the remaining resources to the (a-1) pools at random. // However, ensure that enough resources are in the last pool. // The fraction of resources in the last pool is MISCPOOLSIZE. else if (p == 2) { #region Flowchart 6.3 double TR = RCC.Sum(); // Magnitude of resources not yet allocated double notYetAllocated = miscResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); // Fraction of resources not yet allocated double miscPoolPrct = notYetAllocated / TR; // Logic: Check if the fraction of resources in // miscResources is greater than the cap (ip.MISCPOOLSIZE). // If yes, take the first resource from miscResources // and put it in one of the big pools, chosen at random. // If the fraction of resources in miscResources is still // greater than the cap, repeat the loop. Otherwise, // stop and put the remaining resources in the last pool. // // Also stop under the following condition. Assume the head // of the miscResources list is allocated. Is the value of the // remaining resources in miscResources (the tail) greater than // zero? If not, stop. There has to be at least one non-zero // resource in the last pool. while ( (miscPoolPrct > ip.MISCPOOLSIZE) && (miscResources.Skip(1).Aggregate(0.0, (acc, indx) => acc + RCC[indx]) > 0.0) ) { // Pick a pool at random to get the next resource int poolIndx = (int)GenRandNumbers.GenUniformInt(0, a - 2); B[poolIndx].Add(miscResources.First()); miscResources.RemoveAt(0); notYetAllocated = miscResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); miscPoolPrct = notYetAllocated / TR; } B.Last().AddRange(miscResources); #endregion } // p == 3: // Seed the first pool with the largest resource. // Iterate over the other pools. For each pool, select a seed resource: // This is the largest of the remaining, unassigned resources, and // assign it to the pool. // Form a correlation vector (a list), which is the correlation // of each resource in remainingResources with the seed resource. // If the highest correlation is greater than ip.CC, there are // enough remaining resources to fill the remaining pools, and // satisfy the constraint about the miscellaneous pool size, //assign resource with the highest correlation to the current pool. // Once there are just as many resources remaining as there are pools, // assign one resource to each remaining pool. else if (p == 3) { #region Flowchart 6.4 // Initialize B for (int i = 0; i < B.Length; ++i) { B[i] = new List <int>(); } // Seed the first pool with the largest resource B[0].Add(RANK[0]); List <int> remainingResources = RANK.Skip(1).ToList(); // Assign all zero resources to the last (miscellaneous) pool. // That way, each of the remaining pools is guaranteed to have // a nonzero resource. // This only works if there are at least as many nonzero resources // as there are pools. If not, then skip this step so that each // pool has at least one resource. int numZeroResources = remainingResources.Count(res => RCC[res] == 0.0); if (RCC.Dimension - numZeroResources >= B.Length) { while (RCC[remainingResources.Last()] == 0.0) { B.Last().Add(remainingResources.Last()); remainingResources.RemoveAt(remainingResources.Count - 1); } } // Iterate over the pools. For each pool, select a seed resource, // which is the first resource assigned to the pool. // Form a correlation vector (a list), which is the correlation // of each resource in remainingResources with the seed resource. // While max of the list is greater than ip.CC, and while // the other conditions are satisfied, assign resource with the // maximum correlation to the current pool. // Once condition 2 is no longer true, there are just as many // resources remaining as there are pools. The loop then assigns // one resource to each remaining pool. // Once condition 3 is no longer true, it assigns one resource // to each pool, and all the remaining resources to the last pool. for (int currentPool = 0; currentPool < B.Length - 1; ++currentPool) { int seedResource = B[currentPool].First(); int poolsToBeFilled = B.Length - (currentPool + 1); List <double> correlations = remainingResources.Select(res => CORR[res, seedResource]).ToList(); bool cond1 = correlations.Max() > ip.CC; bool cond2 = remainingResources.Count > poolsToBeFilled; // Magnitude of resources not yet allocated double notYetAllocated = remainingResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); // Fraction of resources not yet allocated double TR = RCC.Sum(); double miscPoolPrct = notYetAllocated / TR; bool cond3 = miscPoolPrct > ip.MISCPOOLSIZE; while (cond1 && cond2 && cond3) { // Find the index of the resource with the maximum correlation // with the seed resource double maxCorr = correlations.Max(); int maxCorrIndx = remainingResources[correlations.IndexOf(maxCorr)]; // Add it to the current pool B[currentPool].Add(maxCorrIndx); // Remove it from the remainingResources list remainingResources.RemoveAt(correlations.IndexOf(maxCorr)); correlations.Remove(maxCorr); // Recompute loop termination conditions cond1 = correlations.Max() > ip.CC; cond2 = remainingResources.Count > poolsToBeFilled; notYetAllocated = remainingResources.Aggregate(0.0, (acc, indx) => acc + RCC[indx]); miscPoolPrct = notYetAllocated / TR; cond3 = miscPoolPrct > ip.MISCPOOLSIZE; } B[currentPool + 1].Add(remainingResources[0]); remainingResources.RemoveAt(0); } B.Last().AddRange(remainingResources); #endregion } else { throw new ApplicationException("Invalid value of p."); } } else { #region Flowchart 6.5 B = new List <int>[] { new List <int>(RANK) }; #endregion } // The fraction of RCC that is in the miscellaneous (last) // activity cost pool. double miscPoolSize = B.Last().Aggregate(0.0, (acc, i) => acc + RCC[i]) / RCC.Sum(); #region Flowchart 6.5 -- Choosing drivers // For each element of B, which is a list of resource indexes, // sort it in descending order by pool size (RCC[element]). // Technically, this is unnecessary, since elements should have // been added to the lists in B in descending order. But instead // of assuming that, since that could change in the future, // I am going to re-sort. Heck, it's only one line of code, // plus this essay of a comment that I just wrote. { var query = B.Select(list => list.OrderByDescending(indx => RCC[indx])); int numToTake; if (r == 0) { numToTake = 1; } else if (r == 1) { numToTake = ip.NUM; } else { throw new ApplicationException("Invalid value of r in FalseSys.cs."); } // This iterates over every list in query, and replaces that list // with a list containing only the first numToTake elements. var drivers = query.Select(list => list.Take(numToTake).ToList()); D = drivers.ToArray(); } #endregion }
/// <summary> /// Randomly generates a firm object (production technology and output market parameters). /// </summary> /// <param name="ip">A pointer to the collection of input parameters.</param> /// <param name="FirmID">Unique identifier for this firm (run number)</param> public Firm(InputParameters ip, int FirmID) { // Choose random values for DISP2 (the top DISP1 resources // account for DISP2 percent of total resource costs), and // density (sparsity) of resource consumption pattern matrix this.g = GenRandNumbers.GenUniformDbl(ip.DISP2_MIN, ip.DISP2_MAX); this.d = GenRandNumbers.GenUniformDbl(ip.DNS_MIN, ip.DNS_MAX); // Generate the true product margins and the true, optimal // decision vector. Keep generating new margins until there // is at least one product in the optimal mix. RowVector MAR, DECT0; do { MAR = this.GenMargins(ip); DECT0 = MAR.Map(x => (x < 1.0) ? 0.0 : 1.0); } while (DECT0.TrueForAll(x => x == 0.0)); // Generate vector of maximum production quantities this.mxq = this.GenMXQ(ip); // And associated vector of optimal production quantities ColumnVector QT = mxq.ewMultiply(DECT0); // Flowchart 5.1 - Create resource consumption pattern matrix this.res_cons_pat = GenResConsPat(ip); // Flowchart 5.2 - Compute TRU // Calculate vector of total units of resource // consumption, by product ColumnVector TRU = this.CalcResConsumption(QT); // Flowchart 5.3 - Compute MAXRU // Calculate resource consumption under the assumption // that all products are produced at maximum quantity ColumnVector MAXRU = this.CalcResConsumption(mxq); RowVector RCC, PC_B, RCCN; double TCT0; #region Flowchart 5.4 - Generate RCC, RCU, and RCCN /* -------------------------------- */ // Flowchart 5.4(a)-(g) // Generate vector of total resource costs (RCC) RCC = GenRCC(ip); /* -------------------------------- */ // Flowchart 5.4(h) // Now generate unit resource costs (RCU) by doing element-wise // division of RCC by MAXRU this.rcu = RCC.Map((x, i) => x / MAXRU[i]); /* -------------------------------- */ // Flowchart 5.4(i) // Compute new RCC vector (RCCN) based on unit resource // costs (RCU) and true unit resource consumption (TRU) RCCN = this.rcu.ewMultiply(TRU); // Check to see if the first resource (RCCN[0]) is the largest. // If not, increase RCU[0] by just enough to make it so. if (RCCN[0] < RCCN.Skip(1).Max() + 1) { RCCN[0] = Math.Ceiling(RCCN.Max()) + 1.0; this.rcu[0] = RCCN[0] / TRU[0]; } #endregion // Flowchart 5.5 - Calculate PC_B // Calculate true unit product costs PC_B = this.CalcTrueProductCosts(); // Flowchart 5.6 - Compute total costs TCT0 // Compute total costs TCT0 = this.CalcTotCosts(QT); // Flowchart 5.7 - Rename RCCN to RCC RCC = RCCN; initial_rcc = RCC; #region Flowchart 5.8 - Calculate SP, TRV0, PROFITT0 // Calculate product selling prices, total revenue, and profit this.sp = PC_B.ewMultiply(MAR); double TRV0 = this.sp * QT; this.profitt0 = TRV0 - TCT0; #endregion // 5.9(a) Create RANK vector // Note: this method provides a stable sort. It's important to use a stable sort. // LOOKUP IN VERSION.TXT WHY IT'S IMPORTANT TO USE A STABLE SORT HERE. initial_rank = Enumerable.Range(0, RCC.Dimension).OrderByDescending(i => RCC[i]).ToArray(); #region Flowchart 5.9(b) - Create RES_CONS_PAT_PRCT this.res_cons_pat_prct = new RectangularMatrix(ip.RCP, ip.CO); for (int r = 0; r < this.res_cons_pat.RowCount; ++r) { RowVector rv = this.res_cons_pat.Row(r); if (TRU[r] != 0.0) { rv = rv.Map((alt_ij, col) => alt_ij * QT[col] / TRU[r]); if (Math.Abs(rv.Sum() - 1.0) > 0.01) { throw new ApplicationException("Sum of row of RES_CONS_PAT_PRCT not equal to 1."); } } else { rv = rv.Map(alt_ij => 0.0); } this.res_cons_pat_prct.CopyRowInto(rv, r); } #endregion #region Flowchart 5.9(c) - Create correlation matrix // Create correlation matrix for rows of RES_CONS_PAT_PRCT MultivariateSample mvs = new MultivariateSample(ip.RCP); for (int c = 0; c < this.res_cons_pat_prct.ColumnCount; ++c) { mvs.Add(this.res_cons_pat_prct.Column(c)); } this.pearsoncorr = new SymmetricMatrix(ip.RCP); for (int i = 0; i < mvs.Dimension; ++i) { for (int j = i; j < mvs.Dimension; ++j) { //PearsonCorr[i, j] = mvs.PearsonRTest( i, j ).Statistic; this.pearsoncorr[i, j] = mvs.TwoColumns(i, j).PearsonRTest().Statistic; } } #endregion // Flowchart 5.10 - Logging true system // Note: I'm deliberately passing copies of the fields MXQ, SP, etc. Output.LogFirm( ip, this, FirmID, MAR, DECT0, TRV0, TCT0, profitt0, RCC); }