Genetic

#region Namespaces
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
#endregion

namespace ScriptCode
{
    /// <summary>
    /// Optimization algorithm scripts are used to select the script parameter values to be used in the next optimization runs. 
    /// </summary>
    public partial class MyOptimizationAlgorithm : OptimizationAlgorithmScriptBase // NEVER CHANGE THE CLASS NAME
    {
        #region Variables
        // The number of generations.
		private int _generations;
		// The number of chromosomes selected in each generation.
		private int _chromosomes;
		// The number of parents used to create each generation other than the first.
		private int _parents;
		// The number of mutations allowed from the parent's child chromosomes.
		private int _mutations;
		// Use for holding the vector indexes of the current generation.
		private List<int> _currentGeneration;
		// The random number generator.
		private Random _random;
        #endregion

        #region OnInitialize
        /// <summary>
        /// This function is used for accepting the script parameters and for initializing the script prior to all other function calls.
        /// Once the script is assigned to a desktop, its parameter values can be specified by the user. 
        /// </summary>
        /// --------------------------------------------------------------------------------------------------
        /// PLEASE USE THE SCRIPT WIZARD (CTRL+W) TO ADD, EDIT AND REMOVE THE SCRIPT PARAMETERS
        /// --------------------------------------------------------------------------------------------------
        /// YOU MUST SET A PARAM TAG FOR EACH PARAMETER ACCEPTED BY THIS FUNCTION.
        /// ALL PARAM TAGS SHOULD BE SET IN THE 'OnInitialize' REGION, RIGHT ABOVE THE 'OnInitialize' FUNCTION.
        /// THE ORDER OF THE TAGS MUST MATCH THE ORDER OF THE ACTUAL PARAMETERS.

        /// REQUIRED ATTRIBUTES:
        /// (1) name: The exact parameter name.
        /// (2) type: The type of data to collect from the user: 
        /// Set to "Integer" when the data type is 'int'
        /// Set to "IntegerArray" when the data type is 'int[]'
        /// Set to "DateTime" when the data type is 'long'  
        /// Set to "DateTimeArray" when the data type is 'long[]'  
        /// Set to "Boolean" when the data type is 'bool'
        /// Set to "BooleanArray" when the data type is 'bool[]'
        /// Set to "Double" when the data type is 'double'
        /// Set to "DoubleArray" when the data type is 'double[]'
        /// Set to "String" when the data type is 'string'
        /// Set to "StringArray" when the data type is 'string[]'

        /// OPTIONAL ATTRIBUTES:
        /// (3) default: The default parameter value is only valid when the type is Integer, Boolean, Double, String or an API Type. 
        /// (4) min: The minimum parameter value is only valid when the type is Integer or Double.
        /// (5) max: The maximum parameter value is only valid when the type is Integer or Double.

        /// EXAMPLE: <param name="" type="" default="" min="" max="">Enter the parameter description here.</param> 
        /// --------------------------------------------------------------------------------------------------
		/// <param name="generations" type="Integer" default="10">The number of generations.</param>
		/// <param name="chromosomes" type="Integer" default="20">The number of chromosomes selected in each generation.</param>
		/// <param name="parents" type="Integer" default="3">The number of parents used to create each generation other than the first.</param>
		/// <param name="mutations" type="Integer" default="1">The number of mutations allowed from the parent's child chromosomes.</param>
        public void OnInitialize(
			int generations,
			int chromosomes,
			int parents,
			int mutations)
        {
            // Set the number of generations.
			_generations = generations;
			// Set the number of chromosomes.
			_chromosomes = chromosomes;
			// Set the number of parents.
			_parents = parents;
			// Set the number of mutations.
			_mutations = mutations;
			// Create for holding the vector indexes of the current generation.
			_currentGeneration = new List<int>();
			// Create the random number generator.
			_random = new Random();
        }
        #endregion

        #region OnSelectNextOptimizationVectors
        /// <summary>
        /// This function is called in order to select the next optimization vectors to be processed.
        /// The function may be called multiple times in a row before the OnUpdateOptimizationVector function is called. 
        /// </summary>
        /// <returns type="IntegerArray">The indexes of the next optimization vectors to process.</returns>
        public override int[] OnSelectNextOptimizationVectors()
        {
            // Create for holding whether an optimization vector has been added to the new generation.
			Dictionary<int, bool> exists = new Dictionary<int, bool>();
			// Create for holding the vector for the new chromosomes.
			List<int> newChromosomes = new List<int>();
			// Check whether this is the first generation.
			if (_currentGeneration.Count == 0) {
				int attempts = 10000;
				// Iterate until there are enough chromosomes in the new generation or there are no more attempts to select a unique random vector.
				while (newChromosomes.Count < _chromosomes && attempts > 0) {
					// Get a random vector index.
					int index = _random.Next(0, OptimizationVectorCount());
					// Check whether the vector hasn't been processed.
					if (!exists.ContainsKey(index) && !OptimizationVectorIsProcessed(index)) {
						// Add the vector index to the new chromosomes.
						newChromosomes.Add(index);
						// Add the index to the list of existing indexes.
						exists.Add(index, true);
					}
					// Reduce the number of attempts.
					attempts--;
				}
			}
			else {
				// The number of unique parent attempts.
				int uniqueParentAttempts = 0;
				// Create for holding the next parent chromosomes.
				List<int> nextParentChromosomes = new List<int>();
				// Iterate until there are enough parents, while selecting the best vectors.
				while (nextParentChromosomes.Count < _parents && uniqueParentAttempts < _parents) {
					double bestScore = double.MinValue;
					int bestVector = - 1;
					// Iterate over the all of the vectors while searching for the best parents.
					for (int i = 0; i < OptimizationVectorCount(); i++) {
						// Check whether the current vector score is an improvement and the vector hasn't yet been selected as a parent.
						if (OptimizationVectorScore(i) > bestScore && !nextParentChromosomes.Contains(i)) {
							// Update the best score.
							bestScore = OptimizationVectorScore(i);
							// Update the best vector.
							bestVector = i;
						}
					}
					// Add the best vector to the list of parents.
					nextParentChromosomes.Add(bestVector);
					// Increase the number of attempts.
					uniqueParentAttempts++;
				}

				// Select the index of the first parent chromosome.
				int firstIndex = _random.Next(0, nextParentChromosomes.Count);
				// Select the index of the second parent chromosome.
				int secondIndex = _random.Next(0, nextParentChromosomes.Count);

				// The number of unique parent attempts.
				uniqueParentAttempts = 100;
				// Iterate until a different second parent chromosome is selected.
				while (firstIndex == secondIndex && uniqueParentAttempts > 0) {
					// Select the index of the second parent chromosome.
					secondIndex = _random.Next(0, nextParentChromosomes.Count);
					// Reduce the number of attempts.
					uniqueParentAttempts--;
				}
				// Get the first parent chromosome.
				double[] firstParent = OptimizationVectorValues(nextParentChromosomes[firstIndex]);
				// Get the second parent chromosome.
				double[] secondParent = OptimizationVectorValues(nextParentChromosomes[secondIndex]);
				// The child chromosome.
				double[] child = new double[firstParent.Length];
				// Create the child vector from the selected parents.
				for (int i = 0; i < child.Length; i++) {
					// Randomly select items from the parents.
					if (_random.NextDouble() >= 0.5)
						child[i] = firstParent[i];
					else child[i] = secondParent[i];
				}

				// The number of attempts to find a unique vector.
				int attempts = 10000;
				// Set the allowed mutations.
				int allowedMutations = _mutations;
				// Iterate until there are enough chromosomes in the new generation or there are no more attempts to create a unique vector. 
				while (newChromosomes.Count < _chromosomes && attempts > 0) {
					// Create for holding the optimization vectors which match the child, given the number of allowed mutations.
					List<int> childMatches = new List<int>();
					// Iterate over the optimization vectors while searching for those that match the child, given the number of allowed mutations.
					for (int i = 0; i < OptimizationVectorCount(); i++) {
						// Check whether the vector hasn't yet been processed.
						if (!OptimizationVectorIsProcessed(i)) {
							// Get the values of the current vector.
							double[] currentValues = OptimizationVectorValues(i);
							// Use for counting the number of differences between the current optimization vector and the child.
							int differences = 0;
							// Iterate over the current vector values.
							for (int j = 0; j < currentValues.Length; j++) {
								// Check whether the current optimization vector has an items which is different than the child.
								if (currentValues[j] != child[j])
									differences++;
							}
							// Check whether the number of differences is less that the mutations allowed.
							if (differences <= allowedMutations)
							// Add the optimization vector as a possible child.
								childMatches.Add(i);
						}
					}
					// Check whether child matches were found.
					if (childMatches.Count > 0) {
						// Get a random child.
						int index = _random.Next(0, childMatches.Count);
						// Iterate until a unique child is selected.
						while (exists.ContainsKey(childMatches[index]) && attempts > 0) {
							// Get a random child.
							index = _random.Next(0, childMatches.Count);
							// Reduce the number of attempts.
							attempts--;
						}
						// Check whether the vector hasn't been selected for this generation..
						if (!exists.ContainsKey(childMatches[index])) {
							// Add the optimization vector.
							exists.Add(childMatches[index], true);
							// Add the child to the new chromosomes list.
							newChromosomes.Add(childMatches[index]);
						}
					}
					// Increase the allowed mutations, since a match wasn't found.
					else allowedMutations++;
					// Reduce the number of attempts.
					attempts--;
				}
			}
			// Clear the chromosomes in the current generation.
			_currentGeneration.Clear();
			// Return the new chromosomes.
			return newChromosomes.ToArray();
        }
        #endregion

        #region OnGetMaxVectors
        /// <summary>
        /// This function is called to get the number of optimization vectors that the algorithm will 
        /// select and run if the optimization process runs to completion.
        /// </summary>
        /// <returns type="Integer">The maximum number of optimization vectors to be run.</returns>
        public override int OnGetMaxVectors()
        {
            // Calculate the maximum number of vectors to calculate.
			int maxVectors = _generations * _chromosomes;
			// Check whether the number of vectors to run is less than the number of existing vectors.
			if (maxVectors <= OptimizationVectorCount())
				return maxVectors;
			else return OptimizationVectorCount();
        }
        #endregion

        #region OnUpdateOptimizationVector
        /// <summary>
        /// This function is called to notify the optimization algorithm that a vector has been processed.
        /// </summary>
        /// <param name="vectorIndex" type="Integer">The vector index of the optimization vector that has been processed</param>
        public override void OnUpdateOptimizationVector(int vectorIndex)
        {
            // Check whether the current generation has too many chromosomes.
			if (_currentGeneration.Count == _chromosomes)
				_currentGeneration.Clear();

			// Check whether the current generation can be added a vector.
			if (_currentGeneration.Count < _chromosomes) {
				// Add the specified vector to the generation.
				_currentGeneration.Add(vectorIndex);
			}
        }
        #endregion

        #region OnShutdown
        /// <summary>
        /// This function is called when the script is shutdown.
        /// </summary>
        public override void OnShutdown()
        {
		    // OnShutdown Content
        }
        #endregion
    }
}