/// <summary> /// Run a single trial /// 1) Generate random test case with the box orientation specified by largeBoxRelativePos. /// 2) Apply test case visual field to black box inputs. /// 3) Activate black box. /// 4) Determine black box output with highest output, this is the selected pixel. /// /// Returns square of distance between target pixel (center of large box) and pixel selected by the black box. /// </summary> private double RunTrial(IBlackBox box, TestCaseField testCaseField, int largeBoxRelativePos, out double activationRange) { // Generate random test case. Also gets the center position of the large box. IntPoint targetPos = testCaseField.InitTestCase(0); // Apply test case visual field to black box inputs. ApplyVisualFieldToBlackBox(testCaseField, box, _visualFieldResolution, _visualOriginPixelXY, _visualPixelSize); // Clear any pre-existign state and activate. box.ResetState(); box.Activate(); if (!box.IsStateValid) { // Any black box that gets itself into an invalid state is unlikely to be // any good, so lets just bail out here. activationRange = 0.0; return(0.0); } // Find output pixel with highest activation. double minActivation, maxActivation; IntPoint highestActivationPoint = FindMaxActivationOutput(box, _visualFieldResolution, out minActivation, out maxActivation); activationRange = Math.Max(0.0, maxActivation - minActivation); // Get the distance between the target and activated pixels, in the real coordinate space. // We actually want squared distance (not distance) thus we can skip taking the square root (expensive CPU operation). return(CalcRealDistanceSquared(targetPos, highestActivationPoint)); }
/// <summary> /// Apply the provided test case to the provided black box's inputs (visual input field). /// </summary> public static void ApplyVisualFieldToBlackBox(TestCaseField testCaseField, IBlackBox box, int visualFieldResolution, double visualOriginPixelXY, double visualPixelSize) { int inputIdx = 0; double yReal = visualOriginPixelXY; for (int y = 0; y < visualFieldResolution; y++, yReal += visualPixelSize) { double xReal = visualOriginPixelXY; for (int x = 0; x < visualFieldResolution; x++, xReal += visualPixelSize, inputIdx++) { box.InputSignalArray[inputIdx] = testCaseField.GetPixel(xReal, yReal); } } }
/// <summary> /// Evaluate the provided IBlackBox against the XOR problem domain and return its fitness score. /// /// Fitness value explanation. /// 1) Max distance from target position in each trial is sqrt(2)*VisualFieldEdgeLength (target in one corner and selected target in /// opposite corner). /// /// 2) An agents is scored by squaring the distance of its selected target from the actual target, squaring the value, /// taking the average over all test cases and then taking the square root. This is referred to as the root mean squared distance (RMSD) /// and is effectively an implementation of least squares (least squared error). The square root term converts values back into units /// of distance (rather than squared distance) /// /// 3) An agent selecting points at random will score VisualFieldEdgeLength * MeanLineInSquareRootMeanSquareLength. Any agent scoring /// this amount or less is assigned a fitness of zero. All other scores are scaled and translated into the range 0-100 where 0 is no better /// or worse than a random agent, and 100 is perfectly selecting the correct target for all test cases (distance of zero between target and /// selected target). /// /// 4) In addition to this the range of output values is scaled to 0-10 and added to the final score, this encourages solutions with a wide /// output range between the highest activation (the selected pixel) and the lowest activation (this encourages prominent/clear selection). /// /// An alternative scheme is fitness = 1/RMSD (separately handling the special case where RMSD==0). /// However, this gives a non-linear increase in fitness as RMSD decreases linearly, which in turns produces a 'spikier' fitness landscape /// which is more likely to cause genomes and species to get caught in a local maximum. /// </summary> public FitnessInfo Evaluate(IBlackBox box) { _evalCount++; // Accumulate square distance from each test case. double acc = 0.0; double activationRangeAcc = 0.0; TestCaseField testCaseField = new TestCaseField(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 25; j++) { double activationRange; acc += RunTrial(box, testCaseField, i, out activationRange); activationRangeAcc += activationRange; } } // Calc root mean squared distance (RMSD) and calculate fitness based comparison to the random agent. const double threshold = VisualFieldEdgeLength * 0.5772; double rmsd = Math.Sqrt(acc / 75.0); double fitness; if (rmsd > threshold) { fitness = 0.0; } else { fitness = (((threshold - rmsd) * 100.0) / threshold) + (activationRangeAcc / 7.5); } // Set stop flag when max fitness is attained. if (!_stopConditionSatisfied && fitness == MaxFitness) { _stopConditionSatisfied = true; } return(new FitnessInfo(fitness, rmsd)); }
/// <summary> /// Construct with an INeatExperiment. This provides config data parsed from the experiment config XML and a method for /// creating genome decoders for different visual resolutions. /// </summary> public BoxesVisualDiscriminationView(BoxesVisualDiscriminationExperiment experiment) { try { InitializeComponent(); cbxResolution.SelectedIndex = 0; _experiment = experiment; SetResolution(_experiment.VisualFieldResolution); _testCaseField = new TestCaseField(); // Create a bitmap for the picturebox. int width = Width; int height = Height; _image = new Bitmap(width, height, ViewportPixelFormat); pbx.Image = _image; } finally { _initializing = false; } }