/// <inheritdoc /> public override ITreeProgram <double> Simplify() { // if its a constant value, just return a constant with that value if (this.IsConstant()) { return(new Constant(this.Compute())); } // otherwise first tries to simplify children var input = new ITreeProgram <double> [this.Input.Count]; for (var i = 0; i < this.Input.Count; i++) { input[i] = this.Input[i].Simplify(); } // if operands are equal return the first if (input[0].Equals(input[1])) { return(input[0]); } // check whether one of operands is maximum and return the other if (input[0].EqualsConstant(double.MaxValue) || input[0].EqualsConstant(double.PositiveInfinity)) { return(input[1]); } if (input[1].EqualsConstant(double.MaxValue) || input[1].EqualsConstant(double.PositiveInfinity)) { return(input[0]); } return(this.CreateNew(input)); }
/// <summary> /// Gets a new copy of <paramref name="program" /> where all sub-programs that are equal to /// <paramref name="oldSubProgram" /> are replaced by <paramref name="newSubProgram" />. /// </summary> /// <param name="program">The root program to copy and search for the given sub-program .</param> /// <param name="oldSubProgram">The sub-program we want to replace.</param> /// <param name="newSubProgram">The new sub-program to replace the given one.</param> /// <returns> /// A copy of the program with the given descendant replaced by the new program. If the given program is equal to the /// sub-program we want to replace, then the replacement is returned. If the given sub-program is not found, a copy of /// the original program is returned, or <c>null</c> if the program is <c>null</c>. /// </returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static ITreeProgram <TOutput> Replace <TOutput>( this ITreeProgram <TOutput> program, ITreeProgram <TOutput> oldSubProgram, ITreeProgram <TOutput> newSubProgram) { if (program == null || oldSubProgram == null || newSubProgram == null) { return(program); } // checks if program is equal, return replacement if (program.Equals(oldSubProgram)) { return(newSubProgram); } // replaces children recursively and creates a new program if (program.Input == null || program.Input.Count == 0) { return(program); } var children = new ITreeProgram <TOutput> [program.Input.Count]; for (var i = 0; i < program.Input.Count; i++) { children[i] = Replace(program.Input[i], oldSubProgram, newSubProgram); } return(program.CreateNew(children)); }
private static void GetCommonRegionIndexes <TOutput>( this ITreeProgram <TOutput> program, ITreeProgram <TOutput> otherProgram, IDictionary <uint, uint> indexes, ref uint idx1, ref uint idx2) { // add the corresponding (common region) indexes indexes.Add(idx1, idx2); // check if children differ (different sub-structure) if (program?.Input == null || otherProgram?.Input == null || program.Input.Count != otherProgram.Input.Count) { // just advance the indexes of both sub-trees if (program != null) { idx1 += (uint)program.Length - 1; } if (otherProgram != null) { idx2 += (uint)otherProgram.Length - 1; } return; } // programs have same number of children, iterate recursively for (var i = 0; i < program.Input.Count; i++) { idx1++; idx2++; GetCommonRegionIndexes(program.Input[i], otherProgram.Input[i], indexes, ref idx1, ref idx2); } }
/// <inheritdoc /> public override ITreeProgram <double> Simplify() { // if its a constant value, just return a constant with that value if (this.IsConstant()) { return(new Constant(this.Compute())); } // otherwise first tries to simplify children var input = new ITreeProgram <double> [this.Input.Count]; for (var i = 0; i < this.Input.Count; i++) { input[i] = this.Input[i].Simplify(); } // if the operands are equal, return 0 if (input[0].Equals(input[1])) { return(Constant.Zero); } // check whether second operand is 0 and return the first return(input[1].EqualsConstant(0) ? input[0] : this.CreateNew(input)); }
private static ITreeProgram <TOutput> ProgramAt <TOutput>(this ITreeProgram <TOutput> program, ref uint index) { if (index == 0) { return(program); } if (program?.Input == null) { return(default(ITreeProgram <TOutput>)); } if (index >= program.Length) { index -= (uint)program.Length - 1; return(default(ITreeProgram <TOutput>)); } foreach (var child in program.Input) { index--; var prog = child.ProgramAt(ref index); if (prog != null || index == 0) { return(prog); } } return(default(ITreeProgram <TOutput>)); }
/// <summary> /// Mutates the given <typeparamref name="TProgram" /> by randomly replacing each sub-program by one program with the /// same arity taken from the defined <see cref="PrimitiveSet{TProgram}" />. /// </summary> /// <param name="program">The program we want to mutate.</param> /// <returns> /// A new <typeparamref name="TProgram" /> created by randomly replacing each sub-program by one program with the same /// arity taken from the defined <see cref="PrimitiveSet{TProgram}" />. /// </returns> public TProgram Mutate(TProgram program) { if (program == null) { return(default(TProgram)); } // mutates all children var numChildren = program.Input.Count; var newChildren = new ITreeProgram <TOutput> [numChildren]; for (var i = 0; i < numChildren; i++) { newChildren[i] = this.Mutate((TProgram)program.Input[i]); } // checks whether to mutate this program (otherwise use same program) var primitive = program; if (this._random.NextDouble() < this.MutationProbability && this._primitives.ContainsKey(numChildren)) { // mutates by creating a new random program with same arity and same children primitive = this._primitives[numChildren].GetRandomItem(this._random); } // creates new program with new children return((TProgram)primitive.CreateNew(newChildren)); }
private static ITreeProgram <TOutput> Replace <TOutput>( this ITreeProgram <TOutput> program, ref uint index, ITreeProgram <TOutput> newSubProgram) { if (program == null) { return(default(ITreeProgram <TOutput>)); } if (index == 0) { return(newSubProgram); } if (program.Input == null) { return(program); } var newChildren = program.Input.ToArray(); for (var i = 0; i < program.Input.Count; i++) { index--; newChildren[i] = Replace(program.Input[i], ref index, newSubProgram); if (index == 0) { break; } } return(program.CreateNew(newChildren)); }
/// <summary> /// Verifies whether the <see cref="MathProgram" /> contains a constant value, i.e., whether any one of its /// descendant programs are instances have a constant value equal to <paramref name="val" />. /// </summary> /// <returns> /// <c>true</c>, if program contains a constant value equal to <paramref name="val" />, <c>false</c> otherwise. /// </returns> /// <param name="program">The program to verify whether it contains a constant.</param> /// <param name="val">The value to test for the program.</param> public static bool ContainsConstant(this ITreeProgram <double> program, double val) { return(program != null && (program.EqualsConstant(val) || program.Input != null && program.Input.Count > 0 && program.Input.Any(child => child.ContainsConstant(val)))); }
/// <summary> /// Checks whether the given <see cref="MathProgram" /> computes a value that is consistently equivalent to that /// computed /// by another program according to some margin. The method works by substituting the variables in both programs' /// expressions by random values for a certain number of trials. In each trial, the difference between the values /// computed by both programs is calculated. If the difference is less than a given margin for all the trials, then the /// programs are considered to be value-equivalent. /// </summary> /// <param name="program">The first program that we want to test.</param> /// <param name="other">The second program that we want to test.</param> /// <param name="margin"> /// The margin used to compare against the difference of values computed by both expressions in each /// trial. /// </param> /// <param name="numTrials">The number of trials used to discern whether the given programs are equivalent.</param> /// <returns><c>True</c> if the given programs are considered to be value-equivalent, <c>False</c> otherwise.</returns> public static bool IsValueEquivalent( this ITreeProgram <double> program, ITreeProgram <double> other, double margin = DEFAULT_MARGIN, uint numTrials = DEFAULT_NUM_TRIALS) { // checks null if (program == null || other == null) { return(false); } // checks equivalence if (program.Equals(other)) { return(true); } // checks expression or constant equivalence after simplification program = program.Simplify(); other = other.Simplify(); if (program.Equals(other) || program.IsConstant() && other.IsConstant() && Math.Abs(program.Compute() - other.Compute()) < margin) { return(true); } // gets RMSE by replacing the values of the variables in some number of trials return(program.GetValueRmsd(other, numTrials) <= margin); }
/// <summary> /// Verifies whether the <see cref="MathProgram" /> has a constant value, i.e., whether all its descendant leaf /// programs are instances of <see cref="Constant" />. /// </summary> /// <returns><c>true</c>, if all leaf programs are constant, <c>false</c> otherwise.</returns> /// <param name="program">The program to verify whether it is a constant.</param> public static bool IsConstant(this ITreeProgram <double> program) { return(program != null && (program is Constant || program.Input != null && program.Input.Count > 0 && program.Input.All(child => child.IsConstant()))); }
private static string GetFunctionPattern(MathProgram primitive, Variable dummy) { // creates a new function program whose children are dummies var children = new ITreeProgram <double> [primitive.Input?.Count ?? 0]; for (var i = 0; i < children.Length; i++) { children[i] = dummy; } var dummyPrimitive = primitive.CreateNew(children); // replaces dummy labels to get programs of the expression var expression = dummyPrimitive.Expression; var exprElements = Regex.Split(expression, $"{dummy.Label}"); // creates a regex splitting pattern var pattern = new StringBuilder(); foreach (var exprElement in exprElements) { var correctedParenthesis = exprElement.Replace("(", @"\("); var subPattern = (correctedParenthesis.Length == 1 ? @"\" : string.Empty) + correctedParenthesis; pattern.Append($"{subPattern}{SUB_ELEM_PATTERN}"); } if (exprElements.Length > 0) { pattern.Remove(pattern.Length - SUB_ELEM_PATTERN.Length, SUB_ELEM_PATTERN.Length); } return(pattern.ToString()); }
private TProgram Generate(PrimitiveSet <TProgram> primitives, uint depth, uint maxDepth) { // check max depth, just return a random terminal program if (depth == maxDepth) { return(primitives.Terminals.ToList().GetRandomItem(this._random)); } // otherwise, get a random primitive var primitivesList = primitives.Functions.ToList(); primitivesList.AddRange(primitives.Terminals); var program = primitivesList.GetRandomItem(this._random); // recursively generate random children for it var numChildren = program.Children.Count; var children = new ITreeProgram <TOutput> [numChildren]; for (var i = 0; i < numChildren; i++) { children[i] = this.Generate(primitives, depth + 1, maxDepth); } // generate a new program with the children return((TProgram)program.CreateNew(children)); }
/// <summary> /// Creates a new <see cref="IfFunction" /> with the given sub-programs. /// </summary> /// <param name="conditionProgram">The sub-program computing the conditional value.</param> /// <param name="zeroProgram">The sub-program computing the value if the conditional is 0.</param> /// <param name="positiveProgram">The sub-program computing the value if the conditional is >0</param> /// <param name="negativeProgram">The sub-program computing the value if the conditional is <0</param> public IfFunction( ITreeProgram <double> conditionProgram, ITreeProgram <double> zeroProgram, ITreeProgram <double> positiveProgram, ITreeProgram <double> negativeProgram) : base(new[] { conditionProgram, zeroProgram, positiveProgram, negativeProgram }) { this.Expression = $"({this.ConditionProgram.Expression}?{this.ZeroProgram.Expression}:" + $"{this.PositiveProgram.Expression}:{this.NegativeProgram.Expression})"; }
/// <summary> /// Gets the maximum breadth of the program, i.e., the number of <see cref="ITreeProgram{TOutput}" /> leaves /// encountered starting from this program. /// </summary> /// <param name="program">The root program to calculate the breadth.</param> /// <returns>The maximum breadth of the program.</returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static uint GetMaxBreadth <TOutput>(this ITreeProgram <TOutput> program) { return(program == null ? 0 : program.Input == null || program.Input.Count == 0 ? 1 : (uint)program.Input.Sum(child => child.GetMaxBreadth())); }
/// <summary> /// Gets all the <see cref="ITreeProgram{TOutput}" /> sub-combinations of the given program. If the program is a leaf, /// there are no combinations, otherwise returns all the possible combinations between the sub-programs of the children /// and also the sub-combinations of each child. /// </summary> /// <param name="program">The program we want to get the sub-combinations.</param> /// <returns>All the sub-combinations of the given program.</returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static ISet <ITreeProgram <TOutput> > GetSubCombinations <TOutput>(this ITreeProgram <TOutput> program) { //remove program itself from sub-combinations var subCombs = GetSubCombs(program); subCombs.Remove(program); return(subCombs); }
/// <inheritdoc /> public override ITreeProgram <double> Simplify() { // if its a constant value, just return a constant with that value if (this.IsConstant()) { return(new Constant(this.Compute())); } // otherwise first tries to simplify children var input = new ITreeProgram <double> [this.Input.Count]; for (var i = 0; i < this.Input.Count; i++) { input[i] = this.Input[i].Simplify(); } //check whether one operand is 1 and return the other if (input[0].EqualsConstant(1)) { return(input[1]); } if (input[1].EqualsConstant(1)) { return(input[0]); } //check whether one of operands is 0 and return 0 if (input[0].EqualsConstant(0) || input[1].EqualsConstant(0)) { return(Constant.Zero); } // check whether there's a ((x*x)*x) situation, returns (x^3) if (input[0] is MultiplicationFunction && input[0].Input[0].Equals(input[1]) && input[0].Input[1].Equals(input[1])) { return(new PowerFunction(input[1], new Constant(3))); } if (input[1] is MultiplicationFunction && input[1].Input[0].Equals(input[0]) && input[1].Input[1].Equals(input[0])) { return(new PowerFunction(input[0], new Constant(3))); } // check whether there's a ((x^a)*x) situation, returns (x^(a+1)) if (input[0] is PowerFunction && input[0].Input[1] is Constant && input[0].Input[0].Equals(input[1])) { return(new PowerFunction(input[1], new Constant(input[0].Input[1].Compute() + 1))); } if (input[1] is PowerFunction && input[1].Input[1] is Constant && input[1].Input[0].Equals(input[0])) { return(new PowerFunction(input[0], new Constant(input[1].Input[1].Compute() + 1))); } return(this.CreateNew(input)); }
/// <summary> /// Converts the given <see cref="string" /> expression written in normal notation, i.e., where functions are written /// in the form func(arg1 arg2 ...) or (arg1 func arg2) to a <see cref="MathProgram" />. /// </summary> /// <param name="expression">The expression we want to convert to an program.</param> /// <returns>A <see cref="MathProgram" /> converted from the given expression.</returns> public MathProgram FromNormalNotation(string expression) { if (expression == null) { return(null); } // cleans expression expression = expression.Replace(" ", string.Empty); // tests for terminals if (this._primitiveLabels.ContainsKey(expression)) { return(this._primitiveLabels[expression]); } if (double.TryParse(expression, out var constValue)) { return(new Constant(constValue)); } // tests for functions foreach (var functionPattern in this._functionPatterns) { // checks if pattern matches var subExprs = RegexSplit(expression, functionPattern.Key); var numChildren = functionPattern.Value.Input.Count; if (subExprs.Count != numChildren || subExprs.Count == 1 && string.Equals(subExprs[0], expression)) { continue; } // tries to parse sub-expressions as children var children = new ITreeProgram <double> [numChildren]; var invalid = false; for (var i = 0; i < numChildren; i++) { var child = FromNormalNotation(subExprs[i]); if (child == null) { // if a child's expression is invalid, break and try next invalid = true; break; } children[i] = child; } if (invalid) { continue; } // creates a new program return((MathProgram)functionPattern.Value.CreateNew(children)); } return(null); }
/// <summary> /// Gets a <see cref="IDictionary{T,T}" /> representing the program indexes in the common region between /// <paramref name="program" /> and <paramref name="otherProgram" />. The dictionary represents the index /// correspondence between the sub-programs of the given programs. /// </summary> /// <returns>The index correspondence between the sub-programs of the given programs.</returns> /// <param name="program">The first program</param> /// <param name="otherProgram">The other program to get the common region.</param> /// <typeparam name="TOutput">The type of program output.</typeparam> public static IDictionary <uint, uint> GetCommonRegionIndexes <TOutput>( this ITreeProgram <TOutput> program, ITreeProgram <TOutput> otherProgram) { var commonRegionIndexes = new Dictionary <uint, uint>(); var idx1 = 0u; var idx2 = 0u; GetCommonRegionIndexes(program, otherProgram, commonRegionIndexes, ref idx1, ref idx2); return(commonRegionIndexes); }
/// <summary> /// Gets a dictionary containing all the <see cref="ITreeProgram{TOutput}" /> leaves of the given program and their /// count. This corresponds to the leaf nodes of the expression tree of the given program. /// </summary> /// <param name="program">The program whose leaf sub-programs we want to retrieve.</param> /// <returns>A set containing all the <see cref="ITreeProgram{TOutput}" /> sub-programs of the given program.</returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static IDictionary <ITreeProgram <TOutput>, uint> GetLeaves <TOutput>(this ITreeProgram <TOutput> program) { if (program == null) { return(null); } var leaves = new Dictionary <ITreeProgram <TOutput>, uint>(); GetLeaves(program, leaves); return(leaves); }
/// <summary> /// Gets a <see cref="ISet{T}" /> containing all the descendant <see cref="ITreeProgram{TOutput}" /> of the given /// program. /// </summary> /// <param name="program">The program we want to get the sub-programs.</param> /// <returns>A set containing all the sub programs of the given program.</returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static ITreeProgram <TOutput>[] GetSubPrograms <TOutput>(this ITreeProgram <TOutput> program) { if (program == null) { return(null); } var subProgs = new ITreeProgram <TOutput> [program.Length - 1]; var index = 0; GetSubPrograms(program, ref index, program.Length - 1, subProgs); return(subProgs); }
/// <summary> /// Gets the root-mean-square deviation (RMSD) between the values computed for the given programs. The method works by /// substituting the variables in both programs' expressions by random values for a certain number of trials. In each /// trial, the square of the difference between the values computed by both programs is calculated. /// </summary> /// <param name="program">The first program that we want to test.</param> /// <param name="other">The second program that we want to test.</param> /// <param name="numTrials">The number of trials used to compute the squared difference.</param> /// <returns>The RMSD between the several values computed for the given programs.</returns> public static double GetValueRmsd( this ITreeProgram <double> program, ITreeProgram <double> other, uint numTrials = DEFAULT_NUM_TRIALS) { // checks null if (program == null || other == null) { return(double.MaxValue); } // checks expression or constant equivalence after simplification program = program.Simplify(); other = other.Simplify(); if (program.Equals(other) || program.IsConstant() && other.IsConstant()) { return(Math.Abs(program.Compute() - other.Compute())); } // replaces variables of each expression by custom variables var customVariables = new Dictionary <Variable, Variable>(); program = ReplaceVariables(program, customVariables); other = ReplaceVariables(other, customVariables); if (customVariables.Count == 0) { return(double.MaxValue); } // gets random values for each variable var customVariablesValues = customVariables.Values.ToDictionary( variable => variable, variable => GetTrialRandomNumbers(numTrials, variable.Range)); // runs a batch of trials var squareDiffSum = 0d; for (var i = 0; i < numTrials; i++) { // replaces the value of each variable by a random number foreach (var customVariablesValue in customVariablesValues) { customVariablesValue.Key.Value = customVariablesValue.Value[i]; } // computes difference of the resulting values of the expressions var prog1Value = program.Compute(); var prog2Value = other.Compute(); var diff = prog1Value.Equals(prog2Value) ? 0 : prog1Value - prog2Value; squareDiffSum += diff * diff; } // returns RMSD return(Math.Sqrt(squareDiffSum / numTrials)); }
/// <summary> /// Checks whether a given <see cref="ITreeProgram{TOutput}" /> is a sub-program of another /// <see cref="ITreeProgram{TOutput}" />. /// A sub-program is an program that is a descendant of a given program. /// </summary> /// <param name="program">The program we want to know if it is a sub-program.</param> /// <param name="other">The program for which to look for a descendant equal to the given program.</param> /// <returns><c>true</c>, if the program is a descendant of the other program, <c>false</c> otherwise.</returns> /// <typeparam name="TOutput">The type of program output.</typeparam> public static bool IsSubProgramOf <TOutput>(this ITreeProgram <TOutput> program, ITreeProgram <TOutput> other) { // checks prog if (program == null || other == null || program.Length >= other.Length || other.Input?.Count == 0) { return(false); } // search the descendants for the given program return(other.Input != null && other.Input.Any(child => program.Equals(child) || program.IsSubProgramOf(child))); }
/// <inheritdoc /> public override ITreeProgram <double> Simplify() { // if its a constant value, just return a constant with that value if (this.IsConstant()) { return(new Constant(this.Compute())); } // otherwise first tries to simplify children var input = new ITreeProgram <double> [this.Input.Count]; for (var i = 0; i < this.Input.Count; i++) { input[i] = this.Input[i].Simplify(); } // check whether the first child is a constant and returns one of the other children accordingly var child1 = input[0]; if (child1.IsConstant()) { var val = child1.Compute(); return(val.Equals(0) ? input[1] : (val > 0 ? input[2] : input[3])); } // check whether first child is a variable, check its range if (child1 is Variable variable) { var range = variable.Range; if (range.Min.Equals(0) && range.Max.Equals(0)) { return(input[1]); } if (range.Min > 0) { return(input[2]); } if (range.Max < 0) { return(input[3]); } } // check whether result children are equal, in which case replace by one of them if (input[1].Equals(input[2]) && input[1].Equals(input[3])) { return(input[1]); } return(this.CreateNew(input)); }
/// <summary> /// Gets a program node representing the primitive of the corresponding <see cref="MathProgram" /> derived type. For /// functions this corresponds to a <see cref="MathProgram" /> whose inputs are <see cref="Constant.Zero" />, for /// terminals it returns the terminal itself. /// </summary> /// <returns>The default program node of the corresponding <see cref="MathProgram" /> derived type.</returns> public ITreeProgram <double> GetPrimitive() { if (this.Input == null || this.Input.Count == 0) { return(this); } var children = new ITreeProgram <double> [this.Input.Count]; for (var i = 0; i < this.Input.Count; i++) { children[i] = Constant.Zero; } return(this.CreateNew(children)); }
/// <summary> /// Checks whether a given <see cref="ITreeProgram{TOutput}" /> contains the given sub-program. /// A sub-program is an program that is a descendant of a given program. /// </summary> /// <returns><c>true</c>, if the given program is a descendant of the given program, <c>false</c> otherwise.</returns> /// <param name="program">The program for which to look for a descendant equal to the given program.</param> /// <param name="other">The program we want to know if it is a sub-program.</param> /// <typeparam name="TOutput">The type of program output.</typeparam> public static bool ContainsSubElement <TOutput>( this ITreeProgram <TOutput> program, ITreeProgram <TOutput> other) { // checks prog if (program == null || other == null || program.Length <= other.Length || program.Input?.Count == 0) { return(false); } // search the descendants for the given program return(program.Input != null && program.Input.Any(child => other.Equals(child) || child.ContainsSubElement(other))); }
private static ITreeProgram <TOutput> GetLargestCommon( ref ITreeProgram <TOutput> prog1, ref ITreeProgram <TOutput> prog2, ISet <ITreeProgram <TOutput> > ignorePrograms = null) { // gets sub-programs and sort descendingly var subProgs1 = new SortedSet <ITreeProgram <TOutput> >(prog1.GetSubPrograms(), Comparer <ITreeProgram <TOutput> > .Create((a, b) => - (a.Length == b.Length ? a.CompareTo(b) : a.Length.CompareTo(b.Length)))); var subProgs2 = new HashSet <ITreeProgram <TOutput> >(prog2.GetSubPrograms()); return(subProgs1.FirstOrDefault( subProg1 => (ignorePrograms == null || !ignorePrograms.Contains(subProg1)) && subProgs2.Contains(subProg1))); }
private static IDictionary <ITreeProgram <TOutput>, uint> GetSubPrograms(ITreeProgram <TOutput> program) { // organizes sub-programs in dictionary-count form var subProgs = program.GetSubPrograms(); var subProgsCounts = new Dictionary <ITreeProgram <TOutput>, uint>(); foreach (var subProg in subProgs) { if (!subProgsCounts.ContainsKey(subProg)) { subProgsCounts.Add(subProg, 0); } subProgsCounts[subProg]++; } return(subProgsCounts); }
/// <summary> /// Computes a <see cref="Range" /> representing the minimum and maximum values that a given <see cref="MathProgram" /> /// can compute, as dictated by its sub-programs. /// </summary> /// <param name="program">The program whose range we want to compute.</param> /// <returns>The range of the given program.</returns> public static Range GetRange(this ITreeProgram <double> program) { // checks for constant value if (program.IsConstant()) { var value = program.Compute(); return(new Range(value, value)); } // checks for variable if (program is Variable) { return(((Variable)program).Range); } // collects info on ranges of all children var childrenRanges = new List <IEnumerable <double> >(); foreach (var child in program.Input) { var childRange = GetRange(child); childrenRanges.Add(new[] { childRange.Min, childRange.Max }); } // gets all combinations between children ranges var min = double.MaxValue; var max = double.MinValue; var allRangeCombinations = childrenRanges.GetAllCombinations(); foreach (var rangeCombination in allRangeCombinations) { // builds new program by replacing children with constant values (range min or max) var children = new ITreeProgram <double> [rangeCombination.Count]; for (var i = 0; i < rangeCombination.Count; i++) { children[i] = new Constant(rangeCombination[i]); } var newElem = program.CreateNew(children); // checks min and max values from new prog value var val = newElem.Compute(); min = Math.Min(min, val); max = Math.Max(max, val); } return(new Range(min, max)); }
private static ISet <ITreeProgram <TOutput> > GetSubCombs <TOutput>(ITreeProgram <TOutput> program) { var combs = new HashSet <ITreeProgram <TOutput> >(); if (program == null) { return(combs); } // checks no more children if (program.IsLeaf()) { combs.Add(program); return(combs); } // gets sub-programs from all children var childrenSubCombs = new List <IEnumerable <ITreeProgram <TOutput> > >(); foreach (var child in program.Input) { var childSubCombs = GetSubCombs(child); childrenSubCombs.Add(childSubCombs); // adds the sub-combinations of children to combination list foreach (var childSubComb in childSubCombs) { combs.Add(childSubComb); } } // creates new programs where each child is replaced by some sub-combination of it var allChildrenCombs = childrenSubCombs.GetAllCombinations(); foreach (var children in allChildrenCombs) { if (children.Count == program.Input.Count) { combs.Add(program.CreateNew(children)); } } childrenSubCombs.Clear(); return(combs); }
private static ISet <ITreeProgram <TOutput> > GetSubOffspring( ITreeProgram <TOutput> subProg1, ITreeProgram <TOutput> subProg2) { var offspring = new HashSet <ITreeProgram <TOutput> > { subProg1, subProg2 }; // check if children differ (different sub-structure) or no children -> nothing to do in this branch if (subProg1.Input == null || subProg2.Input == null || subProg1.Input.Count != subProg2.Input.Count || subProg1.Input.Count == 0) { return(offspring); } // programs have same number of children, iterate recursively to get possible children in each sub-branch var numChildren = subProg1.Input.Count; var subChildren = new List <IEnumerable <ITreeProgram <TOutput> > >(numChildren); for (var i = 0; i < numChildren; i++) { subChildren.Add(GetSubOffspring(subProg1.Input[i], subProg2.Input[i]).ToList()); } // gets all combinations of possible sub-branches var childrenCombinations = subChildren.GetAllCombinations().ToList(); var subOffspring = new HashSet <ITreeProgram <TOutput> >(); // if sub-programs are the same function, we only need one of them if (subProg1.GetType() == subProg2.GetType()) { offspring.Remove(subProg2); } // for each sub-program, creates new sub-programs for each combination foreach (var subProg in offspring) { foreach (var childCombination in childrenCombinations) { subOffspring.Add(subProg.CreateNew(childCombination)); } } return(subOffspring); }