00001
00002
00003
00004
00005
00007
00008 package de.pik.lagom.generic;
00009
00010 import static de.pik.lagom.annotations.Initialization.CLUSTER;
00011 import static de.pik.lagom.annotations.Initialization.EQUAL;
00012 import static de.pik.lagom.annotations.Initialization.INDIVIDUAL;
00013 import static de.pik.lagom.annotations.Variability.CONSTANT;
00014 import static de.pik.lagom.annotations.Variability.PERIOD;
00015 import static de.pik.lagom.annotations.Variability.SIMULATION;
00016 import static de.pik.lagom.annotations.Variability.VOLATILE;
00017
00018 import java.util.Iterator;
00019 import java.util.LinkedList;
00020 import java.util.List;
00021
00022 import net.sf.oval.constraint.GreaterOrApproxZero;
00023 import net.sf.oval.guard.Guarded;
00024 import de.pik.lagom.annotations.Description;
00025 import de.pik.lagom.annotations.Initialization;
00026 import de.pik.lagom.annotations.Variability;
00027 import de.pik.lagom.toolbox.ArrayTools;
00028 import de.pik.lagom.toolbox.Cache;
00029 import de.pik.lagom.toolbox.FixedIntTable;
00030 import de.pik.lagom.toolbox.ListTools;
00031 import de.pik.lagom.toolbox.ProbeBase;
00032 import de.pik.lagom.toolbox.math.FloatMath;
00033 import de.pik.lagom.toolbox.math.RandomGenerator;
00034
00035
00040 @Guarded
00041 class Suppliers {
00047 enum SellerSet {
00048 INCLUDE_IMPORT,
00049 EXCLUDE_IMPORT
00050 }
00051
00052 public class Probe extends ProbeBase {
00053 @Description("The amount of good found when searching for suppliers from the given sector")
00054 @Variability(PERIOD)
00055 @Initialization(INDIVIDUAL)
00056 private double foundSupplyWithoutImport;
00057
00058 @Description("The average price of the good found when searching for suppliers from the " +
00059 "given sector")
00060 @Variability(PERIOD)
00061 @Initialization(INDIVIDUAL)
00062 private double averagePriceOfFoundSupplyWithoutImports;
00063
00064 @Override
00065 public String toString() {
00066 return "Suppliers for a good of sector: " + inputSector.getName();
00067 }
00068
00069 public double getFoundSupplyWithoutImport() {
00070 return foundSupplyWithoutImport;
00071 }
00072
00073 public double getAveragePriceOfFoundSupplyWithoutImports() {
00074 return averagePriceOfFoundSupplyWithoutImports;
00075 }
00076
00078 public void reportFoundSuppliersWithoutImports() {
00079 foundSupplyWithoutImport = maxSupply();
00080 averagePriceOfFoundSupplyWithoutImports = averagePrice();
00081 }
00082 }
00083
00094 private class NumFirmsToObserveTable {
00095 private final FixedIntTable table;
00096 class ValueCalculator implements FixedIntTable.ValueCalculator {
00097 private final double quotient;
00098 public ValueCalculator(double pFirmsToObserveQuotient) {
00099 quotient = pFirmsToObserveQuotient;
00100 }
00101 public int calc(int pKey) {
00102 if (quotient > 0.d) {
00103 return (int) Math.round(Math.max(1.d, quotient * pKey));
00104 } else {
00105 return 0;
00106 }
00107 }
00108 }
00109
00110 public NumFirmsToObserveTable(double pFirmsToObserveQuotient) {
00111 table = new FixedIntTable(new ValueCalculator(pFirmsToObserveQuotient),
00112 Sector.MAX_FIRMS_PER_SECTOR);
00113 }
00114
00115 public FixedIntTable getTable() {
00116 return table;
00117 }
00118 }
00119
00120
00121
00122
00123
00124
00125 static private Cache<Double, FixedIntTable> numFirmsToObserveTableCache = null;
00126
00127 @Description("A lookup table for the number of firms to observe, determined by the current" +
00128 " number of firms in the sector and the FirmsToObserveQuotient")
00129 @Variability(SIMULATION)
00130 @Initialization(EQUAL)
00131 private FixedIntTable numFirmsToObserveTable;
00132
00134 @Description("The sector under consideration in the supplier search.")
00135 @Variability(SIMULATION)
00136 @Initialization(CLUSTER)
00137 private Sector inputSector;
00138
00143
00144
00145
00146 @Description("The list of firms from the sector under consideration that have a non-empty " +
00147 "inventory when findSuppliers is called")
00148 @Variability(PERIOD)
00149 @Initialization(CLUSTER)
00150 private ISeller[] sellingFirms = new ISeller[100];
00151
00152 @Description("The firms with whom business has been made in the previous period.")
00153 @Variability(PERIOD)
00154 @Initialization(CLUSTER)
00155 private List<ISeller> preferredFirms = new LinkedList<ISeller>();
00156
00157
00158 private Double[] availableSupply = new Double[100];
00159
00160
00161 private List<ISeller> availableSuppliers= new LinkedList<ISeller>();
00162
00164 @Description("A counter for the number of selling firms already found")
00165 @Variability(VOLATILE)
00166 @Initialization(EQUAL)
00167 private int numFoundFirms = 0;
00168
00171 @Description("Quotient to compute the number of selling firms to observe from the current " +
00172 "number of firms in the sector")
00173 @Variability(SIMULATION)
00174 @Initialization(CLUSTER)
00175 private double firmsToObserveQuotient;
00176
00178 @Description("The agent that is observing the firms")
00179 @Variability(VOLATILE)
00180 @Initialization(EQUAL)
00181 private IBuyer buyer;
00182
00183 @Description("The foundation of the model")
00184 @Variability(CONSTANT)
00185 @Initialization(EQUAL)
00186 private Foundation foundation;
00187
00188 @Description("Probe of the Suppliers instance, outputs information to the UI")
00189 @Variability(VOLATILE)
00190 @Initialization(INDIVIDUAL)
00191 private final Suppliers.Probe probe = new Suppliers.Probe();
00192
00193
00194
00196 @Description("The sum of goods found in the inventories of the selling firms found when " +
00197 "searching for suppliers")
00198 @Variability(VOLATILE)
00199 @Initialization(INDIVIDUAL)
00200 private double maxSupply=0;
00201
00206 @Description("The money that would be needed for buying the inventory of the selling firms" +
00207 " found")
00208 @Variability(VOLATILE)
00209 @Initialization(INDIVIDUAL)
00210 private double costOfTotalStock=0;
00211
00215 private RandomGenerator random = null;
00216
00217
00219 void init(Foundation pFoundation, IBuyer pBuyer, Sector pSector, double pFirmsToObserveQuotient) {
00220 foundation = pFoundation;
00221 buyer = pBuyer;
00222 inputSector = pSector;
00223 firmsToObserveQuotient = pFirmsToObserveQuotient;
00224 random = Foundation.getRandomGenerator();
00225
00226
00227
00228 if (numFirmsToObserveTableCache == null) {
00229 numFirmsToObserveTableCache = new Cache<Double, FixedIntTable>(new Cache.ValueCalculator<Double, FixedIntTable>() {
00230 public FixedIntTable calc(Double pKey) {
00231 return new NumFirmsToObserveTable(pKey).getTable();
00232 }
00233 });
00234 }
00235
00236 numFirmsToObserveTable = numFirmsToObserveTableCache.get(firmsToObserveQuotient);
00237 }
00238
00245 void findSuppliers(SellerSet pSellerSet) {
00246 int lNumRemainingFirmsToObserve = numFirmsToObserveTable.valueOf(inputSector.getNumFirms());
00247
00248
00249 if (sellingFirms.length < lNumRemainingFirmsToObserve + 1) {
00250 sellingFirms = new ISeller[(lNumRemainingFirmsToObserve + 1) * 2];
00251 }
00252
00253 numFoundFirms = 0;
00254 maxSupply = 0.d ;
00255 costOfTotalStock = 0.d;
00256
00257
00258
00259
00260 for (ISeller lSeller : preferredFirms) {
00261 if (inputSector.getFirmList().contains(lSeller) == true &&
00262 lSeller.getInventory() > 0) {
00263 addSeller(lSeller);
00264 lNumRemainingFirmsToObserve--;
00265 }
00266 }
00267
00268
00269 final Iterator<Firm> iterFirmsToObserved = inputSector.getFirmsWithInventory().iterator();
00270 while (iterFirmsToObserved.hasNext() && (lNumRemainingFirmsToObserve > 0)) {
00271 final Firm lFirm = iterFirmsToObserved.next();
00272 addSeller(lFirm);
00273 lNumRemainingFirmsToObserve--;
00274 }
00275
00276 probe.reportFoundSuppliersWithoutImports();
00277
00278 if (pSellerSet == SellerSet.INCLUDE_IMPORT &&
00279 inputSector.getImportExport().getInventory() > 0) {
00280 addSeller(inputSector.getImportExport());
00281 }
00282 }
00283
00289 void updateSuppliers(SellerSet pSellerSet) {
00290
00291 ISeller[] lOldSellingFirms = new ISeller[100];
00292 ArrayTools.deepCopyFromTo(sellingFirms, lOldSellingFirms);
00293 final int lOldNumFoundFirms= numFoundFirms;
00294
00295 maxSupply = 0.d ;
00296 costOfTotalStock = 0.d;
00297 numFoundFirms =0;
00298 maxSupply();
00299 for (int iSeller=0; iSeller<lOldNumFoundFirms; iSeller++) {
00300 final ISeller lSeller= lOldSellingFirms[iSeller];
00301 if (inputSector.getFirmList().contains(lSeller) == true && lSeller.getBAUSupply()>0) {
00302 addSeller(lSeller);
00303 }
00304 }
00305
00306 updateAvailableSuppliers();
00307
00308
00309
00310
00311 if (areAvailableSuppliers()&& numFoundFirms>0){
00312 ISeller lSupplier =null;
00313 int maxTries= (int) Math.ceil(buyer.getObservedSuppliersQuotient()*availableSuppliers.size());
00314 int nIter=0;
00315 while ( areAvailableSuppliers()&& canExtraSuppliers() && (nIter<=maxTries) &&
00316 getLeastExpensiveAvailableSupplier().getPrice()>sellingFirms[getMostExpensiveSupplier()].getPrice()){
00317 lSupplier = getLeastExpensiveAvailableSupplier();
00318
00319 replaceSeller(getMostExpensiveSupplier(),lSupplier);
00320 nIter++;
00321 }
00322
00323 }
00324
00325
00326 assert(FloatMath.greaterOrApproxZero(maxSupply()));
00327 }
00328
00336 void addSuppliers(double pDemand, SellerSet pSellerSet) {
00337
00338
00339 updateAvailableSuppliers();
00340 while (FloatMath.lowerOrApproxEqual(maxSupply, pDemand) && areAvailableSuppliers() && canExtraSuppliers()){
00341 addExtraSeller(getLeastExpensiveAvailableSupplier());
00342 }
00343 probe.reportFoundSuppliersWithoutImports();
00344
00345 if (pSellerSet == SellerSet.INCLUDE_IMPORT &&
00346 inputSector.getImportExport().getInventory() > 0) {
00347 addSeller(inputSector.getImportExport());
00348 }
00349 assert(FloatMath.greaterOrApproxZero(maxSupply()));
00350
00351 for (int iSupplier=0; iSupplier<numFoundFirms; iSupplier++){
00352 assert(availableSupply[iSupplier]<=sellingFirms[iSupplier].getBAUSupply());
00353
00354 }
00355 }
00356
00357
00358
00359
00366 public void addSeller(ISeller pSeller) {
00367 assert(pSeller.getProductionSector() == inputSector);
00368 sellingFirms[numFoundFirms] = pSeller;
00369 final double lSupply = pSeller.getBAUSupply();
00370 assert (lSupply>=0);
00371 availableSupply[numFoundFirms]=lSupply;
00372 maxSupply += lSupply;
00373 numFoundFirms++;
00374 maxSupply();
00375 costOfTotalStock += lSupply * pSeller.getPrice();
00376 availableSuppliers.remove(pSeller);
00377 }
00378
00385 public void addExtraSeller(ISeller pSeller) {
00386 assert(pSeller.getProductionSector() == inputSector);
00387 assert(pSeller.getExtraSupply() > 0);
00388 sellingFirms[numFoundFirms] = pSeller;
00389 final double lSupply = pSeller.getExtraSupply();
00390 assert (lSupply<=pSeller.getInventory());
00391 availableSupply[numFoundFirms]=lSupply;
00392 assert(availableSupply[numFoundFirms]<=pSeller.getExtraSupply());
00393 maxSupply += lSupply;
00394 costOfTotalStock += lSupply * pSeller.getPrice();
00395 availableSuppliers.remove(pSeller);
00396 numFoundFirms++;
00397 }
00398
00404 public void replaceSeller(int pIndex, ISeller pSeller) {
00405 final ISeller lOldSeller= sellingFirms[pIndex];
00406 final double lOldMaxSupply=maxSupply;
00407 final double lOldSupply = lOldSeller.getBAUSupply();
00408 assert (lOldSupply==availableSupply[pIndex]);
00409 final double lNewSupply = pSeller.getExtraSupply();
00410 final double lOldPrice = lOldSeller.getPrice();
00411 final double lNewPrice = pSeller.getPrice();
00412 sellingFirms[pIndex] = pSeller;
00413 availableSupply[pIndex]=lNewSupply;
00414 assert(availableSupply[pIndex]<=sellingFirms[pIndex].getInventory());
00415 maxSupply += lNewSupply-lOldSupply;
00416 costOfTotalStock += lNewSupply*lNewPrice-lOldSupply*lOldPrice;
00417 availableSuppliers.remove(pSeller);
00418 availableSuppliers.add(lOldSeller);
00419
00420 }
00421
00422 void updateAvailableSuppliers(){
00423 availableSuppliers.clear();
00424 final Iterator<Firm> iterFirmsToObserved = inputSector.getFirmsWithExtraSupply().iterator();
00425 while (iterFirmsToObserved.hasNext()){
00426 final Firm lFirm = iterFirmsToObserved.next();
00427 double lSupply =lFirm.getExtraSupply();
00428 assert(lSupply>0);
00429 availableSuppliers.add(lFirm);
00430 }
00431 for (int iSeller=0; iSeller< numFoundFirms ; iSeller++) {
00432 availableSuppliers.remove(sellingFirms[iSeller]);
00433 }
00434 int maxFirmsToObserve=(int)Math.ceil(firmsToObserveQuotient*inputSector.getNumFirms());
00435 assert (maxFirmsToObserve>0);
00436 while (availableSuppliers.size()> maxFirmsToObserve){
00437 int lIndex= random.nextInt(availableSuppliers.size());
00438 availableSuppliers.remove(lIndex);
00439 }
00440 }
00441
00442
00443 ISeller drawNewSupplier(){
00444 return availableSuppliers.get(random.nextInt(availableSuppliers.size()));
00445 }
00446
00447 Boolean areAvailableSuppliers(){
00448 return (availableSuppliers.size()>0);
00449 }
00450
00451 Boolean canExtraSuppliers(){
00452 return (numFoundFirms<sellingFirms.length-1);
00453 }
00454
00455
00456
00457
00464 @GreaterOrApproxZero
00465 double maxSupply() {
00466 double lSum=0;
00467 for(int iIndex=0;iIndex <numFoundFirms; iIndex++){
00468 lSum+= availableSupply[iIndex];
00469 }
00470 assert (lSum>=0);
00471 return maxSupply;
00472 }
00473
00480 @GreaterOrApproxZero
00481 double costOfTotalStock() {
00482 return costOfTotalStock;
00483 }
00484
00496 @GreaterOrApproxZero
00497 double buy(double pAmount) {
00498
00499 assert (FloatMath.lowerOrApproxEqual(pAmount, maxSupply()));
00500 double lSumCost = 0d;
00501
00502 int iSupplier;
00503 int nIter=0;
00504 while (!FloatMath.approxZero(pAmount, 1e-10) &&
00505 (sellingFirms[iSupplier= getCheapestSupplierWithAvailableSupply()] != null) && nIter<=numFoundFirms) {
00506 ISeller lSupplier= sellingFirms[iSupplier];
00507
00508 double lTradeQuantity = Math.min(maxSupply(),Math.min(pAmount,availableSupply[iSupplier]));
00509 assert(lTradeQuantity>=0);
00510 lSupplier.sell(lTradeQuantity);
00511 availableSupply[iSupplier]-= lTradeQuantity;
00512 maxSupply -= lTradeQuantity;
00513 lSumCost += lTradeQuantity * lSupplier.getPrice();
00514 pAmount -= lTradeQuantity;
00515
00516 foundation.getTradeTrace().reportTrade(lTradeQuantity, buyer, lSupplier);
00517 nIter++;
00518 }
00519
00520 costOfTotalStock -= lSumCost;
00521 assert(maxSupply>=0);
00522
00523 return lSumCost;
00524 }
00525
00537 @GreaterOrApproxZero
00538 double buyValue(double pValue) {
00539 assert (FloatMath.greaterOrApproxZero(pValue));
00540 assert (FloatMath.lowerOrApproxEqual(pValue, costOfTotalStock()));
00541 double lSumAmount = 0d;
00542 double lTax= foundation.getGovernment().getCarbonTaxRate(inputSector.getArrayIndex());
00543
00544 int iSupplier;
00545 int nIter=0;
00546 while (!FloatMath.approxZero(pValue, 1e-10) &&
00547 (sellingFirms[iSupplier= getCheapestSupplierWithAvailableSupply()] != null)&& nIter<=numFoundFirms) {
00548 ISeller lSupplier= sellingFirms[iSupplier];
00549 double lPrice = (1+lTax)*lSupplier.getPrice();
00550 final double lTradeValue = Math.min(pValue, availableSupply[iSupplier]*lPrice);
00551
00552 final double lTradeQuantity=Math.min(maxSupply(),Math.min(lTradeValue/lPrice,availableSupply[iSupplier]));
00553 lSupplier.sell(lTradeQuantity);
00554 availableSupply[iSupplier]-= lTradeQuantity;
00555 maxSupply -= lTradeQuantity;
00556 lSumAmount+=lTradeQuantity;
00557 pValue -= lTradeValue;
00558
00559 foundation.getTradeTrace().reportTrade(lTradeQuantity, buyer, lSupplier);
00560 foundation.getGovernment().collectTax(lTax*lTradeQuantity);
00561 nIter++;
00562 }
00563 assert(FloatMath.greaterOrApproxZero(maxSupply));
00564
00565 return lSumAmount;
00566 }
00567
00568
00579
00580 private int getMostExpensiveSupplier() {
00581 int iMost = 0;
00582 for (int iSupplier = 0; iSupplier < numFoundFirms; iSupplier++) {
00583 final ISeller lSupplier = sellingFirms[iSupplier];
00584 if (lSupplier.getPrice() > sellingFirms[iMost].getPrice()) {
00585 iMost = iSupplier;
00586 }
00587 }
00588 return iMost;
00589 }
00590
00591
00592 private int getCheapestSupplierWithAvailableSupply() {
00593 int iLeast = 0;
00594 for (int iSupplier = 0; iSupplier < numFoundFirms; iSupplier++) {
00595 final ISeller lSupplier = sellingFirms[iSupplier];
00596 if (availableSupply[iSupplier] > 0.d) {
00597 if (lSupplier.getPrice() < sellingFirms[iLeast].getPrice() || availableSupply[iLeast]==0) {
00598 iLeast = iSupplier;
00599 }
00600 }
00601 }
00602 return iLeast;
00603 }
00604
00605
00606
00607 private ISeller getLeastExpensiveAvailableSupplier() {
00608 ISeller lLeast = availableSuppliers.get(0);
00609 for (final ISeller lSupplier: availableSuppliers) {
00610 if (lSupplier.getPrice() < lLeast.getPrice()) {
00611 lLeast = lSupplier;
00612 }
00613 }
00614 return lLeast;
00615 }
00616
00623 @GreaterOrApproxZero
00624 double averagePrice() {
00625 double lTax= foundation.getGovernment().getCarbonTaxRate(inputSector.getArrayIndex());
00626 if (! FloatMath.lowerOrApproxZero(maxSupply) ) {
00627 return (1+lTax)*costOfTotalStock / maxSupply;
00628 } else {
00629 return inputSector.getStats().getAverageTradePrice();
00630 }
00631 }
00632
00633
00634 public Suppliers.Probe getProbe() {
00635 return probe;
00636 }
00637
00638 Sector getInputSector() {
00639 return inputSector;
00640 }
00641
00642 public int getNumSuppliers(){
00643 return numFoundFirms;
00644 }
00645
00646 public int getNumFoundSuppliers() {
00647 return numFoundFirms;
00648 }
00649
00650 public List<ISeller> getAvailableSuppliers() {
00651 return availableSuppliers;
00652 }
00653 }
00654
00656