예제 #1
0
        // TODO: Think over reusing ParetoFinder from opt.Core
        /// <summary>
        /// Метод для проверки строгого доминирования первой особи
        /// над второй
        /// </summary>
        /// <param name="criteria">Список критериев оптимальности</param>
        /// <param name="firstIndividual">Первая особь</param>
        /// <param name="secondIndividual">Вторая особь</param>
        /// <returns>True, если первая особь строго доминирует над второй, иначе false</returns>
        public static bool CheckStrictDomination(
            Dictionary <TId, Criterion> criteria,
            NsgaIndividual firstIndividual,
            NsgaIndividual secondIndividual)
        {
            // По каждому критерию - надо проверить все
            foreach (Criterion crit in criteria.Values)
            {
                // Для удобства скопируем значения
                double firstIndividualValue  = firstIndividual.Criteria[crit.Id];
                double secondIndividualValue = secondIndividual.Criteria[crit.Id];

                // Если при переходе от первой особи ко второй
                // мы смогли улучшить хоть один критерий, то первая
                // особь НЕ ДОМИНИРУЕТ над второй
                switch (crit.Type)
                {
                case CriterionType.Minimizing:
                    if (firstIndividualValue > secondIndividualValue)
                    {
                        return(false);
                    }
                    break;

                case CriterionType.Maximizing:
                    if (firstIndividualValue < secondIndividualValue)
                    {
                        return(false);
                    }
                    break;
                }
            }

            // Если при переходе от первой особи ко второй
            // мы не улучшили ни одного критерия, то первая
            // особь ДОМИНИРУЕТ над второй
            return(true);
        }
예제 #2
0
        /// <summary>
        /// Метод для выполнения одноточечной инверсии особи
        /// </summary>
        /// <param name="population">Популяция, в которой находится родительская
        /// особь и в которую будет помещен потомок</param>
        /// <param name="unitNumber">Номер особи, над которой нужно выполнить инверсию
        /// (уникальный)</param>
        /// <param name="newGenerationNumber">Номер поколения, к которому относится потомок</param>
        private static void Inversion(
            ref Population <NsgaIndividual> population,
            int unitNumber,
            int newGenerationNumber)
        {
            // Признаки, у новой особи они будут такие же,
            // за исключением значений признаков - их поправим позже
            Dictionary <TId, IndividualAttribute> attributes =
                population[unitNumber].Attributes;

            // Генетический код родителя
            string parentChromo = population[unitNumber].GetChromo();

            Random rnd = new Random(DateTime.Now.Millisecond + DateTime.Now.Second);
            // Точка инверсии
            // Такая формула обусловлена следующим:
            //  - не должно получатся число, равное длине строки, потому что
            //    нумерация символов zero-based и будет ошибка;
            int crossoverPointPosition = rnd.Next(parentChromo.Length - 1);

            // Разделим хромосому на части, первая из которых останется неизменной,
            // а вторая инвертируется
            string parentFirstPart  = parentChromo.Substring(0, crossoverPointPosition);
            string parentSecondPart = parentChromo.Substring(crossoverPointPosition);

            // Инвертируем вторую часть хромосомы
            string newSecondPart = string.Empty;

            // Проверим каждый ген в этой части хромосомы
            char[] secondPart = parentSecondPart.ToCharArray();
            foreach (char gene in secondPart)
            {
                // Выполним добавление гена к новой хромосоме,
                // попутно инвертировав его
                switch (gene)
                {
                case '1':
                    newSecondPart += "0";
                    break;

                case '0':
                    newSecondPart += "1";
                    break;
                }
            }

            // Составим хромосому потомка
            string сhildChromo = parentFirstPart + newSecondPart;

            // Получим и добавим в популяцию потомка
            var child = new NsgaIndividual(
                population.GetFreeUnitNumber(),
                newGenerationNumber,
                attributes);

            child.UpdateAttributes(сhildChromo);
            population.Add(child.Number, child);

            // Удалим из популяции исходную особь
            population.Remove(unitNumber);
        }
예제 #3
0
        /// <summary>
        /// Метод для выполнения одноточечного скрещивания двух
        /// особей
        /// </summary>
        /// <param name="population">Популяция, в которой находятся две
        /// родительские особи и куда будут помещены два потомка</param>
        /// <param name="firstUnitNumber">Номер первого родителя в популяции (уникальный)</param>
        /// <param name="secondUnitNumber">Номер второго родителя в популяции (уникальный)</param>
        /// <param name="newGenerationNumber">Номер поколения, к которому относятся потомки</param>
        private static void CrossTwoUnits(
            ref Population <NsgaIndividual> population,
            int firstUnitNumber,
            int secondUnitNumber,
            int newGenerationNumber)
        {
            // Признаки, у новых двух особей они будут такие же,
            // за исключением значений признаков - их поправим позже
            var attributes = population[firstUnitNumber].Attributes;

            // Генетические коды родителей
            string firstChromo  = population[firstUnitNumber].GetChromo();
            string secondChromo = population[secondUnitNumber].GetChromo();

            // Небольшая проверка: длины кодов должны быть одинаковые
            int chromoLength = firstChromo.Length;

            if (chromoLength != secondChromo.Length)
            {
                throw new Exception("Chromosome lengths of two parents must be the same");
            }

            var rnd = new Random(DateTime.Now.Millisecond + DateTime.Now.Second);
            // Точка одноточечного скрещивания
            // Такая формула обусловлена следующим:
            //  - не должно получатся число, равное длине строки, потому что
            //    нумерация символов zero-based и будет ошибка;
            //  - не должен получатся ноль, потому что тогда особи просто
            //    обменяются кодами и ничего не выйдет
            int crossoverPointPosition = 1 + rnd.Next(chromoLength - 2);

            // Разделим хромосомы на части, из которых будут составлены
            // хромосомы потомков
            string firstParentFirstPart   = firstChromo.Substring(0, crossoverPointPosition);
            string firstParentSecondPart  = firstChromo.Substring(crossoverPointPosition);
            string secondParentFirstPart  = secondChromo.Substring(0, crossoverPointPosition);
            string secondParentSecondPart = secondChromo.Substring(crossoverPointPosition);

            // Составим хромосомы потомков
            string firstChildChromo  = firstParentFirstPart + secondParentSecondPart;
            string secondChildChromo = secondParentFirstPart + firstParentSecondPart;

            // Получим и добавим в популяцию первого потомка
            var firstChild = new NsgaIndividual(
                population.GetFreeUnitNumber(),
                newGenerationNumber,
                attributes);

            firstChild.UpdateAttributes(firstChildChromo);
            population.Add(firstChild.Number, firstChild);

            // Получим и добавим в популяцию второго потомка
            var secondChild = new NsgaIndividual(
                population.GetFreeUnitNumber(),
                newGenerationNumber,
                attributes);

            secondChild.UpdateAttributes(secondChildChromo);
            population.Add(secondChild.Number, secondChild);

            // Удалим из популяции родителей
            population.Remove(firstUnitNumber);
            population.Remove(secondUnitNumber);
        }
예제 #4
0
        public static Population <NsgaIndividual> TournamentSelection(
            Population <NsgaIndividual> initPop,
            int selectionLimit)
        {
            // Сначала проверим: если надо отобрать больше, чем есть,
            // то выбросим ошибку
            if (selectionLimit > initPop.Count)
            {
                throw new ArgumentException("Number of units to be selected is greater than number of units in population");
            }

            var selectedIndividuals = new Population <NsgaIndividual>(initPop.GetMaxUnitNumber());

            // Если надо отобрать столько же, сколько есть, просто
            // вернем входную популяцию
            if (selectionLimit == initPop.Count)
            {
                foreach (NsgaIndividual individual in initPop)
                {
                    selectedIndividuals.Add(individual.Number, (NsgaIndividual)individual.Clone());
                }
                return(selectedIndividuals);
            }

            // Рандомайзер для выбора случайных особей
            Random rnd = new Random(DateTime.Now.Millisecond + DateTime.Now.Second);
            // Флагом того, что пора прекртатить итерации отбора,
            // будет служить совпадение количества отобранных
            // особей (в списке, объявленном выше) и переданного
            // в качестве аргумента требуемого количества
            int maxUnitNumber = initPop.GetMaxUnitNumber();

            while (selectedIndividuals.Count != selectionLimit)
            {
                // В этом цикле будет проходить разбиение на пары
                // и выбор лучшей особи из пары

                // Список пары отобранных для турнира особей
                var selectedPair = new List <int>();

                // Проверим: если среди неотобранных особей осталось всего две,
                // то составим из них пару и не будем морочить себе голову
                // случайностями
                if ((initPop.Count - selectedIndividuals.Count) == 2)
                {
                    foreach (NsgaIndividual individual in initPop)
                    {
                        if (IsValidPairUnit(individual.Number, selectedPair, selectedIndividuals, initPop))
                        {
                            selectedPair.Add(individual.Number);
                        }
                    }
                }

                // Флагом остановки выбора случайной особи в пару
                // будет служить наличие двух отобранных особей
                while (selectedPair.Count != 2)
                {
                    // Будем выбирать случайное число в диапазоне
                    // от 0 до значения счетчика особей в популяции
                    // до тех пор, пока не наткнемся на особь, которую
                    // можно отобрать в турнирную пару
                    int rndNumber = -1;
                    while (!IsValidPairUnit(rndNumber, selectedPair, selectedIndividuals, initPop))
                    {
                        // "+ 1" из-за особенностей реализации метода
                        // Random.Next(Int32, Int32). Если не будет "+ 1",
                        // то maxUnitNumber не выпадет никогда
                        rndNumber = rnd.Next(0, maxUnitNumber + 1);
                    }
                    // Когда найден подходящий номер, добавим его в пару
                    selectedPair.Add(rndNumber);
                }

                // Выберем из пары лучшую особь
                // (согласно алгоритма NSGA-II)
                NsgaIndividual fittestIndividual = SelectFittestOfTwo(selectedPair, initPop);
                selectedIndividuals.Add(fittestIndividual.Number, (NsgaIndividual)fittestIndividual.Clone());
            }

            // Вернем результат
            return(selectedIndividuals);
        }
예제 #5
0
        /// <summary>
        /// Метод для преобразования модели в популяцию
        /// </summary>
        /// <param name="model">Исходная модель для преобразования</param>
        /// <param name="generationNumber">Номер поколения, который будет присвоен
        /// всем особям в создаваемой популяции</param>
        /// <returns></returns>
        private static Population <NsgaIndividual> ModelToPopulation(Model model, int generationNumber)
        {
            var population = new Population <NsgaIndividual>();

            // 1. Сформируем словарь с параметрами особей. Все особи
            // данной популяции должны характеризоваться одинаковым
            // набором параметров, поэтому сформируем его заранее, а
            // потом будем передавать в качестве параметра конструктора
            // всем особям. Значения параметров будем затем назначать
            // вручную
            var attributes = new Dictionary <TId, IndividualAttribute>();

            foreach (Parameter param in model.Parameters.Values)
            {
                var attribute =
                    new IndividualAttribute(
                        param.Id,
                        param.MinValue,
                        param.MaxValue,
                        Program.ApplicationSettings.ValuesDecimalPlaces);
                attributes.Add(param.Id, attribute);
            }

            // Подготовим нормализованные критерии
            var normalizedExpCriteria = new Dictionary <TId, Dictionary <TId, double> >();

            foreach (Experiment exp in model.Experiments.Values)
            {
                if (!exp.IsActive)
                {
                    continue;
                }

                var critValues = new Dictionary <TId, double>();

                normalizedExpCriteria.Add(exp.Id, critValues);
            }

            // Смена ингридиента:
            // Для каждого максимизируемого критерия выполним Y_new = 1 / Y_old
            // так он станет минимизируемым
            // Затем - нормализация
            var experiments = PrepareCriteria(model);

            // 2. Добавим в популяцию особи - столько же, сколько
            // АКТИВНЫХ экспериментов в модели
            foreach (Experiment exp in model.Experiments.Values)
            {
                if (!exp.IsActive)
                {
                    continue;
                }

                var unit = new NsgaIndividual(
                    exp.Number,
                    generationNumber,
                    attributes);
                // Запишем значения оптимизируемых параметров эксперимента
                // в словарь признаков особи
                foreach (Parameter param in model.Parameters.Values)
                {
                    unit.Attributes[param.Id].Value =
                        exp.ParameterValues[param.Id];
                    // Рассчитаем генетический код этой особи
                    unit.Attributes[param.Id].ResolveCodeFromValue();
                }
                // Запишем значения критериев оптимальности в словарь
                // критериев особи
                foreach (Criterion crit in model.Criteria.Values)
                {
                    unit.Criteria.Add(crit.Id, experiments[exp.Id].CriterionValues[crit.Id]);
                }
                // Добавим особь в популяцию
                population.Add(exp.Number, unit);
            }

            return(population);
        }