src/de/pik/lagom/generic/Suppliers.java

00001 
00002 // Copyright 2010 by Carlo Jaeger, Antoine Mandel, Steffen Fuerst and European Climate Forum
00003 // Licensed under the Open Software License version 3.0
00004 // See the file "License-OSL-3.0.txt" in the distribution for more information
00005 // The License text can be also found under http://www.opensource.org/licenses/osl-3.0.php
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   }//end of class Probe
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   // this findSuppliers method is a performance bottleneck of the simulation, so the 
00121   // numFirmsToObserveTableCache is used to store the results of the multiplication of 
00122   // firmsToObserveQuotient with the actual number of firms in the sector.
00123   // For each different firmsToObserveQuotient a new FixedIntTable that store the results
00124   // is created and added to the Cache afterwards (as part of the Cache class).
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   // attention: modifications of the list do not modify the maxSupply and costOfTotalStock
00144   // counters, this must be done explicitly. 
00145   // @Steffen TODO: add a wrapper, in the case that this doesn't affect the performance too much
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   // These fields were added due to performance reasons, previously the values where calculated
00194   // in maxSupply(), averagePrice()
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     // prepare the cache for the pre-calculated tables
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     // check that the array size is big enough for the needed number of firms
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     // Add first the preferred firms to the list of suppliers provided they have not been
00258     // destructed in the meantime. The preferred firms 
00259     // are those with whom business was done in the preceding period. 
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     // observe other firms until the desired number has been observed.
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     // rearrange the array of selling Firms and update maxSupply, CostOfTotalStock and numFoundFirms.
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     //if supply price is too expensive, replace the most expensive supplier. 
00309     // This requires that there are available suppliers.
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         //addExtraSeller(lSupplier);
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     // while rationed, add a supplier. 
00338     //This Requires that there are available suppliers and that the suppliers list is not full. 
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     //assert (pAmount >=0);
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         // hacks to prevent rounding errors
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         //assert(lTradeQuantity>0);
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         // hacks to prevent rounding errors
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         //assert(lTradeQuantity>0);
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   // returns the index of the most expensive supplier 
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   // returns null if all suppliers have no inventory 
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   // returns the index of the least expensive available supplier 
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   // getter/setter methods
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 // EOF

Generated on Tue Sep 14 11:11:49 2010 for lagom_generiC by  doxygen 1.5.4