{- Copyright (C) 2014-2015 Dr. Alistair Ward This file is part of WeekDaze. WeekDaze is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. WeekDaze is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with WeekDaze. If not, see . -} {- | [@AUTHOR@] Dr. Alistair Ward [@DESCRIPTION@] * Defines the structure produced by module 'WeekDaze.Implementation.EvolutionaryAlgorithm.evolveStudentViewTimetable'. * Renders this structure in either text or XHTML. -} module WeekDaze.OutputFormat.EvolutionStrategyStatistics( -- * Types -- ** Type-synonyms -- NGenerations, OptimiseLessonCriteriaWeightsLog, -- ** Data-types EvolutionStrategyStatistics( -- MkEvolutionStrategyStatistics, -- getDepletionStrategyName, -- getReconstructionStrategyName, -- getInitialFecundity, -- getFinalFecundity, -- getMaybeImprovementFactor, getWeightedMeanOfTimetableCriteria -- getNGenerations, -- getMaybeTimetableCriteriaValues ), -- * Constants -- notApplicable, -- runtimeInformationCSSIdentifier, -- timetableAxisCSSIdentifier, -- * Functions composeRuntimeLog, toHtml, -- showReal -- ** Constructor mkEvolutionStrategyStatistics, -- ** Predicates -- isFecundityUnchanged ) where import Control.Arrow((***), (&&&)) import qualified Data.List import qualified Data.Maybe import qualified Data.Ord import qualified Factory.Math.Statistics import qualified Text.Printf import qualified Text.XHtml.Strict import qualified WeekDaze.Colour.HTMLColour as Colour.HTMLColour import qualified WeekDaze.ExecutionConfiguration.CriterionWeight as ExecutionConfiguration.CriterionWeight import qualified WeekDaze.ExecutionConfiguration.EvolutionStrategies as ExecutionConfiguration.EvolutionStrategies import qualified WeekDaze.ExecutionConfiguration.ExecutionOptions as ExecutionConfiguration.ExecutionOptions import qualified WeekDaze.ExecutionConfiguration.LessonCriteriaWeights as ExecutionConfiguration.LessonCriteriaWeights import qualified WeekDaze.ExecutionConfiguration.OptimiseLessonCriteriaWeights as ExecutionConfiguration.OptimiseLessonCriteriaWeights import qualified WeekDaze.ExecutionConfiguration.TimetableBreederFecundity as ExecutionConfiguration.TimetableBreederFecundity import qualified WeekDaze.ExecutionConfiguration.TimetableCriteriaWeights as ExecutionConfiguration.TimetableCriteriaWeights import qualified WeekDaze.Model.TimetableAxisTriple as Model.TimetableAxisTriple import qualified WeekDaze.Model.TimetableForWeek as Model.TimetableForWeek import qualified WeekDaze.OutputConfiguration.Options as OutputConfiguration.Options import qualified WeekDaze.OutputFormat.DeterministicStudentViewTimetableSelection as OutputFormat.DeterministicStudentViewTimetableSelection import qualified WeekDaze.Text.CSS as Text.CSS import Text.XHtml.Strict((+++), (<<), (!)) -- | Used in text & html output. notApplicable :: String notApplicable = "N/A" -- | A CSS class-label, for runtime-information tables. runtimeInformationCSSIdentifier :: Text.CSS.CSSIdentifier runtimeInformationCSSIdentifier = "runtimeInformation" -- | A number of generations in the evolution of the /timetable/. type NGenerations = Int -- | Identifier used for the CSS-class for timetable-axes timetableAxisCSSIdentifier :: Text.CSS.CSSIdentifier timetableAxisCSSIdentifier = "timetableAxis" -- | Show the specified real number to a specific number of decimal places. showReal :: Real r => OutputConfiguration.Options.NDecimalDigits -> r -> String showReal nDecimalDigits = (Text.Printf.printf "%.*f" nDecimalDigits :: Double -> String) . realToFrac -- | Defines the ancillary data written by 'Implementation.EvolutionaryAlgorithm.evolveStudentViewTimetable', for one /evolution-strategy/. data EvolutionStrategyStatistics weightedMean criterionValue = MkEvolutionStrategyStatistics { getDepletionStrategyName :: String, -- ^ The name of the depletion-strategy used to liberate space in the candidate timetable. getReconstructionStrategyName :: String, -- ^ The name of the reconstruction-strategy used to re-populate the candidate timetable. getInitialFecundity :: ExecutionConfiguration.TimetableBreederFecundity.Fecundity, -- ^ The initial value of the fecundity. getFinalFecundity :: ExecutionConfiguration.TimetableBreederFecundity.Fecundity, -- ^ The value to which the initial fecundity was reduced, when the evolutionary process terminated. getMaybeImprovementFactor :: Maybe weightedMean, -- ^ The relative improvement in the /timetable/ resulting from this evolution-strategy relative to the previous, according to the weighted mean over the values of heterogeneous /timetable-criteria/; infinity is represented by 'Nothing'. getWeightedMeanOfTimetableCriteria :: weightedMean, -- ^ The /fitness/ of the /timetable/, according to the weighted mean over the values of heterogeneous /timetable-criteria/. getNGenerations :: NGenerations, -- ^ The number of generations through which the evolution progressed. getMaybeTimetableCriteriaValues :: Maybe [Maybe criterionValue] -- ^ Given at least one productive generation, the values of heterogeneous /timetable-criteria/ (where the corresponding weight is non-zero), for the selected candidate. } -- Constructor. mkEvolutionStrategyStatistics :: String -- ^ The name of the depletion-strategy used to liberate space in the candidate timetable. -> String -- ^ The name of the reconstruction-strategy used to re-populate the candidate timetable. -> ExecutionConfiguration.TimetableBreederFecundity.Fecundity -- ^ The value of the initial fecundity. -> ExecutionConfiguration.TimetableBreederFecundity.Fecundity -- ^ The value to which the initial fecundity was reduced, when the evolutionary process terminated. -> Maybe weightedMean -- ^ The relative improvement in the /timetable/ resulting from this evolution-strategy relative to the previous, according to the weighted mean over the values of heterogeneous /timetable-criteria/. -> weightedMean -- ^ The /fitness/ of the /timetable/, according to the weighted mean over the values of heterogeneous /timetable-criteria/. -> NGenerations -- ^ The number of generations through which the evolution progressed. -> Maybe [Maybe criterionValue] -- ^ Given at least one productive generation, the values of heterogeneous /timetable-criteria/ (where the corresponding weight is non-zero), for the selected candidate. -> EvolutionStrategyStatistics weightedMean criterionValue mkEvolutionStrategyStatistics depletionStrategyName reconstructionStrategyName initialFecundity finalFecundity maybeImprovement weightedMeanOfTimetableCriteria nGenerations maybeTimetableCriteriaValues = MkEvolutionStrategyStatistics { getDepletionStrategyName = depletionStrategyName, getReconstructionStrategyName = reconstructionStrategyName, getInitialFecundity = initialFecundity, getFinalFecundity = finalFecundity, getMaybeImprovementFactor = maybeImprovement, getWeightedMeanOfTimetableCriteria = weightedMeanOfTimetableCriteria, getNGenerations = nGenerations, getMaybeTimetableCriteriaValues = maybeTimetableCriteriaValues } -- | True if the initial & final fecundities are identical. isFecundityUnchanged :: EvolutionStrategyStatistics weightedMean criterionValue -> Bool isFecundityUnchanged = uncurry (==) . (getInitialFecundity &&& getFinalFecundity) -- ^ The log of the optimisation of /lesson-criteria weight/s, based on maximisation of the weighted-mean over heterogeneous /timetable-criteria/, for the initial deterministic timetable. type OptimiseLessonCriteriaWeightsLog criterionWeight weightedMean = [(Int {-trial-number-}, criterionWeight, weightedMean)] -- | Compose the text returned lazily at runtime, to aid diagnosis. composeRuntimeLog :: ( Real criterionValue, Real criterionWeight, Real mean, Real standardDeviation, Real weightedMean ) => ExecutionConfiguration.ExecutionOptions.ExecutionOptions criterionWeight fecundityDecayRatio populationDiversityRatio -> OutputConfiguration.Options.NDecimalDigits -- ^ The required precision in decimal digits. -> (Bool, Bool) -- ^ Whether the /traversal-order/ was specified, & whether the input file-path was specified. -> Bool -- ^ Whether the fecundity of all /evolution-strategies/ have been set to zero. -> ExecutionConfiguration.LessonCriteriaWeights.LessonCriteriaWeights criterionWeight -- ^ The unoptimised /lesson-criteria/ weights. -> Bool -- ^ Whether to accept a proposed set of /lesson-criteria weights/ on the basis of the mean (as opposed to the maximum) over the specified raster-scans, of the weighted mean over all heterogeneous /timetable-criteria/. -> OptimiseLessonCriteriaWeightsLog criterionWeight weightedMean -> OutputFormat.DeterministicStudentViewTimetableSelection.DeterministicStudentViewTimetableSelection criterionValue criterionWeight level locationId mean standardDeviation teacherId timeslotId weightedMean -> [EvolutionStrategyStatistics weightedMean criterionValue] -> [Maybe criterionValue] -- ^ The final values of timetable-criteria. -> [(String, String)] composeRuntimeLog executionOptions nDecimalDigits (traversalOrderWasSpecified, inputFilePathWasSpecified) areAllEvolutionStrategiesZero originalLessonCriteriaWeights useMeanOverRasterScans optimiseLessonCriteriaWeightsLog deterministicStudentViewTimetableSelection evolutionStrategyStatisticsList finalTimetableCriteriaValues = Data.Maybe.catMaybes [ if null optimiseLessonCriteriaWeightsLog then Nothing else Just ( "By randomly mutating lesson-criteria weights by the specified magnitude, the " ++ ( if useMeanOverRasterScans then "mean over" else "maximum of" ) ++ " all specified raster-scans, of the weighted mean over heterogeneous timetable-criteria, for the " ++ hintText ++ ", was sequentially improved by", showListOfStrings $ map ( \(trialNumber, changeMagnitude, deltaOverRastersOfWeightedMeanOverTimetableCriteria) -> showChar '(' . shows trialNumber . showChar ',' $ showReal nDecimalDigits changeMagnitude ++ ")=>" ++ showReal nDecimalDigits deltaOverRastersOfWeightedMeanOverTimetableCriteria ) optimiseLessonCriteriaWeightsLog ), -- Pair. let preamble = "The weighted mean over the values of heterogeneous timetable-criteria, of the " in Just . either ( (,) (preamble ++ hintText) . showUnitInterval ) ( \weightedMeanOfTimetableCriteriaByTraversalOrder -> ( preamble ++ "deterministic timetable resulting from each raster-scan; & the maximum", showListOfReals (map snd weightedMeanOfTimetableCriteriaByTraversalOrder) ++ "; " ++ uncurry (++) ( show *** showString "=>" . showUnitInterval $ Data.List.maximumBy (Data.Ord.comparing snd) weightedMeanOfTimetableCriteriaByTraversalOrder ) ) -- Pair. ) $ OutputFormat.DeterministicStudentViewTimetableSelection.getWeightedMeanOfTimetableCriteria deterministicStudentViewTimetableSelection, Just ( "The value of each timetable-criterion, for the " ++ hintText, showListOfReals . map (Data.Maybe.fromMaybe 0) $ OutputFormat.DeterministicStudentViewTimetableSelection.getInitialTimetableCriteriaValues deterministicStudentViewTimetableSelection ), -- Pair. let initialLessonCriteriaStatistics = OutputFormat.DeterministicStudentViewTimetableSelection.getInitialLessonCriteriaStatistics deterministicStudentViewTimetableSelection in if null initialLessonCriteriaStatistics then Nothing else Just ( "The (weight, (mean, standard deviation), (minimum, maximum)) gathered over the lessons of the " ++ hintText ++ ", for the value of each individual lesson-criterion", showListOfStrings . map ( \((_, lessonCriterionWeightAccessor), maybeLessonCriteriaStatistics) -> Data.Maybe.maybe notApplicable ( \lessonCriterionStatistics -> let [lessonCriterionWeight, lessonCriterionWeight'] = map ( showUnitInterval . ExecutionConfiguration.CriterionWeight.deconstruct . lessonCriterionWeightAccessor ) [ originalLessonCriteriaWeights, ExecutionConfiguration.ExecutionOptions.getLessonCriteriaWeights executionOptions ] ((mean, standardDeviation), (minimumValue, maximumValue)) = (showUnitInterval *** showUnitInterval) *** (showUnitInterval *** showUnitInterval) $ lessonCriterionStatistics in Text.Printf.printf "(%s,(%s,%s),(%s,%s))" ( if null optimiseLessonCriteriaWeightsLog || lessonCriterionWeight == lessonCriterionWeight' then lessonCriterionWeight else lessonCriterionWeight ++ "->" ++ lessonCriterionWeight' ) mean standardDeviation minimumValue maximumValue ) maybeLessonCriteriaStatistics ) $ zip ExecutionConfiguration.LessonCriteriaWeights.associationList initialLessonCriteriaStatistics ), -- Pair if areAllEvolutionStrategiesZero then Nothing else Just ( "The (weighted mean over heterogeneous timetable-criteria, relative improvement in the weighted mean over heterogeneous timetable-criteria, the number of generations through which the timetable evolved, the fecundity, & the value of each timetable-criterion for the best candidate), for each evolution-strategy", showListOfStrings $ map ( \evolutionStrategyStatistics -> Text.Printf.printf "%s/%s=(%s, %s, %d, %s, %s)" ( getDepletionStrategyName evolutionStrategyStatistics ) ( getReconstructionStrategyName evolutionStrategyStatistics ) ( showUnitInterval $ getWeightedMeanOfTimetableCriteria evolutionStrategyStatistics ) ( Data.Maybe.maybe "Infinity" ( \improvementFactor -> if improvementFactor == 0 then "0" else showString (showReal nDecimalDigits (100 * improvementFactor)) "%" ) $ getMaybeImprovementFactor evolutionStrategyStatistics ) ( getNGenerations evolutionStrategyStatistics ) ( ( \(x, y) -> show x ++ if x == y then "" else "->" ++ show y ) $ (getInitialFecundity &&& getFinalFecundity) evolutionStrategyStatistics ) ( Data.Maybe.maybe notApplicable showListOfStrings $ map ( Data.Maybe.maybe notApplicable showUnitInterval ) `fmap` getMaybeTimetableCriteriaValues evolutionStrategyStatistics ) ) evolutionStrategyStatisticsList ), -- Pair. if areAllEvolutionStrategiesZero then Nothing else Just ( "The final value of each timetable-criterion, & the improvement, from the " ++ hintText, showListOfStrings . zipWith ( \maybeFinalWeightedTimetableCriterion maybeInitialWeightedTimetableCriterion -> case (maybeFinalWeightedTimetableCriterion, maybeInitialWeightedTimetableCriterion) of (Just finalWeightedTimetableCriterion, Just initialWeightedTimetableCriterion) -> Text.Printf.printf "(%s,%s)" ( showUnitInterval finalWeightedTimetableCriterion ) . showUnitInterval $ finalWeightedTimetableCriterion - initialWeightedTimetableCriterion _ -> notApplicable ) finalTimetableCriteriaValues $ OutputFormat.DeterministicStudentViewTimetableSelection.getInitialTimetableCriteriaValues deterministicStudentViewTimetableSelection ) -- Pair. ] where hintText :: String hintText | traversalOrderWasSpecified = "deterministic timetable resulting from all specified raster-scans" -- There may be more than one. | inputFilePathWasSpecified = "specified timetable" | otherwise = "best deterministic timetable" showListOfStrings :: [String] -> String showListOfStrings = showChar '[' . (`showString` "]") . Data.List.intercalate ", " showUnitInterval :: Real r => r -> String showUnitInterval 0 = "0" showUnitInterval 1 = "1" showUnitInterval x = showReal nDecimalDigits x showListOfReals :: Real r => [r] -> String showListOfReals = showListOfStrings . map showUnitInterval -- | Render the results in XHTML. toHtml :: ( RealFrac criterionValue, RealFrac criterionWeight, RealFrac standardDeviation, RealFrac mean, RealFrac weightedMean, Show criterionValue, Show criterionWeight, Show mean, Show standardDeviation, Show weightedMean ) => ExecutionConfiguration.ExecutionOptions.ExecutionOptions criterionWeight fecundityDecayRatio populationDiversityRatio -> OutputConfiguration.Options.NDecimalDigits -- ^ The required number of decimal digits. -> ExecutionConfiguration.LessonCriteriaWeights.LessonCriteriaWeights criterionWeight -- ^ The unoptimised /lesson-criteria/ weights. -> OptimiseLessonCriteriaWeightsLog criterionWeight weightedMean -> OutputFormat.DeterministicStudentViewTimetableSelection.DeterministicStudentViewTimetableSelection criterionValue criterionWeight level locationId mean standardDeviation teacherId timeslotId weightedMean -> [EvolutionStrategyStatistics weightedMean criterionValue] -> Text.XHtml.Strict.Html toHtml executionOptions nDecimalDigits originalLessonCriteriaWeights optimiseLessonCriteriaWeightsLog deterministicStudentViewTimetableSelection evolutionStrategyStatisticsList = ( if null optimiseLessonCriteriaWeightsLog then Text.XHtml.Strict.noHtml else Text.XHtml.Strict.paragraph ! [ Text.XHtml.Strict.theclass Text.CSS.infoCSSIdentifier ] << ( "By randomly mutating lesson-criteria weights by the specified magnitude, the " ++ ( if useMeanOverRasterScans then "mean over" else "maximum of" ) ++ " all specified raster-scans, of the weighted mean over heterogeneous timetable-criteria, for the initial deterministic timetable, was improved by; " +++ Data.List.intersperse ( Text.XHtml.Strict.toHtml ", " ) ( map ( \(trialNumber, changeMagnitude, deltaOverRastersOfWeightedMeanOverTimetableCriteria) -> '(' +++ ( Text.XHtml.Strict.thespan ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier, Text.XHtml.Strict.title "Trial-number." ] << trialNumber ) +++ ',' +++ ( Text.XHtml.Strict.thespan ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier, Text.XHtml.Strict.title "Change-magnitude." ] << showReal nDecimalDigits changeMagnitude ) +++ ')' +++ Text.XHtml.Strict.spaceHtml +++ Text.XHtml.Strict.primHtmlChar "rArr" +++ Text.XHtml.Strict.spaceHtml +++ ( colourUnitIntervalSpan deltaOverRastersOfWeightedMeanOverTimetableCriteria ! [ Text.XHtml.Strict.title $ "Incremental improvement in the " ++ ( if useMeanOverRasterScans then "mean over" else "maximum of" ) ++ " all specified raster-scans, of the weighted mean over heterogeneous timetable-criteria." ] ) ) optimiseLessonCriteriaWeightsLog ) +++ "." ) ) +++ either ( ( Text.XHtml.Strict.paragraph ! [ Text.XHtml.Strict.theclass Text.CSS.infoCSSIdentifier ] << ) . ( ( "The weighted mean value over heterogeneous timetable-criteria of the single deterministic timetable" +++ Data.Maybe.maybe Text.XHtml.Strict.noHtml ( ( Text.XHtml.Strict.thespan ! [ Text.XHtml.Strict.theclass Text.CSS.dataCSSIdentifier ] << ) . either ( showString " resulting from the raster-scan " . show ) ( showString " specified in the file " . show ) ) ( ExecutionConfiguration.ExecutionOptions.getMaybeHint executionOptions ) +++ ", is " ) +++ ) . ( +++ "." -- Terminate ) . colourUnitIntervalSpan -- ExecutionConfiguration.ExecutionOptions.getMaybeHint was specified. ) ( \weightedMeanOfTimetableCriteriaByTraversalOrder -> Text.XHtml.Strict.table ! [ Text.XHtml.Strict.theclass runtimeInformationCSSIdentifier, Text.XHtml.Strict.identifier "weightedMeanOfTimetableCriteriaByTraversalOrderTable" ] << ( [ Text.XHtml.Strict.tr << ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan 4 -- Spans axis-triple & the weighted-mean over the values of heterogeneous timetable-criteria. ] << ( "The Weighted Mean over the values of Heterogeneous Timetable-criteria," +++ Text.XHtml.Strict.br +++ "of the Deterministic Timetable resulting from each Raster-scan." ) ), Text.XHtml.Strict.tr << [ Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan 3 -- Spans axis-triple. ] << "Slowest-changing to Fastest-changing Coordinate.", Text.XHtml.Strict.th ! [ Text.XHtml.Strict.title "Weighted Mean over the values of Heterogeneous Timetable-criteria." ] << mu ] ] ++ map ( \(timetableAxisTriple, weightedMeanOfTimetableCriteria) -> ( Text.XHtml.Strict.tr ! if timetableAxisTriple == fst (Data.List.maximumBy (Data.Ord.comparing snd) weightedMeanOfTimetableCriteriaByTraversalOrder) then [ Text.XHtml.Strict.identifier "selectedRaster", Text.XHtml.Strict.title "Selected Raster-scan." ] else [] ) << let (x, y, z) = Model.TimetableAxisTriple.deconstruct timetableAxisTriple renderAxis = ( Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass timetableAxisCSSIdentifier ] << ) . show in [ renderAxis x, renderAxis y, renderAxis z, colourUnitIntervalTD weightedMeanOfTimetableCriteria ] ) weightedMeanOfTimetableCriteriaByTraversalOrder ++ [ Text.XHtml.Strict.tr << ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan 3, -- Spans axis-triple. Text.XHtml.Strict.title "The Mean, over the Weighted Mean, over the values of Heterogeneous Timetable-criteria." ] << mu +++ colourUnitIntervalTD ( Factory.Math.Statistics.getMean $ map snd {-weightedMean-} weightedMeanOfTimetableCriteriaByTraversalOrder :: Rational ) ) ] ) ) ( OutputFormat.DeterministicStudentViewTimetableSelection.getWeightedMeanOfTimetableCriteria deterministicStudentViewTimetableSelection ) +++ let initialLessonCriteriaStatistics = OutputFormat.DeterministicStudentViewTimetableSelection.getInitialLessonCriteriaStatistics deterministicStudentViewTimetableSelection in ( if null initialLessonCriteriaStatistics then Text.XHtml.Strict.noHtml -- Otherwise mean of null list fails. else Text.XHtml.Strict.table ! [ Text.XHtml.Strict.theclass runtimeInformationCSSIdentifier, Text.XHtml.Strict.identifier "initialLessonCriteriaStatisticsTable" ] << ( let statisticsHeaders :: [Text.XHtml.Strict.Html] statisticsHeaders = map ( \(title, contents) -> Text.XHtml.Strict.th ! [ Text.XHtml.Strict.title title ] << contents ) [ ( "The mean value", mu ), ( "The standard deviation.", sigma ), ( "The minimum value.", Text.XHtml.Strict.toHtml "Min." ), ( "The maximum value.", Text.XHtml.Strict.toHtml "Max." ) ] nStatisticsHeaders :: Int nStatisticsHeaders = length statisticsHeaders in [ Text.XHtml.Strict.tr << ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan $ ( if wereOptimisedLessonCriteriaWeights then succ -- Spans 'Optimised'. else id ) ( 2 {-spans 'Name' & 'Original'-} + nStatisticsHeaders ) ] << ( "Statistics gathered for the values of each Lesson-criterion in isolation," +++ Text.XHtml.Strict.br +++ "Evaluated over each of the lessons of the best Deterministic Timetable." ) ), Text.XHtml.Strict.tr << ( [ Text.XHtml.Strict.th ! [ Text.XHtml.Strict.rowspan 2 ] << "Name", Text.XHtml.Strict.th ! [ if wereOptimisedLessonCriteriaWeights then Text.XHtml.Strict.colspan 2 -- Spans 'Original' & 'Optimised'. else Text.XHtml.Strict.rowspan 2, Text.XHtml.Strict.title "The weight of this criterion, in the weighted mean over the values of heterogeneous lesson-criteria." ] << "Weight" ] ++ [ Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan nStatisticsHeaders, Text.XHtml.Strict.title "Statistics gathered for the values of each lesson-criterion in isolation, evaluated over each of the lessons of the best deterministic timetable." ] << "Statistics" ] ), Text.XHtml.Strict.tr << ( if wereOptimisedLessonCriteriaWeights then ( map (Text.XHtml.Strict.th <<) ["Original", "Optimised"] ++ ) -- Section. else id ) statisticsHeaders ] ++ map ( \((lessonCriterionTag, lessonCriterionWeightAccessor), maybeLessonCriteriaStatistics) -> Text.XHtml.Strict.tr << ( ( ( Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass "lessonCriterion" ] << lessonCriterionTag ) : let [lessonCriterionWeight, lessonCriterionWeight'] = map ( ExecutionConfiguration.CriterionWeight.deconstruct . lessonCriterionWeightAccessor ) [ originalLessonCriteriaWeights, ExecutionConfiguration.ExecutionOptions.getLessonCriteriaWeights executionOptions ] in if wereOptimisedLessonCriteriaWeights then if lessonCriterionWeight == lessonCriterionWeight' then [ colourUnitIntervalTD lessonCriterionWeight ! [Text.XHtml.Strict.colspan 2] ] else map colourUnitIntervalTD [lessonCriterionWeight, lessonCriterionWeight'] else [ colourUnitIntervalTD lessonCriterionWeight ] ) ++ Data.Maybe.maybe [ Text.XHtml.Strict.td ! [ Text.XHtml.Strict.colspan nStatisticsHeaders, Text.XHtml.Strict.theclass Model.TimetableForWeek.unavailableCSSIdentifier, Text.XHtml.Strict.title zeroWeight ] << notApplicable ] ( \((mean, standardDeviation), (min', max')) -> [ colourUnitIntervalTD mean, colourUnitIntervalTD standardDeviation, colourUnitIntervalTD min', colourUnitIntervalTD max' ] ) maybeLessonCriteriaStatistics ) ) ( zip ExecutionConfiguration.LessonCriteriaWeights.associationList initialLessonCriteriaStatistics ) ) ) +++ Text.XHtml.Strict.table ! [ Text.XHtml.Strict.theclass runtimeInformationCSSIdentifier, Text.XHtml.Strict.identifier "evolutionStrategyStatisticsTable" ] << ( let tableHeaders :: [Text.XHtml.Strict.Html] tableHeaders = map ( \(title, contents) -> Text.XHtml.Strict.th ! [ Text.XHtml.Strict.title title ] << contents ) [ ( "The strategy by which space is liberated in a candidate-timetable, to permit potentially beneficial mutation.", "Depletion" ), ( "The strategy by which a candidate-timetable is reconstructed, after application of the chosen depletion-strategy.", "Reconstruction" ), ( "The initial size of the breeding-program for this evolution-strategy.", "Initial" ), ( "The final size of the breeding-program on termination of this evolution-strategy, which may be less than the original size should that have proven excessive.", "Final" ) ] nColumns, nTimetableCriteria :: Int nColumns = length tableHeaders + 3 {-double height headers from previous row-} nTimetableCriteria = length ExecutionConfiguration.TimetableCriteriaWeights.associationList in [ Text.XHtml.Strict.tr << ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan $ nColumns + nTimetableCriteria ] << ( "The Improvement in the value of each Timetable-criterion, resulting from each Evolution-strategy, where " +++ Data.Maybe.maybe ( Text.XHtml.Strict.toHtml "a Variable number of" ) ( (+++ " Initial") . ( Text.XHtml.Strict.thespan ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier ] << ) -- Section. ) ( ExecutionConfiguration.EvolutionStrategies.getMaybeNInitialScouts $ ExecutionConfiguration.ExecutionOptions.getEvolutionStrategies executionOptions ) +++ " Scouts were Dispatched." ) ), Text.XHtml.Strict.tr ! [ Text.XHtml.Strict.identifier "timetableCriteriaWeights" ] << ( ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan nColumns ] << ( "Timetable-criteria Weights" +++ Text.XHtml.Strict.spaceHtml +++ Text.XHtml.Strict.primHtmlChar "rarr" ) ) : map ( \(timetableCriterionTag, timetableCriterionWeightAccessor) -> colourUnitIntervalTD ( ExecutionConfiguration.CriterionWeight.deconstruct . timetableCriterionWeightAccessor $ ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights executionOptions ) ! [ Text.XHtml.Strict.title timetableCriterionTag ] ) ExecutionConfiguration.TimetableCriteriaWeights.associationList ), Text.XHtml.Strict.tr ! [ Text.XHtml.Strict.identifier "bestDeterministicWeightedTimetableCriteria" ] << ( ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan nColumns ] << ( "The value of each Timetable-criterion, for the Best Deterministic Timetable" +++ Text.XHtml.Strict.spaceHtml +++ Text.XHtml.Strict.primHtmlChar "rarr" ) ) : zipWith ( \(timetableCriterionTag, _) -> Data.Maybe.maybe ( Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Model.TimetableForWeek.unavailableCSSIdentifier, Text.XHtml.Strict.title zeroWeight ] << notApplicable ) ( (! [Text.XHtml.Strict.title timetableCriterionTag]) . colourUnitIntervalTD ) ) ExecutionConfiguration.TimetableCriteriaWeights.associationList ( OutputFormat.DeterministicStudentViewTimetableSelection.getInitialTimetableCriteriaValues deterministicStudentViewTimetableSelection ) ) ] ++ if null evolutionStrategyStatisticsList then [] else [ Text.XHtml.Strict.tr << [ Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan 2, -- Spans 'Depletion' & 'Reconstruction'. Text.XHtml.Strict.title "The means by which a candidate-timetable is mutated, in the search for improvement." ] << "Chronologically ordered Evolution-strategies", Text.XHtml.Strict.th ! [ Text.XHtml.Strict.colspan 2, -- Spans 'Initial' & 'Final'. Text.XHtml.Strict.title "The size of the breeding-program for this evolution-strategy." ] << "Fecundity", Text.XHtml.Strict.th ! [ Text.XHtml.Strict.rowspan 2, -- Double height. Text.XHtml.Strict.title "The number of generations through which the timetable evolved, using this evolution-strategy." ] << "Generations", Text.XHtml.Strict.th ! [ Text.XHtml.Strict.rowspan 2, -- Double height. Text.XHtml.Strict.title "The weighted mean over the values of heterogeneous timetable-criteria." ] << mu, Text.XHtml.Strict.th ! [ Text.XHtml.Strict.rowspan 2, -- Double height. Text.XHtml.Strict.title "The improvement in the weighted mean over the values of heterogeneous timetable-criteria, relative to the previous phase." ] << delta, Text.XHtml.Strict.th ! [ Text.XHtml.Strict.rowspan 2, -- Double height. Text.XHtml.Strict.colspan nTimetableCriteria, Text.XHtml.Strict.title "The value of each timetable-criterion, for the best candidate resulting from a specific evolution-strategy." ] << "Timetable-criterion Values" ], Text.XHtml.Strict.tr << tableHeaders ] ++ map ( \evolutionStrategyStatistics -> let isFecundityUnchanged' = isFecundityUnchanged evolutionStrategyStatistics in Text.XHtml.Strict.tr << ( [ Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass "depletionStrategy" ] << getDepletionStrategyName evolutionStrategyStatistics, Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass "reconstructionStrategy" ] << getReconstructionStrategyName evolutionStrategyStatistics, Text.XHtml.Strict.td ! [ Text.XHtml.Strict.colspan $ if isFecundityUnchanged' then 2 else 1, Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier ] << getInitialFecundity evolutionStrategyStatistics, if isFecundityUnchanged' then Text.XHtml.Strict.noHtml else Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier ] << getFinalFecundity evolutionStrategyStatistics, Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier ] << getNGenerations evolutionStrategyStatistics ] ++ Data.Maybe.maybe [ Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Model.TimetableForWeek.unavailableCSSIdentifier, Text.XHtml.Strict.colspan $ nTimetableCriteria + 2 {-spans mu & delta-}, Text.XHtml.Strict.title "Zero productive generations of evolution occurred." ] << notApplicable ] ( ( [ colourUnitIntervalTD $ getWeightedMeanOfTimetableCriteria evolutionStrategyStatistics, Data.Maybe.maybe infiniteValue ( \improvementFactor -> ( Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier, Text.XHtml.Strict.thestyle $ "color: " ++ Colour.HTMLColour.unitIntervalToHTMLColourCode (1 - recip (1 + improvementFactor)) -- Map the interval [0, infinity) to [0, 1]. ] << ) . (`showString` "%") . showReal nDecimalDigits $ 100 * improvementFactor ) $ getMaybeImprovementFactor evolutionStrategyStatistics ] ++ ) . zipWith ( \(timetableCriterionTag, _) -> Data.Maybe.maybe ( Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Model.TimetableForWeek.unavailableCSSIdentifier, Text.XHtml.Strict.title zeroWeight ] << notApplicable ) ( (! [Text.XHtml.Strict.title timetableCriterionTag]) . colourUnitIntervalTD ) ) ExecutionConfiguration.TimetableCriteriaWeights.associationList ) ( getMaybeTimetableCriteriaValues evolutionStrategyStatistics ) ) ) evolutionStrategyStatisticsList ++ take 1 {-perhaps none-} ( map ( \finalTimetableCriteriaValues -> Text.XHtml.Strict.tr << ( ( Text.XHtml.Strict.th ! [ Text.XHtml.Strict.title "The weighted change in the value of each timetable-criterion, from the best Deterministic Timetable.", Text.XHtml.Strict.colspan 7 -- Depletion, Reconstruction, Initial, Final, Generations, Mu, Delta. ] << delta ) : Data.List.zipWith3 ( \(timetableCriterionTag, timetableCriterionWeightAccessor) maybeFinalTimetableCriterionValue maybeInitialTimetableCriterionValue -> case (maybeFinalTimetableCriterionValue, maybeInitialTimetableCriterionValue) of (Just finalTimetableCriterionValue, Just initialTimetableCriterionValue) -> let deltaTimetableCriterionValue = finalTimetableCriterionValue - initialTimetableCriterionValue in Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier, Text.XHtml.Strict.thestyle $ "color: " ++ Colour.HTMLColour.unitIntervalToHTMLColourCode ((1 + deltaTimetableCriterionValue) / 2), -- Map the interval [-1, 1] -> [0, 1]. Text.XHtml.Strict.title timetableCriterionTag ] << Text.XHtml.Strict.lineToHtml {-embed nbsps-} ( case deltaTimetableCriterionValue of 0 -> Text.Printf.printf "%-*d" (nDecimalDigits + 2) (0 :: Int) -- Left-justify in a fixed-width field. _ -> showReal nDecimalDigits . ( * realToFrac deltaTimetableCriterionValue ) . ExecutionConfiguration.CriterionWeight.deconstruct . timetableCriterionWeightAccessor $ ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights executionOptions ) _ -> Text.XHtml.Strict.td ! [ Text.XHtml.Strict.theclass Model.TimetableForWeek.unavailableCSSIdentifier, Text.XHtml.Strict.title zeroWeight ] << notApplicable ) ExecutionConfiguration.TimetableCriteriaWeights.associationList finalTimetableCriteriaValues ( OutputFormat.DeterministicStudentViewTimetableSelection.getInitialTimetableCriteriaValues deterministicStudentViewTimetableSelection ) ) ) . Data.Maybe.mapMaybe getMaybeTimetableCriteriaValues {-weed-out those with zero productive generations-} $ reverse {-the last set of results is required-} evolutionStrategyStatisticsList ) ) where wereOptimisedLessonCriteriaWeights, useMeanOverRasterScans :: Bool (wereOptimisedLessonCriteriaWeights, useMeanOverRasterScans) = ExecutionConfiguration.OptimiseLessonCriteriaWeights.isRequired &&& ExecutionConfiguration.OptimiseLessonCriteriaWeights.getUseMeanOverRasterScans $ ExecutionConfiguration.ExecutionOptions.getOptimiseLessonCriteriaWeights executionOptions colourUnitInterval :: (RealFrac r, Show r) => (Text.XHtml.Strict.Html -> Text.XHtml.Strict.Html) -> r -> Text.XHtml.Strict.Html colourUnitInterval element x = element ! [ Text.XHtml.Strict.theclass Text.CSS.numericDataCSSIdentifier, Text.XHtml.Strict.thestyle $ "color: " ++ Colour.HTMLColour.unitIntervalToHTMLColourCode x ] << ( Text.XHtml.Strict.lineToHtml {-embed nbsps-} . if x `elem` [0, 1] then ( Text.Printf.printf "%-*d" (nDecimalDigits + 2) :: Int -> String -- Left-justify in a fixed-width field. ) . round else showReal nDecimalDigits ) x colourUnitIntervalSpan, colourUnitIntervalTD :: (RealFrac r, Show r) => r -> Text.XHtml.Strict.Html colourUnitIntervalSpan = colourUnitInterval Text.XHtml.Strict.thespan colourUnitIntervalTD = colourUnitInterval Text.XHtml.Strict.td zeroWeight :: String zeroWeight = "Zero weight" delta, mu, sigma, infinity :: Text.XHtml.Strict.Html [delta, mu, sigma, infinity] = map ( uncurry (<<) . ( (Text.XHtml.Strict.abbr !) . return {-to List-monad-} . Text.XHtml.Strict.title &&& Text.XHtml.Strict.primHtmlChar ) ) ["Delta", "mu", "sigma", "infin"] infiniteValue = Text.XHtml.Strict.td ! [ Text.XHtml.Strict.title "Infinity" ] << infinity