/* * Copyright © 2010 by Ondrej Skalicka. All Rights Reserved */ package cz.cvut.felk.cig.jcop.solver; import cz.cvut.felk.cig.jcop.result.Result; import cz.cvut.felk.cig.jcop.result.ResultEntry; import cz.cvut.felk.cig.jcop.result.render.SimpleCompareRender; import cz.cvut.felk.cig.jcop.util.compare.ResultEntryFitnessComparator; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Median solver takes another solver, runs it repeatedly and then returns median of result entries of original solver. * <p/> * MedianSolver runs original solver (also referred as wrapped solver) several times. Every time, original solver * returns list of result entries. For every item in that list, it creates separate group (implemented as list) and puts * that item to the group. Then takes another list of results (2nd run of wrapped solver) and puts all result entries in * appropriate groups. This is repeated until required number of runs of wrapped solver has been made. * <p/> * Afterwards takes median from every group (key to sort is fitness) and use these medians as a result by MedianSolver * (wraps the medians back to a {@link cz.cvut.felk.cig.jcop.result.Result}). * <p/> * Note that wrapped entry must return identical number of result entries in result each time and their order is * required to be the same each run. If first condition is violated, exception will be raised. If second is violated, * unexpected result may occur. * * @author Ondrej Skalicka */ public class MedianSolver extends BaseSolver { /** * Original (wrapped) solver to be ran repeatedly. */ protected Solver solver; /** * Number of times original solver has to be ran. */ protected int repeatedRuns; /** * File to write additional data to. */ protected File file; /** * Creates new wrapper around other solver with given number of runs. * * @param solver which solver to run repeatedly * @param repeatedRuns how many times to run the solver * @throws IllegalArgumentException if repeatedRuns is lower than 1 */ public MedianSolver(Solver solver, int repeatedRuns) throws IllegalArgumentException { this(solver, repeatedRuns, null); } /** * Creates new wrapper around other solver with given number of runs. Also, writes basic information about all * solutions into a file. * * @param solver which solver to run repeatedly * @param repeatedRuns how many times to run the solver * @param file file to write additional information into * @throws IllegalArgumentException if repeatedRuns is lower than 1 */ public MedianSolver(Solver solver, int repeatedRuns, File file) throws IllegalArgumentException { if (repeatedRuns < 1) throw new IllegalArgumentException("RepeatedRuns must be greater than zero, " + repeatedRuns + " found."); this.repeatedRuns = repeatedRuns; this.solver = solver; this.defaultRenders.add(new SimpleCompareRender()); this.file = file; } /** * {@inheritDoc} * * @throws IllegalArgumentException if two runs of wrapped solver returns results of different size */ public void run() throws IllegalArgumentException { List<List<ResultEntry>> groups = null; for (int i = 0; i < this.repeatedRuns; ++i) { this.solver.getResult().clearResults(); this.solver.run(); Result result = this.solver.getResult(); if (groups == null) { // create empty groups groups = new ArrayList<List<ResultEntry>>(result.getResultEntries().size()); // allocate space in every group for (int j = 0; j < result.getResultEntries().size(); ++j) { groups.add(new ArrayList<ResultEntry>(this.repeatedRuns)); } } if (result.getResultEntries().size() != groups.size()) throw new IllegalArgumentException(String.format( "Wrapped solver must return result with identical number of elements each run, " + "given %d in run %d, expected %d", result.getResultEntries().size(), i, groups.size())); for (int j = 0; j < result.getResultEntries().size(); j++) { ResultEntry resultEntry = result.getResultEntries().get(j); groups.get(j).add(resultEntry); } } int middleIndex = this.repeatedRuns / 2; for (List<ResultEntry> group : groups) { Collections.sort(group, new ResultEntryFitnessComparator()); if (this.logger.isDebugEnabled()) { this.logger.debug("Sorted result entries, dump:"); for (int i = 0; i < group.size(); i++) { ResultEntry resultEntry = group.get(i); this.logger.debug(i + ". " + resultEntry.getBestFitness()); } } this.getResult().addEntry(group.get(middleIndex)); } if (this.file != null) { try { PrintStream printStream = new PrintStream(file); for (List<ResultEntry> group : groups) { printStream.printf("Sorted entries for %s/%s\n", group.get(0).getAlgorithm(), group.get(0).getProblem()); for (int i = 0; i < group.size(); i++) { printStream.printf("%03d. %.5f\n", i, group.get(i).getBestFitness()); } } } catch (FileNotFoundException e) { e.printStackTrace(); } } } }