4

I am implementing a simple two-state particle filter. If you don't know what a particle filter is, that's fine - the short version is that I need to compute weighted means with weights between 0 and 1, and values between 0 and 1. Each particle has a value and a weight.

C# is giving me absolutely bizarre numerical problems though.

In trying to debug this, this is what my code looks like:

            ConcurrentBag<Particle> particles; //this is inputted as an argument to my function
            double mean = 0.0;
            double totalWeight = 0.0;
            foreach (Particle p in particles)
            {
                mean += p.Value * p.Weight;
                totalWeight += p.Weight;

                if (p.Value > 1.01 || p.Weight > 1.01)
                {
                    Console.WriteLine("Value " + p.Value);
                    Console.WriteLine("Weight " + p.Weight);
                    Console.WriteLine("wtf");
                }
            }

            if (totalWeight == 0.0)
            {
                //in this case, everything has miniscule weight, so let's just return 0.0 to avoid this precision corner case.
                return new Bernoulli(0.0);
            }
            double oldMean = mean;
            mean /= totalWeight;
            return mean;

That if statement with the "wtf" is there for debug purposes, and it's being triggered. But, the print out is:

Value 1.0 Weight 0.01

This if statement shouldn't be true at all! What is happening?

Edit: A little update on debugging. This is my current entire function:

public override IDistribution createDistribution(ConcurrentBag<Particle> particles)
        {
            if (particles.Count == 0)
            {
                throw new Exception("Cannot create Distribution from empty particle collection");
            }
            if (!particles.ToArray()[0].GetType().IsAssignableFrom(typeof(BinaryParticle)))
            {
                throw new Exception("Cannot create Bernoulli Distribution from non-Binary Particle");
            }

            decimal mean = 0.0m;
            decimal totalWeight = 0.0m;
            foreach (Particle p in particles)
            {
                mean += (decimal)(p.Value * p.Weight);
                totalWeight += (decimal)p.Weight;


                    if ((p.Weight > 1.01))
                    {
                        {
                            Console.WriteLine("Value " + p.Value);
                            Console.WriteLine("Weight " + p.Weight);
                            Console.WriteLine("Value " + p.Value.ToString("0.0000000"));
                            Console.WriteLine("wtf");
                        }
                    }


            if (totalWeight == 0.0m)
            {
                //in this case, everything has miniscule weight, so let's just return 0.0 to avoid this precision corner case.
                return new Bernoulli(0.0);
            }
            decimal oldMean = mean;
            mean /= totalWeight;

            try
            {
                return new Bernoulli((double)mean);
            }
            catch (Exception e)
            {
                decimal testMean = 0.0m;
                decimal testTotalWeight = 0.0m;
                Console.WriteLine(e);
                foreach (Particle p in particles)
                {
                    testMean += (decimal)(p.Value * p.Weight);
                    testTotalWeight += (decimal)p.Weight;
                    Console.WriteLine("weight is " + p.Weight);
                    Console.WriteLine("value is " + p.Value);
                    Console.WriteLine("Total mean is " + testMean);
                    Console.WriteLine("Total weight is " + testTotalWeight);
                }


                Console.WriteLine(testMean / testTotalWeight);
                throw new Exception();
            }
        }

"mean" is giving a different value than is being printed in the writeline in the catch block. I have no idea why. Also, bizarrely, it is weight > 1.01 that is the true condition, when weight is 0.01.

user650261
  • 2,115
  • 5
  • 24
  • 47
  • How are you verifying that value is in fact 1.0? The code `double value = 1.0; if (value > 1.01) { Console.WriteLine("wtf"); }` behaves as expected (does not print wtf). – Eric J. Jul 22 '15 at 16:54
  • 2
    Can you try `Console.WriteLine("Value " + p.Value.ToString("0.0000000"));` – Ulugbek Umirov Jul 22 '15 at 16:55
  • FWIW, your code looks correct to me. However just for the sake of writing clearer code, I would rename `double mean` to `double weightedTotal` and change `mean /= totalWeight;` to `double mean = weightedTotal / totalWeight`. – Eric J. Jul 22 '15 at 16:56
  • Ulugbek Umirov: that prints: Value 1.0000000 – user650261 Jul 22 '15 at 17:00
  • 1
    What happens if you use a tolerance. I.E. (P.Value - 1.01) > 0.0001 – thinklarge Jul 22 '15 at 17:09
  • Are `Value` and `Weight` simple properties? Do you have any logic inside `get`? – Ulugbek Umirov Jul 22 '15 at 17:11
  • Looks like standard floating point comparison issues. When you compare a floating point to some value, you need a tolerance because float points are approximations of real numbers. If the value stored in the floating point number is not a power of two, then the value has to be approximated. This means that in floating point comparisons, sometimes `0.99999 == 1.0` and sometimes `0.99999 != 1.0`. That's why you need to do a floating point comparison and specify a tolerance. See this question: http://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp – StarPilot Jul 22 '15 at 17:18
  • This is not a tolerance issue if the output really is 1.0000000. – Eric J. Jul 22 '15 at 17:58
  • 1
    Can you modify your posted code into a complete example that demonstrates the issue? Otherwise, I doubt you will get a meaningful answer. – Eric J. Jul 22 '15 at 18:01
  • Ulugbek: they are simple properties: public double Weight { get { return weight; } set { weight = value; } } public double Value { get { return value; } } Eric: I am willing to provide whatever's useful. What would be useful code to you? I can, for instance, provide an instance of the bag. The bigger issue is that when I try to normalize this thing, this is leading to means greater than 1, which is causing problems with Mathdotnet Distributions. The hack is to clamp it to 1, but I shouldn't have to do that. – user650261 Jul 22 '15 at 18:11

1 Answers1

2

Okay, you guys are going to be mad, so let me start off by saying I'm sorry :-)

The problem was in fact a race condition, and had to do with a misunderstanding on my part as to how locks in C# work. I was locking on an object whose instance could change in different methods, in which the particle bag was changing. Replacing that with a dedicated lock object fixed my problems.

Sorry ^_^;;

user650261
  • 2,115
  • 5
  • 24
  • 47
  • LOL I actually thought about this as an answer but didn't say anything. As soon as I saw ConcurentBag alarm bells went off. I should have said something. Good job on finding the solution and post this as an answer to your own question. – thinklarge Jul 22 '15 at 20:22