it.gcatania.dropboxchallenges.packingYourDropbox.DropboxOptimizationTest.java Source code

Java tutorial

Introduction

Here is the source code for it.gcatania.dropboxchallenges.packingYourDropbox.DropboxOptimizationTest.java

Source

/**
 * Copyright 2011 Gabriele Catania <gabriele.ctn@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package it.gcatania.dropboxchallenges.packingYourDropbox;

import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.FakeComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.RectangleAreaComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.RectangleMaxSideComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.RectangleMaxSideThenPerimeterComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.RectanglePerimeterComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.comparators.RectangleSideRatioComparator;
import it.gcatania.dropboxchallenges.packingYourDropbox.model.Dropbox;
import it.gcatania.dropboxchallenges.packingYourDropbox.model.Rectangle;
import it.gcatania.dropboxchallenges.packingYourDropbox.overheadcalculators.DropboxAreaOverheadCalculator;
import it.gcatania.dropboxchallenges.packingYourDropbox.overheadcalculators.DropboxMagicOverheadCalculator;
import it.gcatania.dropboxchallenges.packingYourDropbox.overheadcalculators.OverheadCalculator;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang.time.StopWatch;
import org.testng.annotations.Test;

/**
 * dropbox optimization test class. Generates multiple groups of random rectangles and evaluates the performance of
 * different methods. Outputs the results in a csv file
 * @author gcatania
 */
public class DropboxOptimizationTest {

    private static final int NUM_ITERATIONS = 100;

    private static final int NUM_RECTANGLES = 20;

    private static final int MIN_SIDE_LENGTH = 1;

    private static final int MAX_SIDE_LENGTH = 80;

    private final Map<Setup, Score> optimizationData = new HashMap<Setup, Score>();

    private final Random random = new Random();

    /**
     * a dropbox build setup
     * @author gcatania
     */
    private static final class Setup {

        private final Comparator<Rectangle> comparator;

        private final OverheadCalculator<?> calculator;

        private Setup(Comparator<Rectangle> comparator, OverheadCalculator<?> calculator) {
            this.calculator = calculator;
            this.comparator = comparator;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Setup) {
                Setup other = (Setup) obj;
                // using by-reference equality, won't work outside this class!
                return other.comparator == comparator && other.calculator == calculator;
            }
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hashCode() {
            // don't care about null safety here
            return 1 + comparator.hashCode() + 13 * calculator.hashCode();
        }

    }

    /**
     * a dropbox build score
     * @author gcatania
     */
    private static final class Score {

        /**
         * counts the number of times the setup yielded the best solution
         */
        private int firstPlaces = 0;

        /**
         * counts the number of times a pre-allocation strategy was more successful than the default one
         */
        private int winningPreAllocations = 0;

        /**
         * counts the number of times a pre-allocation strategy performed like the default one
         */
        private int evenPreAllocations = 0;

        /**
         * the total area of built droboxes
         */
        private long totalArea = 0;

        /**
         * the total free space of built droboxes
         */
        private long totalFreeSpace = 0;

        /**
         * the difference between the total area for this setup and the total area of the setup that performed best
         * overall
         */
        private long totalAreaOverhead = 0;

        /**
         * keeps record of time performance
         */
        private final StopWatch watch = new StopWatch();

        /**
         * transient variable used to compare dropboxes at the end of the single iteration
         */
        private Dropbox lastDropBox = null;

    }

    @Test
    public void testOptimization() throws IOException {
        random.setSeed(133l); // make test reproductible

        optimizationData.clear();

        @SuppressWarnings("unchecked")
        List<Comparator<Rectangle>> comparators = Arrays.asList(new FakeComparator<Rectangle>(),
                // new RectangleSuperComparator(), // same as RectangleAreaComparator
                new RectangleMaxSideThenPerimeterComparator(), new RectangleAreaComparator(),
                new RectangleMaxSideComparator(), new RectangleSideRatioComparator(),
                new RectanglePerimeterComparator());
        List<OverheadCalculator<?>> overheadCalculators = Arrays.<OverheadCalculator<?>>asList(
                new DropboxAreaOverheadCalculator(),
                // new AreaOverheadCalculator(), // not optimal
                // new DistanceFromOriginOverheadCalculator(), // not optimal
                new DropboxMagicOverheadCalculator() // not optimal
        // new FreeSpaceOverheadCalculator(), // same as DropBoxAreaOverheadCalculator
        // new DropBoxOverheadCalculator() // not optimal
        );

        for (int i = 0; i < NUM_ITERATIONS; i++) {
            System.out.println("pass " + i);
            singlePass(comparators, overheadCalculators);
        }
        List<Map.Entry<Setup, Score>> entrySet = new ArrayList<Map.Entry<Setup, Score>>(
                optimizationData.entrySet());
        Collections.sort(entrySet, new Comparator<Map.Entry<Setup, Score>>() {

            /**
             * compares two scores by their number of first places, then by their total area, then by their total free
             * space
             */
            @Override
            public int compare(Map.Entry<Setup, Score> e1, Map.Entry<Setup, Score> e2) {
                Score score1 = e1.getValue();
                Score score2 = e2.getValue();
                int firstPlacesDiff = score2.firstPlaces - score1.firstPlaces;
                if (firstPlacesDiff == 0) {
                    long totalAreaDiff = score1.totalArea - score2.totalArea;
                    if (totalAreaDiff == 0) {
                        return Long.signum(score1.totalFreeSpace - score2.totalFreeSpace);
                    }
                    return Long.signum(totalAreaDiff);
                }
                return firstPlacesDiff;
            }
        });

        long minArea = Long.MAX_VALUE;
        long minFreeSpace = Long.MAX_VALUE;
        long minTime = Long.MAX_VALUE;
        long minAreaOverhead = Long.MAX_VALUE;
        for (Map.Entry<Setup, Score> e : entrySet) {
            Score score = e.getValue();
            if (minArea > score.totalArea) {
                minArea = score.totalArea;
            }
            if (minFreeSpace > score.totalFreeSpace) {
                minFreeSpace = score.totalFreeSpace;
            }
            if (minAreaOverhead > score.totalAreaOverhead) {
                minAreaOverhead = score.totalAreaOverhead;
            }
            long time = score.watch.getTime();
            if (minTime > time) {
                minTime = time;
            }
        }

        BufferedWriter w = new BufferedWriter(new FileWriter("target/results.csv", false));
        w.write("Comparator;Calculator;FirstPlaces;FirstPlacesPerc;TotalArea;TotalAreaPerc;"
                + "FreeSpace;FreeSpacePerc;TimeMillis;TimeMillisPerc;AreaOverhead;AreaOverheadPerc;"
                + "winningPreAllocationsPerc;evenPreAllocationsPerc\n");
        for (Map.Entry<Setup, Score> e : entrySet) {
            Setup setup = e.getKey();
            Score score = e.getValue();
            int firstPlacesPerc = 100 * score.firstPlaces / NUM_ITERATIONS;
            long totalAreaPerc = 100 * score.totalArea / minArea;
            long freeSpacePerc = 100 * score.totalFreeSpace / minFreeSpace;
            long timePerc = 100 * score.watch.getTime() / minTime;
            long areaOverheadPerc = minAreaOverhead > 0 ? 100 * score.totalAreaOverhead / minAreaOverhead : 0;
            int winningPreAllocationsPerc = score.winningPreAllocations * 100 / NUM_ITERATIONS;
            int evenPreAllocationsPerc = score.evenPreAllocations * 100 / NUM_ITERATIONS;
            w.write(new StringBuilder(setup.comparator.getClass().getSimpleName()).append(';')
                    .append(setup.calculator.getClass().getSimpleName()).append(';').append(score.firstPlaces)
                    .append(';').append(firstPlacesPerc).append(';').append(score.totalArea).append(';')
                    .append(totalAreaPerc).append(';').append(score.totalFreeSpace).append(';')
                    .append(freeSpacePerc).append(';').append(score.watch.getTime()).append(';').append(timePerc)
                    .append(';').append(score.totalAreaOverhead).append(';').append(areaOverheadPerc).append(';')
                    .append(winningPreAllocationsPerc).append(';').append(evenPreAllocationsPerc).append('\n')
                    .toString());
        }
        w.close();
    }

    private void singlePass(List<Comparator<Rectangle>> comparators,
            List<OverheadCalculator<?>> overheadCalculators) {
        List<Rectangle> rectangles = createRandomRectangles();

        long minArea = Long.MAX_VALUE;
        for (Comparator<Rectangle> comp : comparators) {
            for (OverheadCalculator<?> o : overheadCalculators) {
                Setup setup = new Setup(comp, o);
                Score score = optimizationData.get(setup);
                if (score == null) {
                    score = new Score();
                    optimizationData.put(setup, score);
                    score.watch.start();
                } else {
                    score.watch.resume();
                }

                DropboxBuilder builder = new DropboxBuilder(comp, o);
                Dropbox dropbox = builder.build(rectangles);
                Dropbox dropbox2 = builder.buildWithPreAllocation(rectangles);
                score.watch.suspend();

                if (dropbox2.getArea() < dropbox.getArea()) {
                    dropbox = dropbox2;
                    score.winningPreAllocations++;
                } else if (dropbox2.getArea() == dropbox.getArea()) {
                    score.evenPreAllocations++;
                }

                long currentArea = dropbox.getArea();
                score.totalArea += currentArea;
                score.totalFreeSpace += dropbox.getFreeSpace();
                score.lastDropBox = dropbox;
                if (currentArea < minArea) {
                    minArea = currentArea;
                }
                // System.out.println(dropbox.draw());
            }
        }

        for (Score score : optimizationData.values()) {
            Dropbox db = score.lastDropBox;
            long areaOverhead = db.getArea() - minArea;
            if (areaOverhead == 0) {
                score.firstPlaces++;
            } else {
                score.totalAreaOverhead += areaOverhead;
            }
        }
    }

    private List<Rectangle> createRandomRectangles() {
        List<Rectangle> rectangles = new ArrayList<Rectangle>(NUM_RECTANGLES);
        for (int i = 0; i < NUM_RECTANGLES; i++) {
            int width = MIN_SIDE_LENGTH + Math.abs(random.nextInt() % MAX_SIDE_LENGTH);
            int height = MIN_SIDE_LENGTH + Math.abs(random.nextInt() % MAX_SIDE_LENGTH);
            Rectangle rect = new Rectangle(width, height);
            // System.out.println("Adding random " + rect);
            rectangles.add(rect);
        }
        return rectangles;
    }

}