{-# LANGUAGE CPP, FlexibleContexts #-} {- Copyright (C) 2013-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 all configured options. * These options are categorised into those which; define the problem, drive the implementation of the solution, & govern the required output; & consequently, most of this module's interface merely proxies requests to the appropriate sub-module. -} module WeekDaze.Input.Options( -- * Types -- ** Type-synonyms ShowLocationViewOfTimetable, ShowTeacherViewOfTimetable, -- Mutator, -- ** Data-types Options(..), -- * Constants tag, -- * Functions -- ** Mutators appendOutputStudentViewTimetableFilePath, -- criteriaWeightsMutator, setDisplayRuntimeInformation, setFecundityDecayRatio, setInputStudentViewTimetableFilePath, setMaybeNInitialScouts, setMaybeOutputConfigFilePath, setMaybeRandomSeed, setMinimumPopulationDiversityRatio, setNDecimalDigits, setOptimiseLessonCriteriaWeights, -- setLessonCriteriaWeights, setPermitTemporaryStudentBodyMerger, setReduceStudentBodyRegister, setRemoveRedundantCourses, -- setTimetableCriteriaWeights, setTimeslotIdBounds, setVerbosity, -- setWeekend, zeroInappropriateExecutionOptions ) where import Control.Arrow((&&&)) import qualified Control.Arrow import qualified Control.Monad.Writer import qualified Data.Default import qualified Data.Foldable import qualified Data.Map import qualified Data.Maybe import qualified Data.Set import qualified Distribution.Verbosity import qualified Factory.Data.Interval import qualified System.FilePath import qualified Text.XML.HXT.Arrow.Pickle as HXT import qualified Text.XML.HXT.DOM.Util import qualified ToolShed.SelfValidate import qualified WeekDaze.Aggregate.LocationCatalogue as Aggregate.LocationCatalogue import qualified WeekDaze.Aggregate.StudentBodyRegister as Aggregate.StudentBodyRegister import qualified WeekDaze.Aggregate.StudentClass as Aggregate.StudentClass import qualified WeekDaze.Aggregate.TeacherRegister as Aggregate.TeacherRegister import qualified WeekDaze.Configuration as Configuration import qualified WeekDaze.Data.Course as Data.Course import qualified WeekDaze.Data.Student as Data.Student import qualified WeekDaze.Data.Teacher as Data.Teacher 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.Input.ConfigVersion as Input.ConfigVersion import qualified WeekDaze.OutputConfiguration.FileFormat as OutputConfiguration.FileFormat import qualified WeekDaze.OutputConfiguration.Format as OutputConfiguration.Format import qualified WeekDaze.OutputConfiguration.Options as OutputConfiguration.Options import qualified WeekDaze.OutputConfiguration.Style as OutputConfiguration.Style import qualified WeekDaze.OutputConfiguration.View as OutputConfiguration.View import qualified WeekDaze.ProblemConfiguration.ProblemParameters as ProblemConfiguration.ProblemParameters import qualified WeekDaze.ProblemConfiguration.ProblemValidationSwitches as ProblemConfiguration.ProblemValidationSwitches import qualified WeekDaze.Size as Size import qualified WeekDaze.Temporal.Day as Temporal.Day #ifdef USE_HDBC import qualified Control.DeepSeq import qualified Database.HDBC import qualified Data.Convertible import qualified Data.Typeable import qualified WeekDaze.Database.Selector as Database.Selector instance ( Control.DeepSeq.NFData campus, Control.DeepSeq.NFData criterionWeight, Control.DeepSeq.NFData level, Control.DeepSeq.NFData fecundityDecayRatio, Control.DeepSeq.NFData locationId, Control.DeepSeq.NFData minimumContrastRatio, Control.DeepSeq.NFData populationDiversityRatio, Control.DeepSeq.NFData stream, Control.DeepSeq.NFData synchronisationId, Control.DeepSeq.NFData teacherId, Control.DeepSeq.NFData teachingRatio, Control.DeepSeq.NFData timeslotId, Data.Convertible.Convertible Database.HDBC.SqlValue campus, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue criterionWeight, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue fecundityDecayRatio, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue level, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue locationId, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue populationDiversityRatio, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue stream, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue synchronisationId, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue teacherId, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue teachingRatio, -- Flexible context. Data.Convertible.Convertible Database.HDBC.SqlValue timeslotId, -- Flexible context. Data.Default.Default campus, Data.Default.Default stream, Data.Typeable.Typeable criterionWeight, Data.Typeable.Typeable fecundityDecayRatio, Data.Typeable.Typeable populationDiversityRatio, Data.Typeable.Typeable teachingRatio, Fractional minimumContrastRatio, Ord level, Ord locationId, Ord synchronisationId, Ord teacherId, Ord timeslotId, RealFrac criterionWeight, RealFrac fecundityDecayRatio, RealFrac populationDiversityRatio, RealFrac teachingRatio, Show campus, Show criterionWeight, Show level, Show locationId, Show minimumContrastRatio, Show synchronisationId, Show timeslotId ) => Database.Selector.Selector (Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId) where fromDatabase connection projectIdSql = let tableName :: Database.Selector.TableName tableName = Database.Selector.tablePrefix ++ tag in do configVersionStrings <- map head {-for each row select the only column-} `fmap` Database.Selector.select connection [Input.ConfigVersion.tag] [tableName] [(Database.Selector.projectIdColumnName, projectIdSql)] -- Extract the configVersion for this project; there can only be zero or one row. (problemParameters, executionOptions, outputOptions) #ifdef QUERY_DB_CONCURRENTLY <- Database.Selector.fromDatabaseConcurrently connection projectIdSql #else <- Database.Selector.fromDatabase connection projectIdSql #endif return {-to IO-monad-} MkOptions { getConfigVersion = if null configVersionStrings then Input.ConfigVersion.defaultConfigVersion else {-defined-} Data.Maybe.maybe ( error $ "WeekDaze.Input.Options.fromDatabase:\tnull " ++ show Input.ConfigVersion.tag ++ "." ) Input.ConfigVersion.fromString . Database.HDBC.fromSql $ head configVersionStrings, getProblemParameters = problemParameters, getExecutionOptions = executionOptions, getOutputOptions = outputOptions } #endif /* USE_HDBC */ -- | The type of a function used to transform 'Options'. type Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId = Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId -> Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId -- | Mutator. appendOutputStudentViewTimetableFilePath :: Show minimumContrastRatio => System.FilePath.FilePath -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId appendOutputStudentViewTimetableFilePath filePath options = options { getOutputOptions = outputOptions { OutputConfiguration.Options.getFileFormats = OutputConfiguration.FileFormat.mkFileFormat filePath (OutputConfiguration.Format.XML OutputConfiguration.View.StudentView) : OutputConfiguration.Options.getFileFormats outputOptions } } where outputOptions = getOutputOptions options -- Used to qualify XML. tag :: String tag = "options" -- | Defines whether the /timetable/ should be shown, from the point-of-view of each /location/. type ShowLocationViewOfTimetable = Bool -- | Defines whether the /timetable/ should be shown, from the point-of-view of each /teacher/. type ShowTeacherViewOfTimetable = Bool -- | Defines the command-line options. data Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId = MkOptions { getConfigVersion :: Input.ConfigVersion.ConfigVersion, -- ^ The version of the semantics & structure of the configuration-file; cf. the version of XML in which it's written. getExecutionOptions :: ExecutionConfiguration.ExecutionOptions.ExecutionOptions criterionWeight fecundityDecayRatio populationDiversityRatio, -- ^ Defines the means by which the problem should be solved. getOutputOptions :: OutputConfiguration.Options.Options minimumContrastRatio, -- ^ Defines the presentation of the results. getProblemParameters :: ProblemConfiguration.ProblemParameters.ProblemParameters campus level locationId stream synchronisationId teacherId teachingRatio timeslotId -- ^ Defines the parameters of the problem. } deriving (Eq, Show) instance ( Enum timeslotId, Num fecundityDecayRatio, Num populationDiversityRatio, Ord fecundityDecayRatio, Ord level, Ord locationId, Ord populationDiversityRatio, Ord stream, Ord synchronisationId, Ord teacherId, Ord timeslotId, Real criterionWeight, RealFrac teachingRatio, Show criterionWeight, Show fecundityDecayRatio, Show level, Show locationId, Show minimumContrastRatio, Show populationDiversityRatio, Show stream, Show synchronisationId, Show teacherId, Show teachingRatio, Show timeslotId ) => ToolShed.SelfValidate.SelfValidator (Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId) where getErrors MkOptions { getExecutionOptions = executionOptions, getOutputOptions = outputOptions, getProblemParameters = problemParameters } | not $ ToolShed.SelfValidate.isValid executionOptions = ToolShed.SelfValidate.getErrors executionOptions | not $ ToolShed.SelfValidate.isValid outputOptions = ToolShed.SelfValidate.getErrors outputOptions | not $ ToolShed.SelfValidate.isValid problemParameters = ToolShed.SelfValidate.getErrors problemParameters | otherwise = ToolShed.SelfValidate.extractErrors [ ( ExecutionConfiguration.ExecutionOptions.getPermitTemporaryStudentBodyMerger executionOptions && uncurry (||) ( ProblemConfiguration.ProblemValidationSwitches.getCheckIfStudentBodiesExceedTeachers &&& ProblemConfiguration.ProblemValidationSwitches.getCheckIfStudentBodySizeExceedsLocationCapacity $ ProblemConfiguration.ProblemParameters.getProblemValidationSwitches problemParameters ), show ExecutionConfiguration.ExecutionOptions.permitTemporaryStudentBodyMergerTag ++ " & any of " ++ show [ProblemConfiguration.ProblemValidationSwitches.checkIfStudentBodiesExceedTeachersTag, ProblemConfiguration.ProblemValidationSwitches.checkIfStudentBodySizeExceedsLocationCapacityTag] ++ ", are incompatible" ), let nMutations, nTimeslotsPerWeek :: Size.NTimeslots nMutations = ExecutionConfiguration.EvolutionStrategies.getStudentViewTimetableForWeekMutationNTimeslots $ ExecutionConfiguration.ExecutionOptions.getEvolutionStrategies executionOptions nTimeslotsPerWeek = ProblemConfiguration.ProblemParameters.calculateNTimeslotsPerDay (ProblemConfiguration.ProblemParameters.getTimeslotIdBounds problemParameters) * Temporal.Day.nDaysPerWeek in ( nMutations > nTimeslotsPerWeek, "the specified number of time-slots " ++ show nMutations ++ ", to mutate during " ++ show ExecutionConfiguration.EvolutionStrategies.studentViewTimetableForWeekMutationTag ++ ", mustn't exceed the " ++ show nTimeslotsPerWeek ++ " available time-slots per week" ) ] instance ( Data.Default.Default campus, Data.Default.Default stream, Eq campus, Eq stream, Fractional criterionWeight, Fractional fecundityDecayRatio, Fractional minimumContrastRatio, Fractional populationDiversityRatio, HXT.XmlPickler campus, HXT.XmlPickler criterionWeight, HXT.XmlPickler fecundityDecayRatio, HXT.XmlPickler level, HXT.XmlPickler locationId, HXT.XmlPickler minimumContrastRatio, HXT.XmlPickler populationDiversityRatio, HXT.XmlPickler stream, HXT.XmlPickler synchronisationId, HXT.XmlPickler teacherId, HXT.XmlPickler teachingRatio, HXT.XmlPickler timeslotId, Ord fecundityDecayRatio, Ord level, Ord locationId, Ord minimumContrastRatio, Ord populationDiversityRatio, Ord synchronisationId, Ord teacherId, Ord timeslotId, Real criterionWeight, Real teachingRatio, Show campus, Show criterionWeight, Show fecundityDecayRatio, Show level, Show minimumContrastRatio, Show populationDiversityRatio, Show synchronisationId, Show timeslotId ) => HXT.XmlPickler (Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId) where xpickle = HXT.xpElem tag . HXT.xpWrap ( Text.XML.HXT.DOM.Util.uncurry4 MkOptions, -- Construct from a quadruple. \MkOptions { getConfigVersion = configVersion, getExecutionOptions = executionOptions, getOutputOptions = outputOptions, getProblemParameters = problemParameters } -> ( configVersion, executionOptions, outputOptions, problemParameters ) -- Deconstruct to a quadruple. ) $ HXT.xp4Tuple ( HXT.xpDefault Input.ConfigVersion.defaultConfigVersion . HXT.xpElem Input.ConfigVersion.tag . HXT.xpList1 {-the default is null-} . HXT.xpElem Input.ConfigVersion.elementTag $ HXT.xpAttr "integer" HXT.xpInt ) ( HXT.xpDefault Data.Default.def HXT.xpickle -- executionOptions. ) HXT.xpickle {-outputOptions-} HXT.xpickle {-problemParameters-} {- | * Mutator. * Sets /displayRuntimeInformation/ in all XHTML-formats. -} setDisplayRuntimeInformation :: OutputConfiguration.Style.DisplayRuntimeInformation -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setDisplayRuntimeInformation b options = options { getOutputOptions = outputOptions { OutputConfiguration.Options.getFileFormats = map ( \fileFormat -> fileFormat { OutputConfiguration.FileFormat.getFormat = case OutputConfiguration.FileFormat.getFormat fileFormat of OutputConfiguration.Format.XHTML style -> OutputConfiguration.Format.XHTML style { OutputConfiguration.Style.getDisplayRuntimeInformation = b } xml -> xml } ) $ OutputConfiguration.Options.getFileFormats outputOptions } } where outputOptions = getOutputOptions options -- | Mutator. setFecundityDecayRatio :: fecundityDecayRatio -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setFecundityDecayRatio fecundityDecayRatio options = options { getExecutionOptions = ExecutionConfiguration.ExecutionOptions.setFecundityDecayRatio fecundityDecayRatio $ getExecutionOptions options } -- | Mutator. setInputStudentViewTimetableFilePath :: System.FilePath.FilePath -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setInputStudentViewTimetableFilePath filePath options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getMaybeHint = Just $ Right filePath } } -- | Mutator. setMaybeNInitialScouts :: Maybe Size.NTimetables -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setMaybeNInitialScouts maybeNInitialScouts options = options { getExecutionOptions = ExecutionConfiguration.ExecutionOptions.setMaybeNInitialScouts maybeNInitialScouts $ getExecutionOptions options } -- | Mutator. setMaybeOutputConfigFilePath :: Maybe System.FilePath.FilePath -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setMaybeOutputConfigFilePath maybeOutputConfigFilePath options = options { getOutputOptions = (getOutputOptions options) { OutputConfiguration.Options.getMaybeOutputConfigFilePath = maybeOutputConfigFilePath } } -- | Mutator. setMaybeRandomSeed :: Maybe ExecutionConfiguration.ExecutionOptions.RandomSeed -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setMaybeRandomSeed maybeSeed options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getMaybeRandomSeed = maybeSeed } } -- | Mutator. setMinimumPopulationDiversityRatio :: populationDiversityRatio -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setMinimumPopulationDiversityRatio populationDiversityRatio options = options { getExecutionOptions = ExecutionConfiguration.ExecutionOptions.setMinimumPopulationDiversityRatio populationDiversityRatio $ getExecutionOptions options } -- | Mutator. setNDecimalDigits :: OutputConfiguration.Options.NDecimalDigits -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setNDecimalDigits nDecimalDigits options = options { getOutputOptions = (getOutputOptions options) { OutputConfiguration.Options.getNDecimalDigits = nDecimalDigits } } -- | Mutator. setOptimiseLessonCriteriaWeights :: ExecutionConfiguration.OptimiseLessonCriteriaWeights.OptimiseLessonCriteriaWeights criterionWeight -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setOptimiseLessonCriteriaWeights optimiseLessonCriteriaWeights options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getOptimiseLessonCriteriaWeights = optimiseLessonCriteriaWeights } } {- | * Mutator. * Other options are dependent on this one, & so are also toggled. If this is inappropriate, then they can always be toggled back, by a separate request. -} setPermitTemporaryStudentBodyMerger :: Num criterionWeight => ExecutionConfiguration.ExecutionOptions.PermitTemporaryStudentBodyMerger -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setPermitTemporaryStudentBodyMerger permitTemporaryStudentBodyMerger options = options { getProblemParameters = problemParameters { ProblemConfiguration.ProblemParameters.getProblemValidationSwitches = (ProblemConfiguration.ProblemParameters.getProblemValidationSwitches problemParameters) { ProblemConfiguration.ProblemValidationSwitches.getCheckIfStudentBodiesExceedTeachers = not permitTemporaryStudentBodyMerger, ProblemConfiguration.ProblemValidationSwitches.getCheckIfStudentBodySizeExceedsLocationCapacity = not permitTemporaryStudentBodyMerger } }, getExecutionOptions = ExecutionConfiguration.ExecutionOptions.setPermitTemporaryStudentBodyMerger permitTemporaryStudentBodyMerger $ getExecutionOptions options } where problemParameters = getProblemParameters options -- | Mutator. setReduceStudentBodyRegister :: ExecutionConfiguration.ExecutionOptions.ReduceStudentBodyRegister -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setReduceStudentBodyRegister b options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getReduceStudentBodyRegister = b } } -- | Mutator. setRemoveRedundantCourses :: ExecutionConfiguration.ExecutionOptions.RemoveRedundantCourses -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setRemoveRedundantCourses b options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getRemoveRedundantCourses = b } } -- | Mutator. setTimetableCriteriaWeights :: ExecutionConfiguration.TimetableCriteriaWeights.TimetableCriteriaWeights criterionWeight -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setTimetableCriteriaWeights timetableCriteriaWeights options = options { getExecutionOptions = (getExecutionOptions options) { ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights = timetableCriteriaWeights } } {- | * Mutator. * As a side-effect various criteria-weights may be turned-off, because the concepts to which they relate, don't exist until there're sufficient /time-slot/s per day. -} setTimeslotIdBounds :: (Enum timeslotId, Num criterionWeight) => Factory.Data.Interval.Interval timeslotId -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setTimeslotIdBounds timeslotIdBounds options | timeslotsPerDay < 2 = setTimetableCriteriaWeights ( timetableCriteriaWeights { ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest = minBound, ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseRatioOfConsecutiveEqualLessons = minBound, ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay = minBound } -- Turn off those criteria for which there's no concept until there're two timeslots per day. ) options' | timeslotsPerDay < 3 = setTimetableCriteriaWeights ( timetableCriteriaWeights { ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay = minBound } -- Turn off those criteria for which there's no concept until there're three timeslots per day. ) options' | otherwise = options' where timeslotsPerDay = ProblemConfiguration.ProblemParameters.calculateNTimeslotsPerDay timeslotIdBounds options' = options { getProblemParameters = (getProblemParameters options) { ProblemConfiguration.ProblemParameters.getTimeslotIdBounds = timeslotIdBounds } } -- Amend timeslotIdBounds as directly requested. timetableCriteriaWeights = ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights $ getExecutionOptions options' -- | Mutator. setVerbosity :: Distribution.Verbosity.Verbosity -> Mutator campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId setVerbosity verbosity options = options { getOutputOptions = (getOutputOptions options) { OutputConfiguration.Options.getVerbosity = verbosity } } {- | * Depending on each of the specified flags, uses the specified function to mutate each corresponding /criterion-weight/. * Logs the tags of all those criterion-weights changed. -} criteriaWeightsMutator :: ( Eq criterionWeight, Num criterionWeight ) => criteriaWeights -> [ ( ( criteriaWeights -> ExecutionConfiguration.CriterionWeight.CriterionWeight criterionWeight, Bool ), ( criteriaWeights -> criteriaWeights, String ) -- Pair. ) -- Pair. ] -- ^ [((Criterion-weights accessor, Whether to mutate this criterion-weight), (Criteria-weights mutator, Criterion-weight tag))]. -> Control.Monad.Writer.Writer [String] criteriaWeights -- ^ Zeroed tags & transformed value. criteriaWeightsMutator criteriaWeights = foldr ( ( \(mutator, message) -> ( Control.Monad.Writer.tell [message] >> -- Log the tag. ) . fmap mutator -- Apply the mutator. ) . snd ) ( return {-to Writer-monad-} criteriaWeights -- Initial value. ) . filter ( uncurry (&&) . Control.Arrow.first ( (/= minBound) . ($ criteriaWeights) ) . fst {-conditions-} ) {- | * Zeroes the weight of each /lesson-criterion/ & /timetable-criterion/, & the /fecundity/ of each /evolution-strategy/, which is either inappropriate for, or irrelevant to, the specified /problem-parameters/. * Logs the affected tags. -} zeroInappropriateExecutionOptions :: ( Enum timeslotId, Eq criterionWeight, Num criterionWeight, Ord campus, Ord level, Ord synchronisationId, Ord timeslotId, RealFrac teachingRatio ) => Bool -- ^ Whether to zero non-critical weights, or merely those that must be zeroed to avoid generating an error. -> ProblemConfiguration.ProblemParameters.ProblemParameters campus level locationId stream synchronisationId teacherId teachingRatio timeslotId -> ExecutionConfiguration.ExecutionOptions.ExecutionOptions criterionWeight fecundityDecayRatio populationDiversityRatio -- ^ Initial value. -> ( ExecutionConfiguration.ExecutionOptions.ExecutionOptions criterionWeight fecundityDecayRatio populationDiversityRatio, ( ( [String], [String] ), [String] ) ) -- ^ Transformed /evolution-strategies/, & the tags of zeroed ((/lesson-criteria weights/, /timetable-criteria weights/), /evolution-strategy fecundities/). zeroInappropriateExecutionOptions zeroNonCritical problemParameters executionOptions = ( executionOptions { ExecutionConfiguration.ExecutionOptions.getLessonCriteriaWeights = lessonCriteriaWeights, ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights = timetableCriteriaWeights, ExecutionConfiguration.ExecutionOptions.getEvolutionStrategies = evolutionStrategies }, ( ( zeroedLessonCriterionWeightTags, zeroedTimetableCriterionWeightTags ), -- Pair. zeroedEvolutionStrategyFecundityTags ) -- Pair. ) where nTimeslotsPerDay :: Size.NTimeslots nTimeslotsPerDay = ProblemConfiguration.ProblemParameters.calculateNTimeslotsPerDay $ ProblemConfiguration.ProblemParameters.getTimeslotIdBounds problemParameters permitTemporaryStudentBodyMerger :: Bool permitTemporaryStudentBodyMerger = ExecutionConfiguration.ExecutionOptions.getPermitTemporaryStudentBodyMerger executionOptions locationCatalogue = ProblemConfiguration.ProblemParameters.getLocationCatalogue problemParameters -- Access. studentBodyRegister = ProblemConfiguration.ProblemParameters.getStudentBodyRegister problemParameters -- Access. teacherRegister = ProblemConfiguration.ProblemParameters.getTeacherRegister problemParameters -- Access. nLocations :: Size.NLocations nLocations = Data.Map.size locationCatalogue nStudents :: Size.NStudents nStudents = Aggregate.StudentClass.getSize $ Aggregate.StudentBodyRegister.getStudentBodies studentBodyRegister nTeachers :: Size.NTeachers nTeachers = Data.Map.size teacherRegister (distinctCourses, noTeacherHasMoreThanOneLesson) = Aggregate.TeacherRegister.extractDistinctCourses &&& Data.Foldable.all ( (<= 1) . Data.Foldable.foldr ( (+) . Data.Course.getRequiredLessonsPerWeek ) 0 . Data.Teacher.getService ) $ teacherRegister hasAnyFacilities, isSingleCampus, hasAnyOptionalKnowledgeRequirements, hasAnyCourseMaximumClassSizes, hasAnySynchronisedCourses, requireAnyFacilities :: Bool (hasAnyFacilities, isSingleCampus) = Aggregate.LocationCatalogue.hasAnyFacilities &&& Aggregate.LocationCatalogue.isSingleCampus $ locationCatalogue hasAnyOptionalKnowledgeRequirements = Aggregate.StudentBodyRegister.hasAnyOptionalKnowledgeRequirements studentBodyRegister (hasAnyCourseMaximumClassSizes, hasAnySynchronisedCourses) = Aggregate.TeacherRegister.hasAnyCourseMaximumClassSizes &&& Aggregate.TeacherRegister.hasAnySynchronisedCourses $ teacherRegister requireAnyFacilities = Data.Foldable.any (not . Data.Set.null . Data.Course.getRequiredFacilityNames) distinctCourses smallestMinimumConsecutivePeriods :: Size.NTimeslots smallestMinimumConsecutivePeriods = Data.Foldable.minimum $ Data.Set.map Data.Course.getMinimumConsecutiveLessons distinctCourses (lessonCriteriaWeights, zeroedLessonCriterionWeightTags) = Control.Monad.Writer.runWriter $ criteriaWeightsMutator ( ExecutionConfiguration.ExecutionOptions.getLessonCriteriaWeights executionOptions ) [ ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfGreatestMinimumConsecutiveLessons, zeroNonCritical && not ( ProblemConfiguration.ProblemParameters.hasVariousMinimumConsecutiveLessons problemParameters ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfGreatestMinimumConsecutiveLessons, ExecutionConfiguration.LessonCriteriaWeights.weightOfGreatestMinimumConsecutiveLessonsTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfGreatestSynchronisedCourseSetSize, zeroNonCritical && not hasAnySynchronisedCourses -- Singleton synchronised courses are trapped by ProblemParameters. ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfGreatestSynchronisedCourseSetSize, ExecutionConfiguration.LessonCriteriaWeights.weightOfGreatestSynchronisedCourseSetSizeTag ) ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfIsCoreKnowledgeRequirement, zeroNonCritical && not ( Aggregate.StudentBodyRegister.hasAnyCoreKnowledgeRequirements studentBodyRegister {-enforced by DTD, but not by database-} && hasAnyOptionalKnowledgeRequirements ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfIsCoreKnowledgeRequirement, ExecutionConfiguration.LessonCriteriaWeights.weightOfIsCoreKnowledgeRequirementTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfIsSpecialistInTopic, zeroNonCritical && ( nTeachers == 1 || not (Aggregate.TeacherRegister.hasAnySpecialists teacherRegister) ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfIsSpecialistInTopic, ExecutionConfiguration.LessonCriteriaWeights.weightOfIsSpecialistInTopicTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMatchCourseClassSizeToLocationCapacity, zeroNonCritical && ( nLocations == 1 || not hasAnyCourseMaximumClassSizes ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMatchCourseClassSizeToLocationCapacity, ExecutionConfiguration.LessonCriteriaWeights.weightOfMatchCourseClassSizeToLocationCapacityTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMaximiseRelativeFacilityUtilisation, zeroNonCritical && not ( hasAnyFacilities && requireAnyFacilities ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMaximiseRelativeFacilityUtilisation, ExecutionConfiguration.LessonCriteriaWeights.weightOfMaximiseRelativeFacilityUtilisationTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMaximiseStudentClassSizeOverCourseClassSize, zeroNonCritical && or [ Data.Map.size studentBodyRegister == 1, not hasAnyCourseMaximumClassSizes, Data.Foldable.all ( Data.Maybe.maybe True ( >= nStudents -- Compare each maximum class-size with the total number of students. ) . Data.Course.getMaybeMaximumClassSize ) distinctCourses ] ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMaximiseStudentClassSizeOverCourseClassSize, ExecutionConfiguration.LessonCriteriaWeights.weightOfMaximiseStudentClassSizeOverCourseClassSizeTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMaximiseStudentClassSizeOverLocationCapacity, zeroNonCritical && nLocations == 1 -- CAVEAT: if there's only one student-body, it's still better to book them into a location of appropriate capacity, since it reduces the heating-bill. ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMaximiseStudentClassSizeOverLocationCapacity, ExecutionConfiguration.LessonCriteriaWeights.weightOfMaximiseStudentClassSizeOverLocationCapacityTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseBookingAtAnotherCoursesSpecifiedTime, zeroNonCritical && not (Aggregate.TeacherRegister.hasAnySpecificTimeRequests teacherRegister) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseBookingAtAnotherCoursesSpecifiedTime, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseBookingAtAnotherCoursesSpecifiedTimeTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseBookingOfLocationByOtherTeachers, zeroNonCritical && ( nLocations == 1 || nTeachers == 1 ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseBookingOfLocationByOtherTeachers, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseBookingOfLocationByOtherTeachersTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseDeviationFromTimeslotRequest, zeroNonCritical && not (Aggregate.TeacherRegister.hasAnyTimeslotRequests teacherRegister) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseDeviationFromTimeslotRequest, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseDeviationFromTimeslotRequestTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseInterCampusMigrationsOfStudents, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || isSingleCampus ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseInterCampusMigrationsOfStudents, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseInterCampusMigrationsOfStudentsTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseInterCampusMigrationsOfTeachers, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || isSingleCampus ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseInterCampusMigrationsOfTeachers, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseInterCampusMigrationsOfTeachersTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseTeachersLocusOperandi, zeroNonCritical && ( nLocations == 1 || noTeacherHasMoreThanOneLesson ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseTeachersLocusOperandi, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseTeachersLocusOperandiTag ) -- Pair. ), ( ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseWasteOfScarceFacilities, zeroNonCritical && not ( hasAnyFacilities && requireAnyFacilities ) ), ( ExecutionConfiguration.LessonCriteriaWeights.zeroWeightOfMinimiseWasteOfScarceFacilities, ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseWasteOfScarceFacilitiesTag ) -- Pair. ) -- Pair. ] (timetableCriteriaWeights, zeroedTimetableCriterionWeightTags) = Control.Monad.Writer.runWriter $ criteriaWeightsMutator ( ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights executionOptions ) [ ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMaximiseComplianceWithFreePeriodPreferences, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || not ( ProblemConfiguration.ProblemParameters.hasAnyFreePeriodPreference problemParameters ) ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMaximiseComplianceWithFreePeriodPreferences, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMaximiseComplianceWithFreePeriodPreferencesTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMaximiseSynchronisationOfSynchronisedCourses, zeroNonCritical && not hasAnySynchronisedCourses -- Singleton synchronised courses are trapped by ProblemParameters. ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMaximiseSynchronisationOfSynchronisedCourses, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMaximiseSynchronisationOfSynchronisedCoursesTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || not ( Aggregate.TeacherRegister.hasAnyIdealTimeslotRequests teacherRegister ) ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay, zeroNonCritical && Data.Foldable.all ( (== 1) . Data.Student.getTeachingRatio ) studentBodyRegister ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseDispersionOfStudentFreePeriodsPerDay, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseDispersionOfStudentFreePeriodsPerDayTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanInterCampusMigrationsOfStudents, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || isSingleCampus ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseMeanInterCampusMigrationsOfStudents, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers, zeroNonCritical && ( nTimeslotsPerDay <= smallestMinimumConsecutivePeriods || isSingleCampus ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseMeanInterCampusMigrationsOfTeachers, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanLocationChangesOfTeachers, zeroNonCritical && ( nLocations == 1 || noTeacherHasMoreThanOneLesson ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseMeanLocationChangesOfTeachers, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanLocationChangesOfTeachersTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanLocusOperandiOfTeachers, zeroNonCritical && ( nLocations == 1 || noTeacherHasMoreThanOneLesson ) ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseMeanLocusOperandiOfTeachers, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanLocusOperandiOfTeachersTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge, zeroNonCritical && not hasAnyOptionalKnowledgeRequirements ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseRatioOfConsecutiveEqualLessons, nTimeslotsPerDay <= smallestMinimumConsecutivePeriods -- CAVEAT: required to prevent an attempt to divide by zero. ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseRatioOfConsecutiveEqualLessons, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseRatioOfConsecutiveEqualLessonsTag ) -- Pair. ), ( ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay, nTimeslotsPerDay <= 2 * smallestMinimumConsecutivePeriods -- CAVEAT: required to prevent an attempt to divide by zero. ), ( ExecutionConfiguration.TimetableCriteriaWeights.zeroWeightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDay, ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseRatioOfSeparatedEqualLessonsWithinAnyDayTag ) -- Pair. ) -- Pair. ] (evolutionStrategies, zeroedEvolutionStrategyFecundityTags) = Control.Monad.Writer.runWriter . foldr ( ( \(mutator, message) -> ( Control.Monad.Writer.tell [message] >> -- Log the tag. ) . fmap mutator -- Apply the mutator. ) . snd ) ( return {-to Writer-monad-} $ ExecutionConfiguration.ExecutionOptions.getEvolutionStrategies executionOptions -- Initial value. ) $ filter ( uncurry (&&) . Control.Arrow.first ( (/= ExecutionConfiguration.TimetableBreederFecundity.zero) . ($ ExecutionConfiguration.ExecutionOptions.getEvolutionStrategies executionOptions) ) . fst {-conditions-} ) [ ( ( ExecutionConfiguration.EvolutionStrategies.getSynchronisedCourseMutationFecundity, zeroNonCritical && not hasAnySynchronisedCourses -- Singleton synchronised courses are trapped by ProblemParameters. ), ( ExecutionConfiguration.EvolutionStrategies.zeroSynchronisedCourseMutationFecundity, ExecutionConfiguration.EvolutionStrategies.synchronisedCourseMutationTag ) -- Pair. ), ( ( ExecutionConfiguration.EvolutionStrategies.getSynchronisedCourseByDayMutationFecundity, zeroNonCritical && not hasAnySynchronisedCourses -- Singleton synchronised courses are trapped by ProblemParameters. ), ( ExecutionConfiguration.EvolutionStrategies.zeroSynchronisedCourseByDayMutationFecundity, ExecutionConfiguration.EvolutionStrategies.synchronisedCourseByDayMutationTag ) -- Pair. ), ( ( ExecutionConfiguration.EvolutionStrategies.getExcessRunlengthMutationFecundity, zeroNonCritical && nTimeslotsPerDay <= smallestMinimumConsecutivePeriods ), ( ExecutionConfiguration.EvolutionStrategies.zeroExcessRunlengthMutationFecundity, ExecutionConfiguration.EvolutionStrategies.excessRunlengthMutationTag ) -- Pair. ), ( ( ExecutionConfiguration.EvolutionStrategies.getSingletonStudentClassMutationFecundity, zeroNonCritical && not permitTemporaryStudentBodyMerger ), ( ExecutionConfiguration.EvolutionStrategies.zeroSingletonStudentClassMutationFecundity, ExecutionConfiguration.EvolutionStrategies.singletonStudentClassMutationTag ) -- Pair. ), ( ( ExecutionConfiguration.EvolutionStrategies.getSplitSessionMutationFecundity, zeroNonCritical && nTimeslotsPerDay <= 2 * smallestMinimumConsecutivePeriods ), ( ExecutionConfiguration.EvolutionStrategies.zeroSplitSessionMutationFecundity, ExecutionConfiguration.EvolutionStrategies.splitSessionMutationTag ) -- Pair. ), ( ( ExecutionConfiguration.EvolutionStrategies.getStudentBodyCombinationMutationFecundity, zeroNonCritical && not permitTemporaryStudentBodyMerger ), ( ExecutionConfiguration.EvolutionStrategies.zeroStudentBodyCombinationMutationFecundity, ExecutionConfiguration.EvolutionStrategies.studentBodyCombinationMutationTag ) -- Pair. ) -- Pair. ] instance ( Enum timeslotId, Eq criterionWeight, Num criterionWeight, Ord campus, Ord level, Ord synchronisationId, Ord teacherId, Ord timeslotId, RealFrac teachingRatio, Show level, Show synchronisationId, Show teacherId, Show timeslotId ) => Configuration.Configuration (Options campus criterionWeight fecundityDecayRatio level locationId minimumContrastRatio populationDiversityRatio stream synchronisationId teacherId teachingRatio timeslotId) where issueWarnings inputOptions = Configuration.issueWarnings executionOptions ++ Configuration.issueWarnings problemParameters ++ [ message ++ ", but the weight assigned to the lesson-criterion " ++ show criterionTag ++ " is zero" | (criterionWeightAccessor, condition, message, criterionTag) <- [ ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfGreatestSynchronisedCourseSetSize, hasAnySynchronisedCourses, "synchronised courses have been defined", ExecutionConfiguration.LessonCriteriaWeights.weightOfGreatestSynchronisedCourseSetSizeTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfIsSpecialistInTopic, Aggregate.TeacherRegister.hasAnySpecialists teacherRegister, "topic-specialists have been defined", ExecutionConfiguration.LessonCriteriaWeights.weightOfIsSpecialistInTopicTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMaximiseRelativeFacilityUtilisation, hasAnyFacilities, "facilities have been defined in various locations", ExecutionConfiguration.LessonCriteriaWeights.weightOfMaximiseRelativeFacilityUtilisationTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseWasteOfScarceFacilities, hasAnyFacilities, "facilities have been defined in various locations", ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseWasteOfScarceFacilitiesTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseBookingAtAnotherCoursesSpecifiedTime, hasAnyTimeslotRequests, "time-slots have been requested by teachers", ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseBookingAtAnotherCoursesSpecifiedTimeTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseDeviationFromTimeslotRequest, hasAnyTimeslotRequests, "time-slots have been requested by teachers", ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseDeviationFromTimeslotRequestTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseInterCampusMigrationsOfStudents, not isSingleCampus, "locations have been partitioned into different campuses", ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseInterCampusMigrationsOfStudentsTag ), ( ExecutionConfiguration.LessonCriteriaWeights.getWeightOfMinimiseInterCampusMigrationsOfTeachers, not isSingleCampus, "locations have been partitioned into different campuses", ExecutionConfiguration.LessonCriteriaWeights.weightOfMinimiseInterCampusMigrationsOfTeachersTag ) -- Quadruple. ], criterionWeightAccessor lessonCriteriaWeights == minBound, condition ] ++ [ message ++ ", but the weight assigned to the timetable-criterion " ++ show criterionTag ++ " is zero" | (criterionWeightAccessor, condition, message, criterionTag) <- [ ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMaximiseSynchronisationOfSynchronisedCourses, hasAnySynchronisedCourses, "synchronised courses have been defined", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMaximiseSynchronisationOfSynchronisedCoursesTag ), ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequest, Aggregate.TeacherRegister.hasAnyIdealTimeslotRequests teacherRegister, "ideal time-slots have been specified for courses", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseAverageAbsoluteDeviationFromIdealTimeslotRequestTag ), ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMaximiseComplianceWithFreePeriodPreferences, ProblemConfiguration.ProblemParameters.hasAnyFreePeriodPreference problemParameters, "free-period preferences have been expressed", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMaximiseComplianceWithFreePeriodPreferencesTag ), ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanInterCampusMigrationsOfStudents, not isSingleCampus, "locations have been partitioned into different campuses", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanInterCampusMigrationsOfStudentsTag ), ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanInterCampusMigrationsOfTeachers, not isSingleCampus, "locations have been partitioned into different campuses", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanInterCampusMigrationsOfTeachersTag ), ( ExecutionConfiguration.TimetableCriteriaWeights.getWeightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledge, Aggregate.StudentBodyRegister.hasAnyOptionalKnowledgeRequirements studentBodyRegister, "student-bodies have specified optional knowledge-requirements", ExecutionConfiguration.TimetableCriteriaWeights.weightOfMinimiseMeanRatioOfIncompletelyBookedOptionalKnowledgeTag ) -- Quadruple. ], criterionWeightAccessor timetableCriteriaWeights == minBound, condition ] where (executionOptions, problemParameters) = getExecutionOptions &&& getProblemParameters $ inputOptions (lessonCriteriaWeights, timetableCriteriaWeights) = ExecutionConfiguration.ExecutionOptions.getLessonCriteriaWeights &&& ExecutionConfiguration.ExecutionOptions.getTimetableCriteriaWeights $ executionOptions (studentBodyRegister, teacherRegister) = ProblemConfiguration.ProblemParameters.getStudentBodyRegister &&& ProblemConfiguration.ProblemParameters.getTeacherRegister $ problemParameters locationCatalogue = ProblemConfiguration.ProblemParameters.getLocationCatalogue problemParameters (hasAnyFacilities, isSingleCampus) = Aggregate.LocationCatalogue.hasAnyFacilities &&& Aggregate.LocationCatalogue.isSingleCampus $ locationCatalogue (hasAnySynchronisedCourses, hasAnyTimeslotRequests) = Aggregate.TeacherRegister.hasAnySynchronisedCourses &&& Aggregate.TeacherRegister.hasAnyTimeslotRequests $ teacherRegister