Tuesday, October 18, 2011

Dealing with floating points in models - Part II

Today I’m going to follow up on Part I, but I’ll give you the implementation details of the classes. I provided you with a generic interface:
IDomainVariableSampler<T>

Now as I pointed out previously, we can choose to implement this interface for any domain type, so let’s try implementing it for a double domain:
    public class DomainDoubleSampler : IDomainVariableSampler<double>
    {
        public double Maximum { get { return 100.0; } }
        public double Minimum { get { return -100.0; } }

        public double BoundaryNegative(double boundary)
        {
            return boundary - double.Epsilon;
        }

        public double BoundaryPositive(double boundary)
        {
            return boundary + double.Epsilon;
        }

        public double Sample(double lowerBound, double upperBound)
        {
            Random rand = new Random(1);
            return lowerBound + rand.NextDouble() * (upperBound - lowerBound);
        }
    }

This implementation defines an input range of [-100.0, 100.0] and is sampling at random inside partitions of this interval.

Now we get to the tricky part of this post – we need somehow to cast this into something that is indexable. I touched on the DomainInput<T,X> class, which we will explore in detail:

    public class DomainInput<T,X> : SequenceContainer<T>
        where X : IDomainVariableSampler<T>, new()
        where T : IComparable<T>

Yes, I know, double generics, but unfortunately this is needed when we use the generic IDomainVariableSampler interface. The way this works is that T defines the domain type and X is a class type that implements IDomainVariableSampler<T> - in other words X is a class implementing a sampler for the domain T. Say we have our class DomainDoubleSampler that implements IDomainVariableSampler<double>, instantiation of a DomainInput for this domain type looks like this:

var inputMap = new DomainInput<double, DomainDoubleSampler>()

Now I’m going to jump back to some of the implementation details in the DomainInput indexer class. The following methods help us instantiate our input domain:

    public class DomainInput<T,X> : SequenceContainer<T>
        where X : IDomainVariableSampler<T>, new()
        where T : IComparable<T>
    {
        ...
        public static DomainInput<T,X> Empty { get { return new DomainInput<T,X>(); } }

        private DomainInput()
        {
            X sampler = new X();

            Add(sampler.Minimum);
            Add(sampler.Maximum);
        }

        private DomainInput(DomainInput<T,X> clone)
        {
            AddRange(clone);
        }

        public DomainInput<T, X> Boundary(T b)
        {
            DomainInput<T,X> di = new DomainInput<T,X>(this);
            di.Add(b);
            //di.Add(b.BoundaryNegative()); // Use when Spec Explorer bug is fixed
            //di.Add(b.BoundaryPositive());
            di.Sort();
            return di;
        }
        ...

Notice that constructors are defined private, and the class is initially instantiated using the Empty property. The domain partitioning is then done using Boundary(T b) which returns a new instance of DomainInput. This allows the following constructor pattern:

DomainInput<T, X>.Empty.Boundary(B1).Boundary(B2).Boundary(B3);

The boundary values are automatically sorted, so they can be specified in any order. Notice that the Empty property automatically sets up boundary values for domain min and max ranges.

Next up, how do we use this class in our models? Well the implementation is a bit technical, but stay with me:

    public class DomainInput<T,X> : SequenceContainer<T>
        where X : IDomainVariableSampler<T>, new()
        where T : IComparable<T>
    {
        ...
        public int NumberOfInputs { get { return 2 * Count - 1; } }

        public bool ValidIndex(int index)
        {
            return index >= 0 && index < NumberOfInputs;
        }

        public T Input(int index)
        {
            Condition.IsTrue(ValidIndex(index));

            if (index % 2 == 0)
            {
                // Boundary cases: Return boundary value
                return this.ElementAt(index / 2);
            }
            else
            {
                // Domain cases: Sample domain
                T lowerBound = this.ElementAt((index - 1) / 2);
                T upperBound = this.ElementAt((index - 1) / 2 + 1);
                X sampler = new X();
                return sampler.Sample(lowerBound, upperBound);
            }
        }
        ...

First, notice that it derives from a SequenceContainer. So our DomainInput behaves like an immutable container, so you can enumerate elements just like you would a normal container. However, it has the special Input method, which will return either a boundary value (if index is even) or sample from a domain partition between two neighboring boundary values (if index is odd) – sampling domain partitions is done through the IDomainVariableSampler<T> interface, so this is the reason we had to implement this interface in the first place!

As I previously pointed out, you can define any sampling strategy through your custom implementation.

If you feel like it would be fun to implement this on your own custom boundary, please go ahead and send me your implementation (you can reach me at ‘simon’ dot ‘ejsing’ at ‘microsoft’ dot ‘com‘), and I’ll share it through this blog. Some ideas for input domains: Strings of length 1 to 5, complex numbers, email addresses, URL’s, etc. 

No comments:

Post a Comment