Can I do concurrent cobyla optimizations using Accord.Net?

Viewed 68

The code below is a minimal working example of a circle estimator i'm using. It usses Accord.Net's cobyla optimizer. The input for the localize method is a set of points. The center location is found using the optimizer and returned. (My real code is a bit more complicated but I left out most less relevant stuff)

The code is working fine but the issue I have is with the static fields (I have more of them in my real code). When I optimize multiple problems concurrently the static field will give problems because the optimizer might read values intended for another thread.

The question is how do I make this code work for concurrent code? Can I ditch the statics and still get the points inside the Func<double[], double>? Are there other ways to circumvent the concurrency problems with the statics?

    internal class CircleLocalizer
    {
        private static (double, double)[] points; // I dislike these statics!
        private double[] prior = { 0, 0 };

        Func<double[], double> objective = x =>
        {
            var distancesToCenter = CircleLocalizer.points.Select(p => Distance.Euclidean(new[] { p.Item1, p.Item2 }, new[] { x[0], x[1] }));
            var averageDistance = distancesToCenter.Average();
            return distancesToCenter.Select(d => Math.Pow(d - averageDistance, 2)).Sum();
        };

        public double[] Localize((double, double) points)
        {
            CircleLocalizer.points = points;

            Cobyla cobyla = new Cobyla(2, objective);
            cobyla.Minimize(Prior);

            return cobyla.Solution;
        }
    }
1 Answers

I would suggest rewriting objective to a regular method that takes the points as a parameter:

double Objective(double[] x, (double, double)[] points){
     var distancesToCenter = points.Select(p => Distance.Euclidean(new[] { p.Item1, p.Item2 }, new[] { x[0], x[1] }));
     var averageDistance = distancesToCenter.Average();
     return distancesToCenter.Select(d => Math.Pow(d - averageDistance, 2)).Sum();
}

and call it like

new Cobyla(2, x => Objective(x, points)); 

When you are writing a lambda function you can 'capture' variables in the outer scope. Internally this is converted to an object with all the captured variables as fields in the class.

Related