/// <summary>
        /// Initializes a new instance of the <see cref="FeatureExpression"/> class.
        /// </summary>
        /// <param name="featureType">The type of the feature.</param>
        /// <param name="name">The name of the feature.</param>
        /// <param name="valueExpressionFactory">Factory to extract the value for a given feature from the example object (input argument).</param>
        /// <param name="valueValidExpressionFactories">Factories to provide validation before invoking the expression created through <see cref="ValueExpressionFactory"/>.</param>
        /// <param name="featureExpressionFactory">The expression must create new Feature instances.</param>
        /// <param name="namespace">The namespace this feature belongs to.</param>
        /// <param name="featureGroup">The feature group this feature belongs to.</param>
        /// <param name="enumerize">If true the marshaller enumerates the feature (as in creates a 1-hot encoding).</param>
        /// <param name="variableName">The variable name to be used in the generated code.</param>
        /// <param name="order">Used to order feature serialization.</param>
        /// <param name="addAnchor">True if an anchor element should be added at the beginning of a dense feature array.</param>
        /// <param name="stringProcessing">Configures string pre-processing for this feature.</param>
        /// <param name="overrideSerializeMethod">An optional method overriding the otherwise auto-resolved serialization method.</param>
        /// <param name="dictify">True if a dictionary should be build for this feature.</param>
        /// <param name="parent">The parent feature expression.</param>
        public FeatureExpression(Type featureType,
            string name,
            Func<Expression, Expression> valueExpressionFactory,
            List<Func<Expression, Expression>> valueValidExpressionFactories = null,
            NewFeatureExpressionDelegate featureExpressionFactory = null,
            string @namespace = null,
            char? featureGroup = null,
            bool enumerize = false,
            string variableName = null,
            int? order = null,
            bool addAnchor = false,
            StringProcessing stringProcessing = StringProcessing.Split,
            MethodInfo overrideSerializeMethod = null,
            bool? dictify = null,
            FeatureExpression parent = null)
        {
            if (featureType == null)
                throw new ArgumentNullException("featureType");

            if (valueExpressionFactory == null)
                throw new ArgumentNullException("valueExpressionFactory");

            Contract.EndContractBlock();

            if(featureType.IsGenericType &&
               featureType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                this.IsNullable = true;
                this.FeatureType = featureType.GetGenericArguments()[0];
            }
            else
            {
                this.IsNullable = false;
                this.FeatureType = featureType;
            }

            this.Name = name;
            this.ValueExpressionFactory = valueExpressionFactory;
            this.ValueValidExpressionFactories = valueValidExpressionFactories;
            this.FeatureExpressionFactory = featureExpressionFactory;
            this.Namespace = @namespace;
            this.FeatureGroup = featureGroup;
            this.Enumerize = enumerize;
            this.VariableName = variableName ?? name;
            this.Order = order ?? 1;
            this.AddAnchor = addAnchor;
            this.Dictify = dictify ?? false;
            this.StringProcessing = stringProcessing;
            this.OverrideSerializeMethod = overrideSerializeMethod;
            this.Dictify = dictify ?? false;
            this.Parent = parent;

            this.DenseFeatureValueElementType = InspectionHelper.GetEnumerableElementType(featureType);

            if (!InspectionHelper.IsNumericType(this.DenseFeatureValueElementType))
                this.DenseFeatureValueElementType = null;
        }
Example #2
0
        /// <summary>
        /// Instantiate the meta information object such as <see cref="PreHashedFeature"/>
        /// for a given feature.
        /// </summary>
        /// <param name="featureInternal">The feature.</param>
        /// <param name="namespace">The namespace.</param>
        /// <returns>The "new" expression for the meta information object.</returns>
        private Expression CreateFeature(FeatureExpressionInternal featureInternal, Expression @namespace)
        {
            FeatureExpression feature = featureInternal.Source;
            var metaFeatureType       = featureInternal.MarshalMethod.MetaFeatureType;

            if (metaFeatureType.IsGenericType && metaFeatureType.GetGenericTypeDefinition() == typeof(EnumerizedFeature <>))
            {
                // preemptively calculate all hashes for each enum value
                var featureParameter = Expression.Parameter(metaFeatureType);
                var valueParameter   = Expression.Parameter(feature.FeatureType);

                var body = new List <Expression>();

                var hashVariables = new List <ParameterExpression>();
                foreach (var value in Enum.GetValues(feature.FeatureType))
                {
                    var hashVar = Expression.Variable(typeof(UInt64));
                    hashVariables.Add(hashVar);

                    // CODE hashVar = feature.FeatureHashInternal(value);
                    body.Add(Expression.Assign(hashVar,
                                               Expression.Call(featureParameter,
                                                               metaFeatureType.GetMethod("FeatureHashInternal"),
                                                               Expression.Constant(value))));
                }

                var cases = Enum.GetValues(feature.FeatureType)
                            .Cast <object>()
                            .Zip(hashVariables, (value, hash) => Expression.SwitchCase(
                                     hash,
                                     Expression.Constant(value, feature.FeatureType)))
                            .ToArray();

                // expand the switch(value) { case enum1: return hash1; .... }
                var hashSwitch = Expression.Switch(valueParameter,
                                                   Expression.Block(Expression.Throw(Expression.New(typeof(NotSupportedException))), Expression.Constant((UInt64)0, typeof(UInt64))),
                                                   cases);

                // CODE return value => switch(value) { .... }
                body.Add(Expression.Lambda(hashSwitch, valueParameter));

                return(CreateNew(
                           metaFeatureType,
                           this.vwParameter,
                           @namespace,
                           Expression.Constant(feature.Name, typeof(string)),
                           Expression.Constant(feature.AddAnchor),
                           Expression.Constant(feature.Dictify),
                           Expression.Lambda(Expression.Block(hashVariables, body), featureParameter)));
            }
            else if (metaFeatureType == typeof(PreHashedFeature))
            {
                // CODE new PreHashedFeature(vw, namespace, "Name", "AddAnchor");
                return(CreateNew(
                           typeof(PreHashedFeature),
                           this.vwParameter,
                           @namespace,
                           Expression.Constant(feature.Name, typeof(string)),
                           Expression.Constant(feature.AddAnchor),
                           Expression.Constant(feature.Dictify)));
            }
            else
            {
                // CODE new Feature("Name", ...)
                return(CreateNew(
                           metaFeatureType,
                           Expression.Constant(feature.Name, typeof(string)),
                           Expression.Constant(feature.AddAnchor),
                           Expression.Constant(feature.Dictify)));
            }
        }
Example #3
0
        private MarshalMethod ResolveFeatureMarshalMethod(FeatureExpression feature)
        {
            if (feature.OverrideSerializeMethod != null)
            {
                return(new MarshalMethod
                {
                    Method = feature.OverrideSerializeMethod,
                    MetaFeatureType = feature.OverrideSerializeMethod.GetParameters().Select(p => p.ParameterType).First(t => typeof(Feature).IsAssignableFrom(t))
                });
            }

            string methodName;

            Type[] metaFeatureTypeCandidates;

            if (feature.FeatureType == typeof(string))
            {
                switch (feature.StringProcessing)
                {
                case StringProcessing.Escape:
                    methodName = "MarshalFeatureStringEscape";
                    break;

                case StringProcessing.EscapeAndIncludeName:
                    methodName = "MarshalFeatureStringEscapeAndIncludeName";
                    break;

                case StringProcessing.Split:
                    methodName = "MarshalFeatureStringSplit";
                    break;

                default:
                    throw new ArgumentException("feature.StringProcessing is not supported: " + feature.StringProcessing);
                }
                metaFeatureTypeCandidates = new [] { typeof(Feature) };
            }
            else if (feature.FeatureType.IsEnum)
            {
                methodName = "MarshalEnumFeature";
                metaFeatureTypeCandidates = new [] { typeof(EnumerizedFeature <>).MakeGenericType(feature.FeatureType) };
            }
            else if (feature.Enumerize)
            {
                methodName = "MarshalEnumerizeFeature";
                metaFeatureTypeCandidates = new [] { typeof(Feature) };
            }
            else
            {
                // probe for PreHashedFeature marshal method, than fallback
                methodName = "MarshalFeature";
                metaFeatureTypeCandidates = new [] { typeof(PreHashedFeature), typeof(Feature) };
            }

            // remove Nullable<> from feature type
            var featureType = feature.FeatureType;

            if (featureType.IsGenericType &&
                featureType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                featureType = featureType.GetGenericArguments()[0];
            }

            foreach (var metaFeatureType in metaFeatureTypeCandidates)
            {
                // find visitor.MarshalFeature(VowpalWabbitMarshallingContext context, Namespace ns, <PreHashedFeature|Feature> feature, <valueType> value)
                var method = this.marshallerTypes
                             .Select(visitor => ReflectionHelper.FindMethod(
                                         visitor,
                                         methodName,
                                         typeof(VowpalWabbitMarshalContext),
                                         typeof(Namespace),
                                         metaFeatureType,
                                         featureType))
                             .FirstOrDefault(m => m != null);

                if (method != null)
                {
                    return(new MarshalMethod
                    {
                        Method = method,
                        MetaFeatureType = metaFeatureType
                    });
                }
            }

            return(null);
        }
Example #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FeatureExpression"/> class.
        /// </summary>
        /// <param name="featureType">The type of the feature.</param>
        /// <param name="name">The name of the feature.</param>
        /// <param name="valueExpressionFactory">Factory to extract the value for a given feature from the example object (input argument).</param>
        /// <param name="valueValidExpressionFactories">Factories to provide validation before invoking the expression created through <see cref="ValueExpressionFactory"/>.</param>
        /// <param name="featureExpressionFactory">The expression must create new Feature instances.</param>
        /// <param name="namespace">The namespace this feature belongs to.</param>
        /// <param name="featureGroup">The feature group this feature belongs to.</param>
        /// <param name="enumerize">If true the marshaller enumerates the feature (as in creates a 1-hot encoding).</param>
        /// <param name="variableName">The variable name to be used in the generated code.</param>
        /// <param name="order">Used to order feature serialization.</param>
        /// <param name="addAnchor">True if an anchor element should be added at the beginning of a dense feature array.</param>
        /// <param name="stringProcessing">Configures string pre-processing for this feature.</param>
        /// <param name="overrideSerializeMethod">An optional method overriding the otherwise auto-resolved serialization method.</param>
        /// <param name="dictify">True if a dictionary should be build for this feature.</param>
        /// <param name="parent">The parent feature expression.</param>
        public FeatureExpression(Type featureType,
                                 string name,
                                 Func <Expression, Expression> valueExpressionFactory,
                                 List <Func <Expression, Expression> > valueValidExpressionFactories = null,
                                 NewFeatureExpressionDelegate featureExpressionFactory = null,
                                 string @namespace   = null,
                                 char?featureGroup   = null,
                                 bool enumerize      = false,
                                 string variableName = null,
                                 int?order           = null,
                                 bool addAnchor      = false,
                                 StringProcessing stringProcessing  = StringProcessing.Split,
                                 MethodInfo overrideSerializeMethod = null,
                                 bool?dictify             = null,
                                 FeatureExpression parent = null)
        {
            if (featureType == null)
            {
                throw new ArgumentNullException("featureType");
            }

            if (valueExpressionFactory == null)
            {
                throw new ArgumentNullException("valueExpressionFactory");
            }

            Contract.EndContractBlock();

            if (featureType.IsGenericType &&
                featureType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                this.IsNullable  = true;
                this.FeatureType = featureType.GetGenericArguments()[0];
            }
            else
            {
                this.IsNullable  = false;
                this.FeatureType = featureType;
            }

            this.Name = name;
            this.ValueExpressionFactory        = valueExpressionFactory;
            this.ValueValidExpressionFactories = valueValidExpressionFactories;
            this.FeatureExpressionFactory      = featureExpressionFactory;
            this.Namespace               = @namespace;
            this.FeatureGroup            = featureGroup;
            this.Enumerize               = enumerize;
            this.VariableName            = variableName ?? name;
            this.Order                   = order ?? 1;
            this.AddAnchor               = addAnchor;
            this.Dictify                 = dictify ?? false;
            this.StringProcessing        = stringProcessing;
            this.OverrideSerializeMethod = overrideSerializeMethod;
            this.Dictify                 = dictify ?? false;
            this.Parent                  = parent;

            this.DenseFeatureValueElementType = InspectionHelper.GetEnumerableElementType(featureType);

            if (!InspectionHelper.IsNumericType(this.DenseFeatureValueElementType))
            {
                this.DenseFeatureValueElementType = null;
            }
        }
Example #5
0
            public VowpalWabbitMultiExampleSerializerCompilerImpl(VowpalWabbitSettings settings, Schema schema, FeatureExpression multiFeature)
            {
                Contract.Requires(settings != null);
                Contract.Requires(schema != null);
                Contract.Requires(multiFeature != null);

                var nonMultiFeatures = schema.Features.Where(fe => fe != multiFeature).ToList();

                this.sharedSerializerCompiler = nonMultiFeatures.Count == 0 ? null :
                                                new VowpalWabbitSingleExampleSerializerCompiler <TExample>(
                    new Schema {
                    Features = nonMultiFeatures
                },
                    settings == null ? null : settings.CustomFeaturizer,
                    !settings.EnableStringExampleGeneration);

                this.adfSerializerComputer = new VowpalWabbitSingleExampleSerializerCompiler <TActionDependentFeature>(
                    settings.TypeInspector.CreateSchema(settings, typeof(TActionDependentFeature)),
                    settings == null ? null : settings.CustomFeaturizer,
                    !settings.EnableStringExampleGeneration);

                var exampleParameter = Expression.Parameter(typeof(TExample), "example");

                // CODE condition1 && condition2 && condition3 ...
                var condition = multiFeature.ValueValidExpressionFactories
                                .Skip(1)
                                .Aggregate(
                    multiFeature.ValueValidExpressionFactories.First()(exampleParameter),
                    (cond, factory) => Expression.AndAlso(cond, factory(exampleParameter)));

                var multiExpression = multiFeature.ValueExpressionFactory(exampleParameter);

                // CODE example => (IEnumerable<TActionDependentFeature>)(example._multi != null ? example._multi : null)
                var expr = Expression.Lambda <Func <TExample, IEnumerable <TActionDependentFeature> > >(
                    Expression.Condition(
                        condition,
                        multiExpression,
                        Expression.Constant(null, multiExpression.Type),
                        typeof(IEnumerable <TActionDependentFeature>)),
                    exampleParameter);

                this.adfAccessor = (Func <TExample, IEnumerable <TActionDependentFeature> >)expr.CompileToFunc();
            }
        private static Schema CreateSchema(
            FeatureExpression parent,
            Type type,
            string parentNamespace,
            char?parentFeatureGroup,
            bool?parentDictify,
            Func <Expression, Expression> valueExpressionFactory,
            Stack <Func <Expression, Expression> > valueValidExpressionFactories,
            Func <PropertyInfo, FeatureAttribute, bool> featurePropertyPredicate,
            Func <PropertyInfo, LabelAttribute, bool> labelPropertyPredicate)
        {
            var props = type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public);

            var localFeatures = (from p in props
                                 let declaredAttr = (FeatureAttribute)p.GetCustomAttributes(typeof(FeatureAttribute), true).FirstOrDefault()
                                                    where featurePropertyPredicate(p, declaredAttr)
                                                    let attr = declaredAttr ?? new FeatureAttribute()
                                                               select new FeatureExpression(
                                     featureType: p.PropertyType,
                                     name: attr.Name ?? p.Name,
                                     // CODE example.Property
                                     valueExpressionFactory: valueExpression => Expression.Property(valueExpressionFactory(valueExpression), p),
                                     // @Reverse: make sure conditions are specified in the right order
                                     valueValidExpressionFactories: valueValidExpressionFactories.Reverse().ToList(),
                                     @namespace: attr.Namespace ?? parentNamespace,
                                     featureGroup: attr.InternalFeatureGroup ?? parentFeatureGroup,
                                     enumerize: attr.Enumerize,
                                     variableName: p.Name,
                                     order: attr.Order,
                                     addAnchor: attr.AddAnchor,
                                     stringProcessing: attr.StringProcessing,
                                     dictify: attr.InternalDictify ?? parentDictify,
                                     parent: parent)
                                 ).ToList();

            var localLabels = from p in props
                              let declaredAttr = (LabelAttribute)p.GetCustomAttributes(typeof(LabelAttribute), true).FirstOrDefault()
                                                 where labelPropertyPredicate(p, declaredAttr) || typeof(ILabel).IsAssignableFrom(p.PropertyType)
                                                 let attr = declaredAttr ?? new LabelAttribute()
                                                            let labelType = p.PropertyType
                                                                            where typeof(ILabel).IsAssignableFrom(labelType) || p.PropertyType == typeof(string)
                                                                            select new LabelExpression
            {
                Name      = p.Name,
                LabelType = p.PropertyType,
                // CODE example.Property
                ValueExpressionFactory = valueExpression => Expression.Property(valueExpressionFactory(valueExpression), p),
                // @Reverse: make sure conditions are specified in the right order
                ValueValidExpressionFactories = valueValidExpressionFactories.Reverse().ToList()
            };

            // Recurse
            var schemas = localFeatures
                          .Select(f =>
            {
                // CODE example.Prop1.Prop2 != null
                valueValidExpressionFactories.Push(valueExpression => Expression.NotEqual(f.ValueExpressionFactory(valueExpression), Expression.Constant(null)));
                var subSchema = CreateSchema(f, f.FeatureType, f.Namespace, f.FeatureGroup, f.Dictify, f.ValueExpressionFactory, valueValidExpressionFactories, featurePropertyPredicate, labelPropertyPredicate);
                valueValidExpressionFactories.Pop();

                return(subSchema);
            })
                          .ToList();

            return(new Schema
            {
                Features = localFeatures.Union(schemas.SelectMany(s => s.Features)).ToList(),
                Label = localLabels.Union(schemas.Select(s => s.Label)).FirstOrDefault(l => l != null)
            });
        }
        private MarshalMethod ResolveFeatureMarshalMethod(FeatureExpression feature)
        {
            if (feature.OverrideSerializeMethod != null)
            {
                return(new MarshalMethod
                {
                    Method = feature.OverrideSerializeMethod,
                    MetaFeatureType = feature.OverrideSerializeMethod.GetParameters().Select(p => p.ParameterType).First(t => typeof(Feature).IsAssignableFrom(t))
                });
            }

            string methodName;

            Type[] metaFeatureTypeCandidates;

            if (feature.FeatureType == typeof(string))
            {
                switch (feature.StringProcessing)
                {
                case StringProcessing.Escape:
                    methodName = "MarshalFeatureStringEscape";
                    break;

                case StringProcessing.EscapeAndIncludeName:
                    methodName = "MarshalFeatureStringEscapeAndIncludeName";
                    break;

                case StringProcessing.Split:
                    methodName = "MarshalFeatureStringSplit";
                    break;

                default:
                    throw new ArgumentException("feature.StringProcessing is not supported: " + feature.StringProcessing);
                }
                metaFeatureTypeCandidates = new [] { typeof(Feature) };
            }
            else if (feature.FeatureType.IsEnum)
            {
                methodName = "MarshalEnumFeature";
                metaFeatureTypeCandidates = new [] { typeof(EnumerizedFeature <>).MakeGenericType(feature.FeatureType) };
            }
            else if (feature.Enumerize)
            {
                methodName = "MarshalEnumerizeFeature";
                metaFeatureTypeCandidates = new [] { typeof(Feature) };
            }
            else
            {
                // probe for PreHashedFeature marshal method, than fallback
                methodName = "MarshalFeature";
                metaFeatureTypeCandidates = new [] { typeof(PreHashedFeature), typeof(Feature) };
            }

            // remove Nullable<> from feature type
            var featureType = feature.FeatureType;

            if (featureType.IsGenericType &&
                featureType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                featureType = featureType.GetGenericArguments()[0];
            }

            var method = ResolveFeatureMarshalMethod("MarshalNamespace", metaFeatureTypeCandidates, featureType, isNamespace: true);

            if (method == null)
            {
                method = ResolveFeatureMarshalMethod(methodName, metaFeatureTypeCandidates, featureType, isNamespace: false);
            }

            return(method);
        }