/// <summary> /// Adds an observation /// </summary> /// <param name="x"></param> public void Add(double x) { if (!double.IsNaN(x)) { mva.Add(x); } }
/// <summary> /// Finds the input that maximizes a function. /// </summary> /// <param name="bounds">Bounds for the search.</param> /// <param name="Evaluate">The function to maximize.</param> /// <param name="GetUpperBound">Returns an upper bound to the function in a region. Need not be tight, but must become tight as the region shrinks.</param> /// <param name="xTolerance">Allowable relative error in the solution on any dimension. Must be greater than zero.</param> /// <returns>A Vector close to the global maximum of the function.</returns> public static Vector Search(Region bounds, Func <Vector, double> Evaluate, Func <Region, double> GetUpperBound, double xTolerance = 1e-4) { if (xTolerance <= 0) { throw new ArgumentOutOfRangeException($"xTolerance <= 0"); } int dim = bounds.Lower.Count; if (dim == 0) { return(Vector.Zero(dim)); } PriorityQueue <QueueNode> queue = new PriorityQueue <QueueNode>(); double lowerBound = double.NegativeInfinity; Vector argmax = bounds.GetMidpoint(); long upperBoundCount = 0; Action <Region, int> addRegion = delegate(Region region, int splitDim) { Stopwatch watch = Stopwatch.StartNew(); double upperBoundF = GetUpperBound(region); watch.Stop(); if (Debug) { if (Debug && timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"GetUpperBound took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); } upperBoundCount++; //if (upperBoundCount % 100 == 0) Trace.WriteLine($"lowerBound = {lowerBound}"); double upperBound = upperBoundF; if (upperBound > lowerBound) { if (Debug) { Trace.WriteLine($"added region {region} with upperBoundF = {upperBoundF}"); } QueueNode node = new QueueNode(region, upperBound, splitDim); queue.Add(node); } else if (Debug) { Trace.WriteLine($"rejected region {region} with upperBound {upperBound} <= lowerBound {lowerBound}"); } }; addRegion(bounds, 0); while (queue.Count > 0) { var node = queue.ExtractMinimum(); // gets the node with highest upper bound if (node.UpperBound <= lowerBound) { continue; } Region region = node.Region; // compute the lower bound Vector midpoint = region.GetMidpoint(); Stopwatch watch = Stopwatch.StartNew(); double nodeLowerBound = Evaluate(midpoint); watch.Stop(); if (Debug) { if (Debug && timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"Evaluate took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); } if (Debug) { Trace.WriteLine($"expanding {node} lower bound = {nodeLowerBound}"); } if (nodeLowerBound > node.UpperBound) { throw new Exception("nodeLowerBound > node.UpperBound"); } if (nodeLowerBound > lowerBound) { argmax = midpoint; lowerBound = nodeLowerBound; } // Find a dimension to split on. // As a region gets split, this will cycle through the dimensions. int splitDim = (node.SplitDim + 1) % dim; // To avoid storing SplitDims, we could make a random choice. // However, this takes much longer to converge. //int splitDim = Rand.Int(dim); bool foundSplit = false; for (int i = 0; i < dim; i++) { if (MMath.AbsDiff(region.Upper[splitDim], region.Lower[splitDim], 1e-10) < xTolerance) { splitDim++; if (splitDim == dim) { splitDim = 0; } } else { foundSplit = true; break; } } if (!foundSplit) { break; } // split the node double splitValue = midpoint[splitDim]; if (region.Upper[splitDim] != splitValue) { Region leftRegion = new Region(region); leftRegion.Upper[splitDim] = splitValue; addRegion(leftRegion, splitDim); } if (region.Lower[splitDim] != splitValue) { Region rightRegion = new Region(region); rightRegion.Lower[splitDim] = splitValue; addRegion(rightRegion, splitDim); } } //Trace.WriteLine($"BranchAndBound.Search upperBoundCount = {upperBoundCount}"); return(argmax); }
/// <summary> /// Finds the input that maximizes a function. /// </summary> /// <param name="bounds">Bounds for the search.</param> /// <param name="Evaluate">The function to maximize.</param> /// <param name="GetUpperBound">Returns an upper bound to the function in a region. Need not be tight, but must become tight as the region shrinks.</param> /// <param name="xTolerance">Allowable relative error in the solution on any dimension. Must be greater than zero.</param> /// <returns>A Vector close to the global maximum of the function.</returns> public static Vector Search(Region bounds, Func <Vector, double> Evaluate, Func <Region, double> GetUpperBound, double xTolerance = 1e-4) { if (xTolerance <= 0) { throw new ArgumentOutOfRangeException($"xTolerance <= 0"); } int dim = bounds.Lower.Count; if (dim == 0) { return(Vector.Zero(dim)); } PriorityQueue <QueueNode> queue = new PriorityQueue <QueueNode>(); double lowerBound = double.NegativeInfinity; Vector argmax = bounds.GetMidpoint(); long upperBoundCount = 0; if (Debug) { Func <Region, double> GetUpperBound1 = GetUpperBound; GetUpperBound = region => { Stopwatch watch = Stopwatch.StartNew(); double upperBound = GetUpperBound1(region); watch.Stop(); if (timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"GetUpperBound took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); //if (upperBoundCount % 100 == 0) Trace.WriteLine($"lowerBound = {lowerBound}"); return(upperBound); }; } Action <Region, int, double> addRegion = delegate(Region region, int splitDim, double upperBound) { if (upperBound > lowerBound) { if (Debug) { Trace.WriteLine($"added region {region} with upperBound = {upperBound}"); } QueueNode node = new QueueNode(region, upperBound); queue.Add(node); } else if (Debug) { Trace.WriteLine($"rejected region {region} with upperBound {upperBound} <= lowerBound {lowerBound}"); } }; upperBoundCount++; addRegion(bounds, 0, GetUpperBound(bounds)); while (queue.Count > 0) { var node = queue.ExtractMinimum(); // gets the node with highest upper bound if (node.UpperBound <= lowerBound) { continue; } Region region = node.Region; // compute the lower bound Vector midpoint = region.GetMidpoint(); Stopwatch watch = Stopwatch.StartNew(); double nodeLowerBound = Evaluate(midpoint); watch.Stop(); if (Debug) { if (timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"Evaluate took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); Trace.WriteLine($"expanding {node} lower bound = {nodeLowerBound}"); } if (nodeLowerBound > node.UpperBound) { throw new Exception("nodeLowerBound > node.UpperBound"); } if (nodeLowerBound > lowerBound) { argmax = midpoint; lowerBound = nodeLowerBound; } Func <int, bool> DimensionCanSplit = i => MMath.AbsDiff(region.Upper[i], region.Lower[i], 1e-10) >= xTolerance; int splitDim; bool lowestChild = false; Region leftRegion = null; double upperBoundLeft = default(double); Region rightRegion = null; double upperBoundRight = default(double); if (lowestChild) { splitDim = -1; double lowestUpperBound = double.PositiveInfinity; for (int i = 0; i < dim; i++) { if (DimensionCanSplit(i)) { double splitValue2 = midpoint[i]; Region leftRegion2 = new Region(region); leftRegion2.Upper[i] = splitValue2; upperBoundCount++; double upperBoundLeft2 = GetUpperBound(leftRegion2); Region rightRegion2 = new Region(region); rightRegion2.Lower[i] = splitValue2; upperBoundCount++; double upperBoundRight2 = GetUpperBound(rightRegion2); double lowerUpperBound = Math.Min(upperBoundLeft2, upperBoundRight2); if (lowerUpperBound < lowestUpperBound) { lowestUpperBound = lowerUpperBound; upperBoundLeft = upperBoundLeft2; upperBoundRight = upperBoundRight2; leftRegion = leftRegion2; rightRegion = rightRegion2; splitDim = i; } } } if (splitDim < 0) { break; } } else { // Find a dimension to split on. splitDim = Rand.Int(dim); bool foundSplit = false; for (int i = 0; i < dim; i++) { if (!DimensionCanSplit(splitDim)) { splitDim++; if (splitDim == dim) { splitDim = 0; } } else { foundSplit = true; break; } } if (!foundSplit) { break; } } // split the node double splitValue = midpoint[splitDim]; if (Debug) { Trace.WriteLine($"splitting dimension {splitDim}"); } if (region.Upper[splitDim] != splitValue) { if (leftRegion == null) { leftRegion = new Region(region); leftRegion.Upper[splitDim] = splitValue; upperBoundCount++; upperBoundLeft = GetUpperBound(leftRegion); } addRegion(leftRegion, splitDim, upperBoundLeft); } if (region.Lower[splitDim] != splitValue) { if (rightRegion == null) { rightRegion = new Region(region); rightRegion.Lower[splitDim] = splitValue; upperBoundCount++; upperBoundRight = GetUpperBound(rightRegion); } addRegion(rightRegion, splitDim, upperBoundRight); } } if (Debug) { Trace.WriteLine($"BranchAndBound.Search upperBoundCount = {upperBoundCount}"); } return(argmax); }
/// <summary> /// Finds the input that maximizes a function. /// </summary> /// <param name="bounds">Bounds for the search.</param> /// <param name="Evaluate">The function to maximize.</param> /// <param name="GetUpperBound">Returns an upper bound to the function in a region. Need not be tight, but must become tight as the region shrinks.</param> /// <param name="xTolerance">Allowable error in the solution on any dimension. Must be greater than zero.</param> /// <returns>A Vector close to the global maximum of the function.</returns> public static Vector Search(Region bounds, Func <Vector, double> Evaluate, Func <Region, double> GetUpperBound, double xTolerance = 1e-4) { if (xTolerance <= 0) { throw new ArgumentException($"xTolerance <= 0"); } int dim = bounds.Lower.Count; if (dim == 0) { return(Vector.Zero(dim)); } PriorityQueue <QueueNode> queue = new PriorityQueue <QueueNode>(); double lowerBound = double.NegativeInfinity; Vector argmax = bounds.GetMidpoint(); Action <Region> addRegion = delegate(Region region) { Stopwatch watch = Stopwatch.StartNew(); double upperBoundF = GetUpperBound(region); watch.Stop(); if (Debug) { if (Debug && timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"GetUpperBound took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); } double upperBound = upperBoundF; if (upperBound > lowerBound) { if (Debug) { Trace.WriteLine($"added region {region} with upperBoundF = {upperBoundF}"); } QueueNode node = new QueueNode() { Region = region, UpperBound = upperBound }; queue.Add(node); } else if (Debug) { Trace.WriteLine($"rejected region {region} with upperBound {upperBound} <= lowerBound {lowerBound}"); } }; addRegion(bounds); int splitDim = 0; while (queue.Count > 0) { var node = queue.ExtractMinimum(); // gets the node with highest upper bound if (node.UpperBound <= lowerBound) { continue; } Region region = node.Region; // compute the lower bound Vector midpoint = region.GetMidpoint(); Stopwatch watch = Stopwatch.StartNew(); double nodeLowerBound = Evaluate(midpoint); watch.Stop(); if (Debug) { if (Debug && timeAccumulator.Count > 10 && watch.ElapsedMilliseconds > timeAccumulator.Mean + 4 * Math.Sqrt(timeAccumulator.Variance)) { Trace.WriteLine($"Evaluate took {watch.ElapsedMilliseconds}ms"); } timeAccumulator.Add(watch.ElapsedMilliseconds); } if (Debug) { Trace.WriteLine($"expanding region {region} with upper bound = {node.UpperBound} lower bound = {nodeLowerBound}"); } if (nodeLowerBound > node.UpperBound) { throw new Exception("nodeLowerBound > node.UpperBound"); } if (nodeLowerBound > lowerBound) { argmax = midpoint; lowerBound = nodeLowerBound; } if (splitMethod == SplitMethod.Cycle || dim == 1) { // cycle the split dimension splitDim++; if (splitDim == dim) { splitDim = 0; } } else if (splitMethod == SplitMethod.Random) { splitDim = Rand.Int(dim); } else if (splitMethod == SplitMethod.LowestChild) { // find the best dimension to split on int bestSplitDim = 0; double bestSplitScore = double.PositiveInfinity; for (int i = 0; i < dim; i++) { if (region.Upper[i] - region.Lower[i] < xTolerance) { continue; } int splitDim2 = i; double splitValue2 = midpoint[i]; Region leftRegion2 = new Region(region); leftRegion2.Upper[splitDim2] = splitValue2; double leftUpperBound = GetUpperBound(leftRegion2); Region rightRegion2 = new Region(region); rightRegion2.Lower[splitDim2] = splitValue2; double rightUpperBound = GetUpperBound(rightRegion2); double score = Math.Min(leftUpperBound, rightUpperBound); if (score < bestSplitScore) { bestSplitScore = score; bestSplitDim = i; } } if (bestSplitScore < double.MaxValue) { //Console.WriteLine("bestSplitValue = {0} (bestSplitScore = {1})", bestSplitValue, bestSplitScore); splitDim = bestSplitDim; //Console.WriteLine("argmaxGumbel = {0} splitDim = {1}", argmaxGumbel, splitDim); } else { // use the previous splitDim } } // find a dimension to split on bool foundSplit = false; for (int i = 0; i < dim; i++) { if (region.Upper[splitDim] - region.Lower[splitDim] < xTolerance) { if (++splitDim == dim) { splitDim = 0; } } else { foundSplit = true; break; } } if (!foundSplit) { break; } double splitValue = midpoint[splitDim]; // split the node Region leftRegion = new Region(region); leftRegion.Upper[splitDim] = splitValue; Region rightRegion = new Region(region); rightRegion.Lower[splitDim] = splitValue; addRegion(leftRegion); addRegion(rightRegion); } return(argmax); }