model.experiments.stickyprices.StickyPricesCSVPrinter.java Source code

Java tutorial

Introduction

Here is the source code for model.experiments.stickyprices.StickyPricesCSVPrinter.java

Source

/*
 * Copyright (c) 2014 by Ernesto Carrella
 * Licensed under MIT license. Basically do what you want with it but cite me and don't sue me. Which is just politeness, really.
 * See the file "LICENSE" for more information
 */

package model.experiments.stickyprices;

import agents.firm.Firm;
import agents.firm.personell.HumanResources;
import agents.firm.production.Blueprint;
import agents.firm.production.control.PlantControl;
import agents.firm.production.control.facades.MarginalPlantControl;
import agents.firm.production.control.maximizer.PeriodicMaximizer;
import agents.firm.production.control.maximizer.WorkforceMaximizer;
import agents.firm.production.control.maximizer.algorithms.marginalMaximizers.MarginalMaximizer;
import agents.firm.purchases.PurchasesDepartment;
import agents.firm.purchases.prediction.FixedIncreasePurchasesPredictor;
import agents.firm.sales.SalesDepartment;
import agents.firm.sales.SalesDepartmentOneAtATime;
import agents.firm.sales.prediction.ErrorCorrectingSalesPredictor;
import agents.firm.sales.prediction.FixedDecreaseSalesPredictor;
import agents.firm.sales.prediction.RecursiveSalePredictor;
import agents.firm.sales.prediction.SalesPredictor;
import agents.firm.sales.pricing.AskPricingStrategy;
import agents.firm.sales.pricing.pid.InventoryBufferSalesControl;
import agents.firm.sales.pricing.pid.SimpleFlowSellerPID;
import agents.firm.utilities.LastClosingPriceEcho;
import au.com.bytecode.opencsv.CSVWriter;
import com.google.common.base.Preconditions;
import financial.market.Market;
import goods.UndifferentiatedGoodType;
import model.MacroII;
import model.experiments.tuningRuns.MarginalMaximizerPIDTuning;
import model.scenario.*;
import model.utilities.logs.LogLevel;
import model.utilities.logs.LogToFile;
import model.utilities.pid.Controller;
import model.utilities.pid.PIDController;
import model.utilities.pid.decorator.PIDStickinessHillClimberTuner;
import model.utilities.pid.decorator.PIDStickinessSalesTuner;
import model.utilities.stats.collectors.DailyStatCollector;
import model.utilities.stats.collectors.enums.MarketDataType;
import model.utilities.stats.collectors.enums.SalesDataType;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;

import static model.experiments.tuningRuns.MarginalMaximizerPIDTuning.printProgressBar;

/**
 * <h4>Description</h4>
 * <p/> All csvs needed for paper 2 are here
 * <p/>
 * <p/>
 * <h4>Notes</h4>
 * Created with IntelliJ
 * <p/>
 * <p/>
 * <h4>References</h4>
 *
 * @author carrknight
 * @version 2014-02-10
 * @see
 */
public class StickyPricesCSVPrinter {

    private static final int DEFAULT_STICKINESS = 50;

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        //set defaults
        //create directory
        Files.createDirectories(Paths.get("runs", "rawdata"));

        System.out.println("SELLERS");
        System.out.println("figure 1 to 5");
        simpleSellerRuns();
        System.out.println("figure 6-7");
        simpleDelaySweep(50, 50, 50, 5);

        System.out.println("ONE SECTOR");
        System.out.println("figure 8");
        sampleMonopolistRunLearned(0, 101, 1, 1, 14, .1f, .1f, "sampleMonopolist.csv");
        System.out.println("figure 9");

        sampleCompetitiveRunLearned(0, 101, 1, 1, 14, .1f, .1f, "sampleCompetitive.csv");

        System.out.println("figure 10");

        woodMonopolistSweep(new BigDecimal("0.00"), new BigDecimal("3"), new BigDecimal("0.00"),
                new BigDecimal("3"), new BigDecimal(".01"), 1);

        System.out.println("SUPPLY CHAIN");
        System.out.println("figure 11");
        badlyOptimizedNoInventorySupplyChain(0, 0f, 2f, 0,
                Paths.get("runs", "rawdata", "badlyOptimized.csv").toFile(), false);
        System.out.println("figure 12");
        badlyOptimizedNoInventorySupplyChain(0, 0f, .2f, 0,
                Paths.get("runs", "rawdata", "timidBadlyOptimized.csv").toFile(), false);
        System.out.println("figure 13");
        badlyOptimizedNoInventorySupplyChain(0, 0f, .2f, 100,
                Paths.get("runs", "rawdata", "stickyBadlyOptimized.csv").toFile(), false);

        System.out.println("figure 14");
        woodMonopolistSupplyChainSweep();

        System.out.println("Market Structure");
        System.out.println("figure 15-16-17");

        oneHundredAllLearnedRuns(Paths.get("runs", "rawdata", "learnedInventoryChain100.csv").toFile(), null, null);
        oneHundredAllLearnedCompetitiveRuns(
                Paths.get("runs", "rawdata", "learnedCompetitiveInventoryChain100.csv").toFile());
        oneHundredAllLearnedFoodRuns(Paths.get("runs", "rawdata", "learnedInventoryFoodChain100.csv").toFile());

        System.out.println("figure 18-19");
        oneHundredLearningMonopolist(Paths.get("runs", "rawdata", "100Monopolists.csv").toFile());
        oneHundredLearningCompetitive(Paths.get("runs", "rawdata", "100Competitive.csv").toFile());

        System.out.println("figure 20-21-22");
        oneHundredAllLearningRuns(Paths.get("runs", "rawdata", "learningInventoryChain100.csv").toFile(), null,
                null);
        oneHundredAllLearningCompetitiveRuns(
                Paths.get("runs", "rawdata", "learningCompetitiveInventoryChain100.csv").toFile());
        oneHundredAllLearningFoodRuns(Paths.get("runs", "rawdata", "learningInventoryFoodChain100.csv").toFile());

        System.out.println("figure 23");
        badlyOptimizedNoInventorySupplyChain(1, 0f, 0.2f, 0,
                Paths.get("runs", "rawdata", "tuningTrial.csv").toFile(), true);

    }

    //isolate PID seller, its issues with delays and simple ways around it
    private static void simpleSellerRuns() {

        //figure 1
        runWithoutDelay();
        System.out.println("done");
        //figure 2
        runWithDelay(10, 0, 1, true, false, 100);
        System.out.println("done");
        //figure 3
        runWithDelay(20, 0, 1, true, false, 100);
        System.out.println("done");
        //figure 4
        runWithDelay(20, 20, 1, true, false, 100);
        System.out.println("done");
        //figure 5
        runWithDelay(20, 0, 10, true, false, 100);

    }

    //simple seller run without delay.
    private static void runWithoutDelay() {
        MacroII macroII = new MacroII(100);
        SimpleSellerScenario scenario = new SimpleSellerScenario(macroII);
        macroII.setScenario(scenario);

        scenario.setDemandSlope(-1);
        scenario.setDemandIntercept(101);
        scenario.setDemandShifts(false);
        scenario.setSellerStrategy(SimpleFlowSellerPID.class); // no inventory
        scenario.setDestroyUnsoldInventoryEachDay(true);
        scenario.setNumberOfSellers(1);
        scenario.setInflowPerSeller(50);
        scenario.setBuyerDelay(0);
        //fix the pid parameters

        DailyStatCollector.addDailyStatCollectorToModel(Paths.get("runs", "rawdata", "simpleSeller.csv").toFile(),
                macroII);
        macroII.start();
        final SimpleFlowSellerPID askPricingStrategy = new SimpleFlowSellerPID(scenario.getDepartments().get(0),
                .1f, .1f, 0f, 0, scenario.getDepartments().get(0).getMarket(),
                scenario.getDepartments().get(0).getRandom().nextInt(100),
                scenario.getDepartments().get(0).getFirm().getModel());
        askPricingStrategy.setFlowTargeting(false);
        scenario.getDepartments().get(0).setAskPricingStrategy(askPricingStrategy);
        askPricingStrategy.setInitialPrice(80); //so the run is the same for all possible runs

        for (int i = 0; i < 15000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(15000, i, 20);
        }
    }

    //simple seller with delay
    private static SimpleSellerScenario runWithDelay(int buyerDelay, int pidSpeed, float dividePIParametersByThis,
            boolean writeToFile, boolean randomize, int seed) {
        Preconditions.checkArgument(pidSpeed >= 0);
        Preconditions.checkArgument(dividePIParametersByThis > 0);

        MacroII macroII;
        macroII = new MacroII(seed);

        SimpleSellerScenario scenario = new SimpleSellerScenario(macroII);
        macroII.setScenario(scenario);

        scenario.setDemandSlope(-1);
        scenario.setDemandIntercept(101);
        scenario.setDemandShifts(false);
        scenario.setSellerStrategy(SimpleFlowSellerPID.class); // no inventory
        scenario.setDestroyUnsoldInventoryEachDay(true);
        scenario.setNumberOfSellers(1);
        scenario.setInflowPerSeller(50);
        scenario.setBuyerDelay(buyerDelay);

        macroII.start();

        //the explicit cast has to be true because we set the strategy to be so earlier
        //change the PI values if needed!
        float proportionalAndIntegralGain = .1f / dividePIParametersByThis;
        final SimpleFlowSellerPID askPricingStrategy = new SimpleFlowSellerPID(scenario.getDepartments().get(0),
                proportionalAndIntegralGain, proportionalAndIntegralGain, 0f, pidSpeed,
                scenario.getDepartments().get(0).getMarket(),
                scenario.getDepartments().get(0).getRandom().nextInt(100),
                scenario.getDepartments().get(0).getFirm().getModel());
        askPricingStrategy.setFlowTargeting(false);

        if (!randomize)
            askPricingStrategy.setInitialPrice(80); //so the run is the same for all possible runs
        else
            askPricingStrategy.setInitialPrice(macroII.getRandom().nextInt(100));
        scenario.getDepartments().get(0).setAskPricingStrategy(askPricingStrategy);

        for (int i = 0; i < 15000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(15000, i, 20);
        }
        if (writeToFile) {
            //i use the sales department data as it shows the "offered" price rather than just the closing one
            final String filename = pidSpeed == 0 && dividePIParametersByThis == 1
                    ? "simpleSeller_withDelays" + buyerDelay + ".csv"
                    : "simpleSeller_demandDelay" + buyerDelay + "speed" + pidSpeed + "slowness"
                            + dividePIParametersByThis + ".csv";
            scenario.getDepartments().get(0).getData()
                    .writeToCSVFile(Paths.get("runs", "rawdata", filename).toFile());
        }

        macroII.finish();

        return scenario;

    }

    //one link supply chains with monopolists
    private static void beefMonopolistRuns() {
        //print out a simple run for all to see in my beautiful paper!

        long seed = 0;

        //all learned
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 100, 0, true, true,
                Paths.get("runs", "rawdata", "everybodyLearnedSlow_withInventory.csv").toFile(), null, null);
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 100, true, true,
                Paths.get("runs", "rawdata", "everybodyLearnedSticky_withInventory.csv").toFile(), null, null);
        //beef learned
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 100, 0, true, false,
                Paths.get("runs", "rawdata", "beefLearnedSlow_withInventory.csv").toFile(), null, null);
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 100, true, false,
                Paths.get("runs", "rawdata", "beefLearnedSticky_withInventory.csv").toFile(), null, null);
        //food learned
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 100, 0, false, true,
                Paths.get("runs", "rawdata", "foodLearnedSlow_withInventory.csv").toFile(), null, null);
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 100, false, true,
                Paths.get("runs", "rawdata", "foodLearnedSticky_withInventory.csv").toFile(), null, null);
        //learning
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 100, 0, false, false,
                Paths.get("runs", "rawdata", "learningSlow_withInventory.csv").toFile(), null, null);
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 100, false, false,
                Paths.get("runs", "rawdata", "learningSticky_withInventory.csv").toFile(), null, null);

        //non sticky
        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 0, true, true,
                Paths.get("runs", "rawdata", "nonsticky_withInventory.csv").toFile(), null, null);
    }

    //go through many possible combination of delaying PID to see their effects!
    private static void simpleDelaySweep(int maxDivider, int maxSpeed, int demandDelay, int experimentsPerSetup)
            throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(Paths.get("runs", "rawdata", "delaySweep.csv").toFile()));
        writer.writeNext(new String[] { "speed", "divider", "distance", "variance", "success" });

        for (int divider = 1; divider < maxDivider; divider++) {
            //for all speeds
            for (int speed = 0; speed < maxSpeed; speed++) {

                SummaryStatistics averageSquaredDistance = new SummaryStatistics();
                SummaryStatistics averageVariance = new SummaryStatistics();
                int successes = 0;

                for (int runNumber = 0; runNumber < experimentsPerSetup; runNumber++) {
                    float totalDistance = 0;
                    SummaryStatistics prices = new SummaryStatistics();

                    //runNumber!
                    final SimpleSellerScenario run = runWithDelay(demandDelay, speed, divider, false, true,
                            runNumber);

                    final double[] pricesInRun = run.getDepartments().get(0).getData()
                            .getObservationsRecordedTheseDays(SalesDataType.LAST_ASKED_PRICE, 0, 14999);
                    for (double price : pricesInRun) {
                        totalDistance += Math.pow(price - 51, 2);
                        prices.addValue(price);
                    }

                    averageSquaredDistance.addValue(Math.sqrt(totalDistance));
                    averageVariance.addValue(prices.getVariance());
                    if (pricesInRun[pricesInRun.length - 1] == 51)
                        successes++;
                }

                String[] csvLine = new String[5];
                csvLine[0] = String.valueOf(speed);
                csvLine[1] = String.valueOf(divider);
                csvLine[2] = String.valueOf(averageSquaredDistance.getMean());
                csvLine[3] = String.valueOf(averageVariance.getMean());
                csvLine[4] = String.valueOf(successes);
                writer.writeNext(csvLine);
                writer.flush();
                System.out.println(Arrays.toString(csvLine));
            }

        }

    }

    //go through many possible combination of PI parameters to show what is the "optimal" set when dealing with adapting demand
    //using big decimals because they are more precise (really pointless since then it's fed as a float, but whatever)
    private static void woodMonopolistSweep(final BigDecimal minimumP, final BigDecimal maximumP,
            final BigDecimal minimumI, final BigDecimal maximumI, final BigDecimal increment,
            final int runsPerParameterCombination) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(Paths.get("runs", "rawdata", "monoSweep.csv").toFile()));
        writer.writeNext(new String[] { "P", "I", "distance", "variance", "success" });

        BigDecimal currentP = minimumP;
        while (currentP.compareTo(maximumP) <= 0) {
            BigDecimal currentI = minimumI;

            while (currentI.compareTo(maximumI) <= 0) {

                SummaryStatistics averageSquaredDistance = new SummaryStatistics();
                SummaryStatistics averageVariance = new SummaryStatistics();
                int successes = 0;

                for (int run = 0; run < runsPerParameterCombination; run++) {

                    //create the run
                    MacroII macroII = new MacroII(run);
                    MonopolistScenario scenario = new MonopolistScenario(macroII);
                    macroII.setScenario(scenario);
                    //set the demand
                    scenario.setDemandIntercept(102);
                    scenario.setDemandSlope(2);
                    scenario.setDailyWageSlope(1);
                    scenario.setDailyWageIntercept(0);
                    scenario.setAskPricingStrategy(SimpleFlowSellerPID.class);
                    scenario.setWorkersToBeRehiredEveryDay(true);
                    scenario.setControlType(
                            MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);
                    scenario.setBuyerDelay(0);

                    //start it and have one step
                    macroII.start();
                    macroII.schedule.step(macroII);

                    //now set the right parameters
                    final SalesDepartment salesDepartment = scenario.getMonopolist()
                            .getSalesDepartment(UndifferentiatedGoodType.GENERIC);
                    final SimpleFlowSellerPID strategy = new SimpleFlowSellerPID(salesDepartment,
                            currentP.floatValue(), currentI.floatValue(), 0f, 0, salesDepartment.getMarket(),
                            salesDepartment.getRandom().nextInt(100), salesDepartment.getFirm().getModel());
                    //  strategy.setInitialPrice(102);
                    //start them all at the same price, otherwise you advantage the slow by being so slow initially that they end up being right later

                    salesDepartment.setAskPricingStrategy(strategy);

                    //and make it learned!
                    salesDepartment.setPredictorStrategy(new FixedDecreaseSalesPredictor(2));
                    final HumanResources hr = scenario.getMonopolist().getHRs().iterator().next();
                    hr.setPredictor(new FixedIncreasePurchasesPredictor(1));

                    float totalDistance = 0;
                    SummaryStatistics prices = new SummaryStatistics();
                    //run the model
                    double price = 0;
                    double quantity = 0;
                    for (int i = 0; i < 1000; i++) {
                        macroII.schedule.step(macroII);
                        price = strategy.getTargetPrice();
                        quantity = salesDepartment.getTodayInflow();
                        totalDistance += Math
                                .pow(Math.min(price - (102 - 2 * quantity), price - (102 - 2 * quantity - 1)), 2);
                        prices.addValue(price);
                    }

                    //Model over, now compute statistics

                    averageSquaredDistance.addValue(Math.sqrt(totalDistance));
                    averageVariance.addValue(prices.getVariance());
                    if (price <= 68 && price >= 67)
                        successes++;

                    //            System.out.println(salesDepartment.getLatestObservation(SalesDataType.LAST_ASKED_PRICE));
                    macroII.finish();

                }

                String[] csvLine = new String[5];
                csvLine[0] = currentP.toString();
                csvLine[1] = currentI.toString();
                csvLine[2] = String.valueOf(averageSquaredDistance.getMean());
                csvLine[3] = String.valueOf(averageVariance.getMean());
                csvLine[4] = String.valueOf(successes);
                writer.writeNext(csvLine);
                writer.flush();
                System.out.println(Arrays.toString(csvLine));

                currentI = currentI.add(increment).setScale(2);
                System.out.println();

            }

            currentP = currentP.add(increment).setScale(2);

        }

    }

    private static void sampleMonopolistRunLearned(int seed, int demandIntercept, int demandSlope, int wageSlope,
            int dailyWageIntercept, float proportionalGain, float integralGain, String filename) {

        //create the run
        MacroII macroII = new MacroII(seed);
        MonopolistScenario scenario = new MonopolistScenario(macroII);
        macroII.setScenario(scenario);
        //set the demand
        scenario.setDemandIntercept(demandIntercept);
        scenario.setDemandSlope(demandSlope);
        scenario.setDailyWageSlope(wageSlope);
        scenario.setDailyWageIntercept(dailyWageIntercept);
        scenario.setAskPricingStrategy(SimpleFlowSellerPID.class);
        scenario.setControlType(MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);

        //start it and have one step
        macroII.start();
        macroII.schedule.step(macroII);

        //now set the right parameters
        final SalesDepartment salesDepartment = scenario.getMonopolist()
                .getSalesDepartment(UndifferentiatedGoodType.GENERIC);
        final SimpleFlowSellerPID strategy = new SimpleFlowSellerPID(salesDepartment, proportionalGain,
                integralGain, 0f, 0, salesDepartment.getMarket(), salesDepartment.getRandom().nextInt(100),
                salesDepartment.getFirm().getModel());
        salesDepartment.setAskPricingStrategy(strategy);

        //and make it learned!
        salesDepartment.setPredictorStrategy(new FixedDecreaseSalesPredictor(demandSlope));
        scenario.getMonopolist().getHRs().iterator().next()
                .setPredictor(new FixedIncreasePurchasesPredictor(wageSlope));

        //run the model
        for (int i = 0; i < 5000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(5000, i, 100);
        }

        salesDepartment.getData().writeToCSVFile(Paths.get("runs", "rawdata", filename).toFile());

    }

    private static void sampleCompetitiveRunLearned(int seed, int demandIntercept, int demandSlope, int wageSlope,
            int dailyWageIntercept, float proportionalGain, float integralGain, String filename) {

        //create the run
        MacroII macroII = new MacroII(seed);
        TripolistScenario scenario = new TripolistScenario(macroII);
        macroII.setScenario(scenario);
        //set the demand
        scenario.setDemandIntercept(demandIntercept);
        scenario.setDemandSlope(demandSlope);
        scenario.setDailyWageSlope(wageSlope);
        scenario.setDailyWageIntercept(dailyWageIntercept);
        scenario.setAskPricingStrategy(SimpleFlowSellerPID.class);
        scenario.setControlType(MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);

        //start it and have one step
        macroII.start();
        macroII.schedule.step(macroII);

        //now set the right parameters
        for (final Firm firm : scenario.getCompetitors()) {
            final SalesDepartment salesDepartment = firm.getSalesDepartment(UndifferentiatedGoodType.GENERIC);
            final SimpleFlowSellerPID strategy = new SimpleFlowSellerPID(salesDepartment,
                    proportionalGain + (float) macroII.random.nextGaussian() / 100f,
                    integralGain + (float) macroII.random.nextGaussian() / 100f, 0f, 0, salesDepartment.getMarket(),
                    salesDepartment.getRandom().nextInt(100), salesDepartment.getFirm().getModel()); //added a bit of noise
            salesDepartment.setAskPricingStrategy(strategy);

            //all impacts are 0 because it's perfect competitive
            salesDepartment.setPredictorStrategy(new FixedDecreaseSalesPredictor(0));
            firm.getHRs().iterator().next().setPredictor(new FixedIncreasePurchasesPredictor(0));
        }

        //run the model
        for (int i = 0; i < 5000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(5000, i, 100);
        }

        macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                .writeToCSVFile(Paths.get("runs", "rawdata", filename).toFile());

    }

    //sweep over the period of maximization to see what happens if the frequency is too high or too low.
    private static void competitiveSweepRun(int additionalCompetitors) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(
                Paths.get("runs", "rawdata", "competitivePeriodSweep" + additionalCompetitors + ".csv").toFile()));
        writer.writeNext(new String[] { "speed", "distance", "finaldistance", "variance" });

        for (int speed = 1; speed < 30; speed++) {
            SummaryStatistics distance = new SummaryStatistics();
            SummaryStatistics finalDistance = new SummaryStatistics();
            SummaryStatistics variance = new SummaryStatistics();
            for (int seed = 0; seed < 50; seed++) {

                final double[] result = competitiveSweepRun(seed, 101, 1, 1, 14, .1f, .1f, speed, 58, null,
                        additionalCompetitors);
                distance.addValue(result[0]);
                finalDistance.addValue(result[1]);
                variance.addValue(result[2]);

            }

            final String[] nextLine = { String.valueOf(speed), String.valueOf(distance.getMean()),
                    String.valueOf(finalDistance.getMean()), String.valueOf(variance.getMean()) };
            System.out.println(Arrays.toString(nextLine));
            writer.writeNext(nextLine);
            writer.flush();
        }

        writer.close();

    }

    private static double[] competitiveSweepRun(int seed, int demandIntercept, int demandSlope, int wageSlope,
            int dailyWageIntercept, float proportionalGain, float integralGain, int maximizationTimePeriod,
            double correctCompetitivePrice, String filename, int additionalCompetitors) {

        //create the run
        MacroII macroII = new MacroII(seed);
        TripolistScenario scenario = new TripolistScenario(macroII);
        macroII.setScenario(scenario);
        scenario.setAdditionalCompetitors(additionalCompetitors);
        //set the demand
        scenario.setDemandIntercept(demandIntercept);
        scenario.setDemandSlope(demandSlope);
        scenario.setDailyWageSlope(wageSlope);
        scenario.setDailyWageIntercept(dailyWageIntercept);
        scenario.setAskPricingStrategy(SimpleFlowSellerPID.class);
        scenario.setControlType(MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);

        //start it and have one step
        macroII.start();
        macroII.schedule.step(macroII);

        //now set the right parameters
        for (final Firm firm : scenario.getCompetitors()) {
            final SalesDepartment salesDepartment = firm.getSalesDepartment(UndifferentiatedGoodType.GENERIC);
            final SimpleFlowSellerPID strategy = new SimpleFlowSellerPID(salesDepartment,
                    proportionalGain + (float) macroII.random.nextGaussian() / 100f,
                    integralGain + (float) macroII.random.nextGaussian() / 100f, 0f, 0, salesDepartment.getMarket(),
                    salesDepartment.getRandom().nextInt(100), salesDepartment.getFirm().getModel()); //added a bit of noise
            salesDepartment.setAskPricingStrategy(strategy);

            //all impacts are 0 because it's perfect competitive
            salesDepartment.setPredictorStrategy(new FixedDecreaseSalesPredictor(0));
            firm.getHRs().iterator().next().setPredictor(new FixedIncreasePurchasesPredictor(0));
        }
        for (final PlantControl control : scenario.getMaximizers()) {
            ((MarginalPlantControl) control).getMaximizer().setHowManyDaysBeforeEachCheck(maximizationTimePeriod);
        }

        SummaryStatistics distance = new SummaryStatistics();
        SummaryStatistics finalPrice = new SummaryStatistics();
        SummaryStatistics finalDistance = new SummaryStatistics();

        //run the model
        for (int i = 0; i < 4000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(5000, i, 100);
            final double closingPrice = macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE);
            double distanceFromCorrect;
            if (Double.isNaN(closingPrice) || closingPrice < 0) {
                distanceFromCorrect = correctCompetitivePrice;
            } else {
                distanceFromCorrect = Math.pow(correctCompetitivePrice - closingPrice, 2);
            }
            distance.addValue(distanceFromCorrect);
        }

        //run the model
        for (int i = 0; i < 1000; i++) {
            macroII.schedule.step(macroII);
            MarginalMaximizerPIDTuning.printProgressBar(1000, i, 100);
            double closingPrice = macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE);
            finalPrice.addValue(closingPrice);

            double distanceFromCorrect;
            if (Double.isNaN(closingPrice) || closingPrice < 0) {
                closingPrice = 0;
            }

            distanceFromCorrect = Math.pow(correctCompetitivePrice - closingPrice, 2);

            distance.addValue(distanceFromCorrect);
            finalDistance.addValue(distanceFromCorrect);
        }

        if (filename != null)
            macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                    .writeToCSVFile(Paths.get("runs", "rawdata", filename).toFile());

        return new double[] { distance.getMean(), finalDistance.getMean(), finalPrice.getVariance() };

    }

    private static void tuningTrial(int seed, final float proportionalGain, final float integralGain,
            final int initialSpeed, File csvFileToWrite, final Path departmentLog) {

        OneLinkSupplyChainResult.beefMonopolistOneRun(seed, 1, 1, true, true,
                new Function<SalesDepartment, AskPricingStrategy>() {
                    @Override
                    public AskPricingStrategy apply(SalesDepartment department) {
                        if (departmentLog != null)
                            department.addLogEventListener(
                                    new LogToFile(departmentLog, LogLevel.TRACE, department.getModel()));
                        SimpleFlowSellerPID pricer = new SimpleFlowSellerPID(department, proportionalGain,
                                integralGain, 0, 0, department.getMarket(), department.getRandom().nextInt(100),
                                department.getFirm().getModel());
                        pricer.decorateController(new Function<PIDController, Controller>() {
                            @Override
                            public Controller apply(PIDController pidController) {
                                final PIDStickinessSalesTuner pidStickinessSalesTuner = new PIDStickinessSalesTuner(
                                        pidController, department, department.getModel());
                                pidStickinessSalesTuner.setObservationsBeforeTuning(100);
                                return pidStickinessSalesTuner;
                            }
                        });
                        return pricer;
                    }
                }, null, csvFileToWrite, null, null);
    }

    private static void badlyOptimizedNoInventorySupplyChain(int seed, final float proportionalGain,
            final float integralGain, final int speed, File csvFileToWrite, final boolean tuning) {
        final MacroII macroII = new MacroII(seed);
        final OneLinkSupplyChainScenarioWithCheatingBuyingPrice scenario1 = new OneLinkSupplyChainScenarioWithCheatingBuyingPrice(
                macroII) {

            @Override
            protected void buildBeefSalesPredictor(SalesDepartment dept) {
                FixedDecreaseSalesPredictor predictor = SalesPredictor.Factory
                        .newSalesPredictor(FixedDecreaseSalesPredictor.class, dept);
                predictor.setDecrementDelta(2);
                dept.setPredictorStrategy(predictor);

            }

            @Override
            public void buildFoodPurchasesPredictor(PurchasesDepartment department) {
                department.setPredictor(new FixedIncreasePurchasesPredictor(0));

            }

            @Override
            protected SalesDepartment createSalesDepartment(Firm firm, Market goodmarket) {
                SalesDepartment department = super.createSalesDepartment(firm, goodmarket);
                if (goodmarket.getGoodType().equals(OneLinkSupplyChainScenario.OUTPUT_GOOD)) {
                    department.setPredictorStrategy(new FixedDecreaseSalesPredictor(0));
                } else {
                    final InventoryBufferSalesControl askPricingStrategy = new InventoryBufferSalesControl(
                            department, 10, 200, macroII, proportionalGain, integralGain, 0, macroII.getRandom());
                    askPricingStrategy.setSpeed(speed);
                    if (tuning)
                        askPricingStrategy.decorateController(pidController -> {
                            final PIDStickinessHillClimberTuner pidStickinessSalesTuner = new PIDStickinessHillClimberTuner(
                                    department.getModel(), pidController, department, 1000);
                            pidStickinessSalesTuner.setStepSize(5);
                            pidStickinessSalesTuner.attachWriter(Paths.get("runs", "rawdata", "stickLog.csv"));
                            //                   pidStickinessSalesTuner.setObservationsBeforeTuning(5000);
                            //                    pidStickinessSalesTuner.setLogToWrite();
                            return pidStickinessSalesTuner;
                        });
                    department.setAskPricingStrategy(askPricingStrategy);

                }
                return department;
            }

            @Override
            protected PurchasesDepartment createPurchaseDepartment(Blueprint blueprint, Firm firm) {
                final PurchasesDepartment purchaseDepartment = super.createPurchaseDepartment(blueprint, firm);
                if (purchaseDepartment != null)
                    purchaseDepartment.setPriceAverager(new LastClosingPriceEcho());
                return purchaseDepartment;
            }

            @Override
            protected HumanResources createPlant(Blueprint blueprint, Firm firm, Market laborMarket) {
                HumanResources hr = super.createPlant(blueprint, firm, laborMarket);
                hr.setPriceAverager(new LastClosingPriceEcho());

                if (blueprint.getOutputs().containsKey(OneLinkSupplyChainScenario.INPUT_GOOD)) {
                    hr.setPredictor(new FixedIncreasePurchasesPredictor(1));
                }
                if (blueprint.getOutputs().containsKey(OneLinkSupplyChainScenario.OUTPUT_GOOD)) {
                    hr.setPredictor(new FixedIncreasePurchasesPredictor(0));
                }
                return hr;
            }
        };
        scenario1.setControlType(MarginalMaximizer.class);
        scenario1.setSalesDepartmentType(SalesDepartmentOneAtATime.class);
        scenario1.setBeefPriceFilterer(null);

        //competition!
        scenario1.setNumberOfBeefProducers(1);
        scenario1.setNumberOfFoodProducers(5);

        //no delay

        //add csv writer if needed
        if (csvFileToWrite != null)
            DailyStatCollector.addDailyStatCollectorToModel(csvFileToWrite, macroII);

        macroII.setScenario(scenario1);
        macroII.start();

        while (macroII.schedule.getTime() < 14000) {
            macroII.schedule.step(macroII);
            printProgressBar(14001, (int) macroII.schedule.getSteps(), 100);
            System.out.println(macroII.getMarket(OneLinkSupplyChainScenario.OUTPUT_GOOD)
                    .getLatestObservation(MarketDataType.VOLUME_PRODUCED));
        }

        SummaryStatistics averageFoodPrice = new SummaryStatistics();
        SummaryStatistics averageBeefProduced = new SummaryStatistics();
        SummaryStatistics averageBeefPrice = new SummaryStatistics();
        for (int j = 0; j < 1000; j++) {
            //make the model run one more day:
            macroII.schedule.step(macroII);
            averageFoodPrice.addValue(macroII.getMarket(OneLinkSupplyChainScenario.OUTPUT_GOOD)
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE));
            averageBeefProduced
                    .addValue(macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD).getYesterdayVolume());
            averageBeefPrice.addValue(macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD)
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE));
        }

        System.out.println("seed: " + seed);
        System.out.println("beef price: " + averageBeefPrice.getMean());
        System.out.println("food price: " + averageFoodPrice.getMean());
        System.out.println("produced: " + averageBeefProduced.getMean());
        System.out.println();

    }

    private static void oneHundredAllLearnedRuns(File csvFileToWrite,
            Function<SalesDepartment, AskPricingStrategy> woodPricingFactory,
            Function<SalesDepartment, AskPricingStrategy> furniturePricingFactory)
            throws ExecutionException, InterruptedException, IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(csvFileToWrite));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {
            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.beefMonopolistOneRun(i, 1,
                    DEFAULT_STICKINESS, true, true, woodPricingFactory, furniturePricingFactory, null, null, null);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredAllLearningRuns(File csvFileToWrite,
            Function<SalesDepartment, AskPricingStrategy> woodPricingFactory,
            Function<SalesDepartment, AskPricingStrategy> furniturePricingFactory)
            throws ExecutionException, InterruptedException, IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(csvFileToWrite));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {

            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.beefMonopolistOneRun(i, 1,
                    DEFAULT_STICKINESS, false, false, woodPricingFactory, furniturePricingFactory, null, null,
                    null);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredAllLearnedCompetitiveRuns(File csvFileToWrite)
            throws ExecutionException, InterruptedException, IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(csvFileToWrite));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {
            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.everybodyLearnedCompetitivePIDRun(i, 1,
                    DEFAULT_STICKINESS, null);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredAllLearningCompetitiveRuns(File csvFileToWrite)
            throws ExecutionException, InterruptedException, IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(csvFileToWrite));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {
            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.everybodyLearningCompetitiveStickyPIDRun(i,
                    1f, DEFAULT_STICKINESS);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredAllLearnedFoodRuns(File file) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(file));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {
            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.foodMonopolistOneRun(i, 1,
                    DEFAULT_STICKINESS, true, true, null, null);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredAllLearningFoodRuns(File file) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(file));
        writer.writeNext(new String[] { "production", "beefPrice", "foodPrice" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {
            OneLinkSupplyChainResult result = OneLinkSupplyChainResult.foodMonopolistOneRun(i, 1,
                    DEFAULT_STICKINESS, false, false, null, null);
            String[] resultString = new String[3];
            resultString[0] = String.valueOf(result.getQuantity());
            resultString[1] = String.valueOf(result.getBeefPrice());
            resultString[2] = String.valueOf(result.getFoodPrice());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredLearningMonopolist(File file) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(file));
        writer.writeNext(new String[] { "production", "price" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {

            //create the run
            MacroII macroII = new MacroII(i);
            MonopolistScenario scenario = new MonopolistScenario(macroII);
            macroII.setScenario(scenario);
            //set the demand

            scenario.setAskPricingStrategy(InventoryBufferSalesControl.class);
            scenario.setControlType(
                    MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);

            //start it and have one step
            macroII.start();
            macroII.schedule.step(macroII);

            //now set the right parameters
            final SalesDepartment salesDepartment = scenario.getMonopolist()
                    .getSalesDepartment(UndifferentiatedGoodType.GENERIC);

            //learning
            assert salesDepartment.getPredictorStrategy() instanceof ErrorCorrectingSalesPredictor;

            //run the model
            for (int j = 0; j < 5000; j++) {
                macroII.schedule.step(macroII);
                MarginalMaximizerPIDTuning.printProgressBar(5000, j, 100);
            }

            String[] resultString = new String[2];
            resultString[0] = String.valueOf(macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                    .getLatestObservation(MarketDataType.VOLUME_TRADED));
            resultString[1] = String.valueOf(macroII.getMarket(UndifferentiatedGoodType.GENERIC).getData()
                    .getLatestObservation(MarketDataType.CLOSING_PRICE));
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    private static void oneHundredLearningCompetitive(File file) throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(file));
        writer.writeNext(new String[] { "production", "price" });

        //this will take a looong time

        //run the test 5 times!
        for (long i = 0; i < 100; i++) {

            //create the run
            MacroII macroII = new MacroII(i);
            TripolistScenario scenario = new TripolistScenario(macroII);
            macroII.setScenario(scenario);
            scenario.setAdditionalCompetitors(4);
            //set the demand
            scenario.setAskPricingStrategy(InventoryBufferSalesControl.class);
            scenario.setControlType(
                    MonopolistScenario.MonopolistScenarioIntegratedControlEnum.MARGINAL_PLANT_CONTROL);

            //start it and have one step
            macroII.start();
            macroII.schedule.step(macroII);

            //now set the right parameters
            for (Firm firm : scenario.getCompetitors()) {
                final SalesDepartment salesDepartment = firm.getSalesDepartment(UndifferentiatedGoodType.GENERIC);
                //learning
                assert salesDepartment.getPredictorStrategy() instanceof RecursiveSalePredictor;
            }

            //run the model
            for (int j = 0; j < 5000; j++) {
                macroII.schedule.step(macroII);
                MarginalMaximizerPIDTuning.printProgressBar(5000, j, 100);
            }

            //average over the last 500 steps
            SummaryStatistics prices = new SummaryStatistics();
            SummaryStatistics quantities = new SummaryStatistics();
            for (int j = 0; j < 500; j++) {
                macroII.schedule.step(macroII);
                assert !Float.isNaN(macroII.getMarket(UndifferentiatedGoodType.GENERIC).getTodayAveragePrice());
                prices.addValue(macroII.getMarket(UndifferentiatedGoodType.GENERIC).getTodayAveragePrice());
                quantities.addValue(macroII.getMarket(UndifferentiatedGoodType.GENERIC).getTodayVolume());

            }

            String[] resultString = new String[2];
            resultString[0] = String.valueOf(quantities.getMean());
            resultString[1] = String.valueOf(prices.getMean());
            System.out.println(Arrays.toString(resultString));
            writer.writeNext(resultString);
            writer.flush();
        }

        writer.close();

    }

    public static double[] beefMonopolistOneRun(long seed, float divideMonopolistGainsByThis, int monopolistSpeed,
            final boolean beefLearned, final boolean foodLearned, int maximizationSpeed, File csvFileToWrite) {
        SummaryStatistics distance = new SummaryStatistics();
        SummaryStatistics last1000Distance = new SummaryStatistics();

        final MacroII macroII = new MacroII(seed);
        final OneLinkSupplyChainScenarioWithCheatingBuyingPrice scenario1 = new OneLinkSupplyChainScenarioWithCheatingBuyingPrice(
                macroII) {

            @Override
            protected void buildBeefSalesPredictor(SalesDepartment dept) {
                if (beefLearned) {
                    FixedDecreaseSalesPredictor predictor = SalesPredictor.Factory
                            .newSalesPredictor(FixedDecreaseSalesPredictor.class, dept);
                    predictor.setDecrementDelta(2);
                    dept.setPredictorStrategy(predictor);
                } else {
                    assert dept.getPredictorStrategy() instanceof RecursiveSalePredictor; //assuming here nothing has been changed and we are still dealing with recursive sale predictors
                    dept.setPredictorStrategy(new RecursiveSalePredictor(model, dept, 500));
                }
            }

            @Override
            public void buildFoodPurchasesPredictor(PurchasesDepartment department) {
                if (foodLearned)
                    department.setPredictor(new FixedIncreasePurchasesPredictor(0));

            }

            @Override
            protected SalesDepartment createSalesDepartment(Firm firm, Market goodmarket) {
                SalesDepartment department = super.createSalesDepartment(firm, goodmarket);
                if (goodmarket.getGoodType().equals(OneLinkSupplyChainScenario.OUTPUT_GOOD)) {
                    if (foodLearned)
                        department.setPredictorStrategy(new FixedDecreaseSalesPredictor(0));
                }
                return department;
            }

            @Override
            protected HumanResources createPlant(Blueprint blueprint, Firm firm, Market laborMarket) {
                HumanResources hr = super.createPlant(blueprint, firm, laborMarket);
                if (blueprint.getOutputs().containsKey(OneLinkSupplyChainScenario.INPUT_GOOD)) {
                    if (beefLearned) {
                        hr.setPredictor(new FixedIncreasePurchasesPredictor(1));
                    }
                }
                if (blueprint.getOutputs().containsKey(OneLinkSupplyChainScenario.OUTPUT_GOOD)) {
                    if (foodLearned)
                        hr.setPredictor(new FixedIncreasePurchasesPredictor(0));
                }
                return hr;
            }
        };
        scenario1.setControlType(MarginalMaximizer.class);
        scenario1.setSalesDepartmentType(SalesDepartmentOneAtATime.class);
        scenario1.setBeefPriceFilterer(null);

        //competition!
        scenario1.setNumberOfBeefProducers(1);
        scenario1.setBeefTargetInventory(100);
        scenario1.setNumberOfFoodProducers(5);

        scenario1.setDivideProportionalGainByThis(divideMonopolistGainsByThis);
        scenario1.setDivideIntegrativeGainByThis(divideMonopolistGainsByThis);
        //no delay
        scenario1.setBeefPricingSpeed(monopolistSpeed);

        //add csv writer if needed
        if (csvFileToWrite != null)
            DailyStatCollector.addDailyStatCollectorToModel(csvFileToWrite, macroII);

        macroII.setScenario(scenario1);
        macroII.start();
        macroII.schedule.step(macroII);
        Preconditions.checkState(scenario1.getMaximizers().size() == 6, scenario1.getMaximizers().size()); // 1 monopolist, 5 competitors
        for (WorkforceMaximizer control : scenario1.getMaximizers())
            ((PeriodicMaximizer) control).setHowManyDaysBeforeEachCheck(maximizationSpeed);

        while (macroII.schedule.getTime() < 5000) {
            macroII.schedule.step(macroII);
            printProgressBar(14001, (int) macroII.schedule.getSteps(), 100);
            long price = macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD).getLastPrice();
            if (price < 0)
                price = 0;
            distance.addValue(Math.pow(68 - price, 2));
        }

        SummaryStatistics averageFoodPrice = new SummaryStatistics();
        SummaryStatistics averageBeefProduced = new SummaryStatistics();
        SummaryStatistics averageBeefPrice = new SummaryStatistics();
        for (int j = 0; j < 1000; j++) {
            //make the model run one more day:
            macroII.schedule.step(macroII);
            averageFoodPrice.addValue(macroII.getMarket(OneLinkSupplyChainScenario.OUTPUT_GOOD)
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE));
            averageBeefProduced
                    .addValue(macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD).getYesterdayVolume());
            averageBeefPrice.addValue(macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD)
                    .getLatestObservation(MarketDataType.AVERAGE_CLOSING_PRICE));

            long price = macroII.getMarket(OneLinkSupplyChainScenario.INPUT_GOOD).getLastPrice();
            if (price < 0)
                price = 0;
            distance.addValue(Math.pow(68 - price, 2));
            last1000Distance.addValue(Math.pow(68 - price, 2));
        }

        return new double[] { distance.getMean(), last1000Distance.getMean() };

    }

    //sweep over the period of maximization to see what happens if the frequency is too high or too low.
    private static void woodMonopolistSupplyChainSweep() throws IOException {

        CSVWriter writer = new CSVWriter(
                new FileWriter(Paths.get("runs", "rawdata", "woodMonopolistStickinessesSweep.csv").toFile()));
        final String[] header = { "decisionSpeed", "stickiness", "distance", "finaldistance" };
        System.out.println(Arrays.toString(header));
        writer.writeNext(header);

        for (int speed = 1; speed < 30; speed++) {
            for (int stickiness = 0; stickiness < 50; stickiness++) {
                SummaryStatistics distance = new SummaryStatistics();
                SummaryStatistics finalDistance = new SummaryStatistics();
                for (int seed = 0; seed < 5; seed++) {

                    final double[] result = beefMonopolistOneRun(seed, 1, stickiness, true, true, speed, null);
                    distance.addValue(result[0]);
                    finalDistance.addValue(result[1]);

                }

                final String[] nextLine = { String.valueOf(speed), String.valueOf(stickiness),
                        String.valueOf(distance.getMean()), String.valueOf(finalDistance.getMean()) };
                System.out.println(Arrays.toString(nextLine));
                writer.writeNext(nextLine);
                writer.flush();
            }
        }

        writer.close();

    }

}