lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cpoersc...@apache.org
Subject [3/3] lucene-solr:jira/solr-8542-v2: Generic models for ranking (#174)
Date Wed, 19 Oct 2016 02:18:20 GMT
Generic models for ranking (#174)

This is to make the models more generic to handle all forms of two types of models. One type of model is the linear model. Previously, we focused on only the RankSVM, but this version is generic with any linear model including Pranking, a form of ordinal regression. The other type of model is a generic multiple additive trees model. Previosuly, we focused only on LambdaMART, but this version is generic with any form of additive trees, which also includes gradient boosted regression trees (GBRT) models.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/bfa05b83
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/bfa05b83
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/bfa05b83

Branch: refs/heads/jira/solr-8542-v2
Commit: bfa05b830dce07837f675745bfbd07814fe4794b
Parents: f2a8e8a
Author: jdorando <jondorando@gmail.com>
Authored: Tue Oct 18 10:08:24 2016 -0400
Committer: mnilsson23 <mnilsson23@bloomberg.net>
Committed: Tue Oct 18 10:17:12 2016 -0400

----------------------------------------------------------------------
 solr/contrib/ltr/README.md                      |  58 ++-
 solr/contrib/ltr/example/libsvm_formatter.py    |   2 +-
 .../contrib/ltr/example/techproducts-model.json |   4 +-
 .../apache/solr/ltr/model/LambdaMARTModel.java  | 369 ------------------
 .../org/apache/solr/ltr/model/LinearModel.java  | 145 +++++++
 .../ltr/model/MultipleAdditiveTreesModel.java   | 375 +++++++++++++++++++
 .../org/apache/solr/ltr/model/RankSVMModel.java | 139 -------
 .../java/org/apache/solr/ltr/package-info.java  |   2 +-
 solr/contrib/ltr/src/java/overview.html         |   2 +-
 .../featureExamples/features-linear-efi.json    |  17 +
 .../featureExamples/features-linear.json        |  51 +++
 .../featureExamples/features-ranksvm-efi.json   |  17 -
 .../featureExamples/features-ranksvm.json       |  51 ---
 .../featureExamples/lambdamart_features.json    |  16 -
 .../multipleadditivetreesmodel_features.json    |  16 +
 .../modelExamples/external_model.json           |   2 +-
 .../modelExamples/external_model_store.json     |   2 +-
 .../src/test-files/modelExamples/fq-model.json  |   2 +-
 .../modelExamples/lambdamartmodel.json          |  38 --
 ...ambdamartmodel_external_binary_features.json |  38 --
 .../lambdamartmodel_no_feature.json             |  24 --
 .../lambdamartmodel_no_features.json            |  14 -
 .../modelExamples/lambdamartmodel_no_left.json  |  22 --
 .../lambdamartmodel_no_params.json              |   8 -
 .../modelExamples/lambdamartmodel_no_right.json |  22 --
 .../lambdamartmodel_no_threshold.json           |  24 --
 .../modelExamples/lambdamartmodel_no_tree.json  |  15 -
 .../modelExamples/lambdamartmodel_no_trees.json |  10 -
 .../lambdamartmodel_no_weight.json              |  24 --
 .../modelExamples/linear-model-efi.json         |  14 +
 .../test-files/modelExamples/linear-model.json  |  30 ++
 .../multipleadditivetreesmodel.json             |  38 ++
 ...tivetreesmodel_external_binary_features.json |  38 ++
 .../multipleadditivetreesmodel_no_feature.json  |  24 ++
 .../multipleadditivetreesmodel_no_features.json |  14 +
 .../multipleadditivetreesmodel_no_left.json     |  22 ++
 .../multipleadditivetreesmodel_no_params.json   |   8 +
 .../multipleadditivetreesmodel_no_right.json    |  22 ++
 ...multipleadditivetreesmodel_no_threshold.json |  24 ++
 .../multipleadditivetreesmodel_no_tree.json     |  15 +
 .../multipleadditivetreesmodel_no_trees.json    |  10 +
 .../multipleadditivetreesmodel_no_weight.json   |  24 ++
 .../test-files/modelExamples/ranksvm-model.json |  30 --
 .../test-files/modelExamples/svm-model-efi.json |  14 -
 .../src/test-files/modelExamples/svm-model.json |  20 -
 .../org/apache/solr/ltr/TestLTROnSolrCloud.java |   4 +-
 .../apache/solr/ltr/TestLTRQParserExplain.java  |  36 +-
 .../apache/solr/ltr/TestLTRQParserPlugin.java   |   4 +-
 .../solr/ltr/TestLTRReRankingPipeline.java      |   6 +-
 .../apache/solr/ltr/TestLTRScoringQuery.java    |  16 +-
 .../org/apache/solr/ltr/TestLTRWithFacet.java   |   4 +-
 .../org/apache/solr/ltr/TestLTRWithSort.java    |   4 +-
 .../org/apache/solr/ltr/TestRerankBase.java     |   4 +-
 .../solr/ltr/TestSelectiveWeightCreation.java   |   6 +-
 .../ltr/feature/TestEdisMaxSolrFeature.java     |   4 +-
 .../ltr/feature/TestExternalValueFeatures.java  |   2 +-
 .../solr/ltr/feature/TestFeatureLogging.java    |  10 +-
 .../ltr/feature/TestFieldLengthFeature.java     |  10 +-
 .../solr/ltr/feature/TestFieldValueFeature.java |   8 +-
 .../solr/ltr/feature/TestFilterSolrFeature.java |   4 +-
 .../ltr/feature/TestNoMatchSolrFeature.java     |  14 +-
 .../ltr/feature/TestOriginalScoreFeature.java   |   6 +-
 .../solr/ltr/feature/TestRankingFeature.java    |   8 +-
 .../ltr/feature/TestUserTermScoreWithQ.java     |   4 +-
 .../ltr/feature/TestUserTermScorerQuery.java    |   4 +-
 .../ltr/feature/TestUserTermScorereQDF.java     |   4 +-
 .../solr/ltr/feature/TestValueFeature.java      |  12 +-
 .../solr/ltr/model/TestLambdaMARTModel.java     | 249 ------------
 .../apache/solr/ltr/model/TestLinearModel.java  | 211 +++++++++++
 .../model/TestMultipleAdditiveTreesModel.java   | 249 ++++++++++++
 .../apache/solr/ltr/model/TestRankSVMModel.java | 211 -----------
 .../solr/ltr/store/rest/TestModelManager.java   |  18 +-
 .../store/rest/TestModelManagerPersistence.java |   6 +-
 73 files changed, 1482 insertions(+), 1492 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/README.md
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/README.md b/solr/contrib/ltr/README.md
index ada1d5b..5fe0087 100644
--- a/solr/contrib/ltr/README.md
+++ b/solr/contrib/ltr/README.md
@@ -33,7 +33,7 @@ the techproducts example please follow these steps.
         `mkdir example/techproducts/solr/techproducts/lib`
      3. Install the plugin in the lib folder
 
-        `cp build/contrib/ltr/lucene-ltr-7.0.0-SNAPSHOT.jar example/techproducts/solr/techproducts/lib/`
+        `cp build/contrib/ltr/solr-ltr-7.0.0-SNAPSHOT.jar example/techproducts/solr/techproducts/lib/`
      4. Replace the original solrconfig with one importing all the ltr components
 
         `cp contrib/ltr/example/solrconfig.xml example/techproducts/solr/techproducts/conf/`
@@ -61,7 +61,7 @@ the techproducts example please follow these steps.
        http://localhost:8983/solr/techproducts/schema/model-store
      * Perform a reranking query using the model, and retrieve the features
 
-       http://localhost:8983/solr/techproducts/query?indent=on&q=test&wt=json&rq={!ltr%20model=svm%20reRankDocs=25%20efi.user_query=%27test%27}&fl=[features],price,score,name
+       http://localhost:8983/solr/techproducts/query?indent=on&q=test&wt=json&rq={!ltr%20model=linear%20reRankDocs=25%20efi.user_query=%27test%27}&fl=[features],price,score,name
 
 
 BONUS: Train an actual machine learning model
@@ -78,7 +78,7 @@ BONUS: Train an actual machine learning model
 
    This script deploys your features from `config.json` "featuresFile" to Solr.  Then it takes the relevance judged query
    document pairs of "userQueriesFile" and merges it with the features extracted from Solr into a training
-   file.  That file is used to train a rankSVM model, which is then deployed to Solr for you to rerank results.
+   file.  That file is used to train a linear model, which is then deployed to Solr for you to rerank results.
 
 4. Search and rerank the results using the trained model
 
@@ -153,7 +153,7 @@ using standard Solr queries. As an example:
 ]
 ```
 
-Defines four features. Anything that is a valid Solr query can be used to define
+Defines five features. Anything that is a valid Solr query can be used to define
 a feature.
 
 ### Filter Query Features
@@ -198,19 +198,18 @@ The majority of features should be possible to create using the methods describe
 above.
 
 # Defining Models
-Currently the Learning to Rank plugin supports 2 main types of
-ranking models: [Ranking SVM](http://www.cs.cornell.edu/people/tj/publications/joachims_02c.pdf)
-and [LambdaMART](http://research.microsoft.com/pubs/132652/MSR-TR-2010-82.pdf)
+Currently the Learning to Rank plugin supports 2 generalized forms of
+models: 1. Linear Model i.e. [RankSVM](http://www.cs.cornell.edu/people/tj/publications/joachims_02c.pdf), [Pranking](https://papers.nips.cc/paper/2023-pranking-with-ranking.pdf)
+and 2. Multiple Additive Trees i.e. [LambdaMART](http://research.microsoft.com/pubs/132652/MSR-TR-2010-82.pdf), [Gradient Boosted Regression Trees (GBRT)](https://papers.nips.cc/paper/3305-a-general-boosting-method-and-its-application-to-learning-ranking-functions-for-web-search.pdf)
 
-### Ranking SVM
-Currently only a linear ranking svm is supported. Use LambdaMART for
-a non-linear model. If you'd like to introduce a bias set a constant feature
+### Linear
+If you'd like to introduce a bias set a constant feature
 to the bias value you'd like and make a weight of 1.0 for that feature.
 
 ###### model.json
 ```json
 {
-    "class":"org.apache.solr.ltr.model.RankSVMModel",
+    "class":"org.apache.solr.ltr.model.LinearModel",
     "name":"myModelName",
     "features":[
         { "name": "userTextTitleMatch"},
@@ -228,27 +227,26 @@ to the bias value you'd like and make a weight of 1.0 for that feature.
 }
 ```
 
-This is an example of a toy Ranking SVM model. Type specifies the class to be
-using to interpret the model (RankSVMModel in the case of Ranking SVM).
-Name is the model identifier you will use when making request to the ltr
-framework. Features specifies the feature space that you want extracted
-when using this model. All features that appear in the model params will
-be used for scoring and must appear in the features list.  You can add
-extra features to the features list that will be computed but not used in the
-model for scoring, which can be useful for logging.
-Params are the Ranking SVM parameters.
+This is an example of a toy Linear model. Class specifies the class to be
+using to interpret the model. Name is the model identifier you will use 
+when making request to the ltr framework. Features specifies the feature 
+space that you want extracted when using this model. All features that 
+appear in the model params will be used for scoring and must appear in 
+the features list.  You can add extra features to the features list that 
+will be computed but not used in the model for scoring, which can be useful 
+for logging. Params are the Linear parameters.
 
-Good library for training SVM's (https://www.csie.ntu.edu.tw/~cjlin/liblinear/ ,
-https://www.csie.ntu.edu.tw/~cjlin/libsvm/) . You will need to convert the
-libSVM model format to the format specified above.
+Good library for training SVM, an example of a Linear model, is 
+(https://www.csie.ntu.edu.tw/~cjlin/liblinear/ , https://www.csie.ntu.edu.tw/~cjlin/libsvm/) . 
+You will need to convert the libSVM model format to the format specified above.
 
-### LambdaMART
+### Multiple Additive Trees
 
 ###### model2.json
 ```json
 {
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel",
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel",
     "features":[
         { "name": "userTextTitleMatch"},
         { "name": "originalScore"}
@@ -285,17 +283,17 @@ libSVM model format to the format specified above.
     }
 }
 ```
-This is an example of a toy LambdaMART. Type specifies the class to be using to
-interpret the model (LambdaMARTModel in the case of LambdaMART). Name is the
+This is an example of a toy Multiple Additive Trees. Class specifies the class to be using to
+interpret the model. Name is the
 model identifier you will use when making request to the ltr framework.
 Features specifies the feature space that you want extracted when using this
 model. All features that appear in the model params will be used for scoring and
 must appear in the features list.  You can add extra features to the features
 list that will be computed but not used in the model for scoring, which can
-be useful for logging. Params are the LambdaMART specific parameters. In this
+be useful for logging. Params are the Multiple Additive Trees specific parameters. In this
 case we have 2 trees, one with 3 leaf nodes and one with 1 leaf node.
 
-A good library for training LambdaMART ( http://sourceforge.net/p/lemur/wiki/RankLib/ ).
+A good library for training LambdaMART, an example of Multiple Additive Trees, is ( http://sourceforge.net/p/lemur/wiki/RankLib/ ).
 You will need to convert the RankLib model format to the format specified above.
 
 # Deploy Models and Features

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/example/libsvm_formatter.py
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/example/libsvm_formatter.py b/solr/contrib/ltr/example/libsvm_formatter.py
index 89ba12f..25cf10b 100644
--- a/solr/contrib/ltr/example/libsvm_formatter.py
+++ b/solr/contrib/ltr/example/libsvm_formatter.py
@@ -45,7 +45,7 @@ class LibSvmFormatter:
     def convertLibSvmModelToLtrModel(self,libSvmModelLocation, outputFile, modelName):
         with open(libSvmModelLocation, 'r') as inFile:
             with open(outputFile,'w') as convertedOutFile:
-                convertedOutFile.write('{\n\t"class":"org.apache.solr.ltr.model.RankSVMModel",\n')
+                convertedOutFile.write('{\n\t"class":"org.apache.solr.ltr.model.LinearModel",\n')
                 convertedOutFile.write('\t"name": "' + str(modelName) + '",\n')
                 convertedOutFile.write('\t"features": [\n')
                 isFirst = True;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/example/techproducts-model.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/example/techproducts-model.json b/solr/contrib/ltr/example/techproducts-model.json
index a1b58f1..0efded7 100644
--- a/solr/contrib/ltr/example/techproducts-model.json
+++ b/solr/contrib/ltr/example/techproducts-model.json
@@ -1,6 +1,6 @@
 {
-    "class":"org.apache.solr.ltr.model.RankSVMModel",
-    "name":"svm",
+    "class":"org.apache.solr.ltr.model.LinearModel",
+    "name":"linear",
     "features":[
     {"name":"isInStock"},
     {"name":"price"},

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LambdaMARTModel.java
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LambdaMARTModel.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LambdaMARTModel.java
deleted file mode 100644
index 23704d0..0000000
--- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LambdaMARTModel.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.ltr.model;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Explanation;
-import org.apache.solr.ltr.feature.Feature;
-import org.apache.solr.ltr.norm.Normalizer;
-import org.apache.solr.util.SolrPluginUtils;
-
-/**
- * A scoring model that computes scores based on the LambdaMART algorithm.
- * <p>
- * Example configuration:
-<pre>{
-   "class" : "org.apache.solr.ltr.model.LambdaMARTModel",
-   "name" : "lambdamartmodel",
-   "features":[
-       { "name" : "userTextTitleMatch"},
-       { "name" : "originalScore"}
-   ],
-   "params" : {
-       "trees" : [
-           {
-               "weight" : 1,
-               "root": {
-                   "feature" : "userTextTitleMatch",
-                   "threshold" : 0.5,
-                   "left" : {
-                       "value" : -100
-                   },
-                   "right" : {
-                       "feature" : "originalScore",
-                       "threshold" : 10.0,
-                       "left" : {
-                           "value" : 50
-                       },
-                       "right" : {
-                           "value" : 75
-                       }
-                   }
-               }
-           },
-           {
-               "weight" : 2,
-               "root" : {
-                   "value" : -10
-               }
-           }
-       ]
-   }
-}</pre>
- * <p>
- * Background reading:
- * <ul>
- * <li> <a href="http://research.microsoft.com/pubs/132652/MSR-TR-2010-82.pdf">
- * Christopher J.C. Burges. From RankNet to LambdaRank to LambdaMART: An Overview.
- * Microsoft Research Technical Report MSR-TR-2010-82.</a>
- * </ul>
- */
-public class LambdaMARTModel extends LTRScoringModel {
-
-  private final HashMap<String,Integer> fname2index;
-  private List<RegressionTree> trees;
-
-  private RegressionTree createRegressionTree(Map<String,Object> map) {
-    final RegressionTree rt = new RegressionTree();
-    if (map != null) {
-      SolrPluginUtils.invokeSetters(rt, map.entrySet());
-    }
-    return rt;
-  }
-
-  private RegressionTreeNode createRegressionTreeNode(Map<String,Object> map) {
-    final RegressionTreeNode rtn = new RegressionTreeNode();
-    if (map != null) {
-      SolrPluginUtils.invokeSetters(rtn, map.entrySet());
-    }
-    return rtn;
-  }
-
-  public class RegressionTreeNode {
-    private static final float NODE_SPLIT_SLACK = 1E-6f;
-
-    private float value = 0f;
-    private String feature;
-    private int featureIndex = -1;
-    private Float threshold;
-    private RegressionTreeNode left;
-    private RegressionTreeNode right;
-
-    public void setValue(float value) {
-      this.value = value;
-    }
-
-    public void setValue(String value) {
-      this.value = Float.parseFloat(value);
-    }
-
-    public void setFeature(String feature) {
-      this.feature = feature;
-      final Integer idx = fname2index.get(this.feature);
-      // this happens if the tree specifies a feature that does not exist
-      // this could be due to lambdaSmart building off of pre-existing trees
-      // that use a feature that is no longer output during feature extraction
-      featureIndex = (idx == null) ? -1 : idx;
-    }
-
-    public void setThreshold(float threshold) {
-      this.threshold = threshold + NODE_SPLIT_SLACK;
-    }
-
-    public void setThreshold(String threshold) {
-      this.threshold = Float.parseFloat(threshold) + NODE_SPLIT_SLACK;
-    }
-
-    public void setLeft(Object left) {
-      this.left = createRegressionTreeNode((Map<String,Object>) left);
-    }
-
-    public void setRight(Object right) {
-      this.right = createRegressionTreeNode((Map<String,Object>) right);
-    }
-
-    public boolean isLeaf() {
-      return feature == null;
-    }
-
-    public float score(float[] featureVector) {
-      if (isLeaf()) {
-        return value;
-      }
-
-      // unsupported feature (tree is looking for a feature that does not exist)
-      if  ((featureIndex < 0) || (featureIndex >= featureVector.length)) {
-        return 0f;
-      }
-
-      if (featureVector[featureIndex] <= threshold) {
-        return left.score(featureVector);
-      } else {
-        return right.score(featureVector);
-      }
-    }
-
-    public String explain(float[] featureVector) {
-      if (isLeaf()) {
-        return "val: " + value;
-      }
-
-      // unsupported feature (tree is looking for a feature that does not exist)
-      if  ((featureIndex < 0) || (featureIndex >= featureVector.length)) {
-        return  "'" + feature + "' does not exist in FV, Return Zero";
-      }
-
-      // could store extra information about how much training data supported
-      // each branch and report
-      // that here
-
-      if (featureVector[featureIndex] <= threshold) {
-        String rval = "'" + feature + "':" + featureVector[featureIndex] + " <= "
-            + threshold + ", Go Left | ";
-        return rval + left.explain(featureVector);
-      } else {
-        String rval = "'" + feature + "':" + featureVector[featureIndex] + " > "
-            + threshold + ", Go Right | ";
-        return rval + right.explain(featureVector);
-      }
-    }
-
-    @Override
-    public String toString() {
-      final StringBuilder sb = new StringBuilder();
-      if (isLeaf()) {
-        sb.append(value);
-      } else {
-        sb.append("(feature=").append(feature);
-        sb.append(",threshold=").append(threshold.floatValue()-NODE_SPLIT_SLACK);
-        sb.append(",left=").append(left);
-        sb.append(",right=").append(right);
-        sb.append(')');
-      }
-      return sb.toString();
-    }
-
-    public RegressionTreeNode() {
-    }
-
-    public void validate() throws ModelException {
-      if (isLeaf()) {
-        if (left != null || right != null) {
-          throw new ModelException("LambdaMARTModel tree node is leaf with left="+left+" and right="+right);
-        }
-        return;
-      }
-      if (null == threshold) {
-        throw new ModelException("LambdaMARTModel tree node is missing threshold");
-      }
-      if (null == left) {
-        throw new ModelException("LambdaMARTModel tree node is missing left");
-      } else {
-        left.validate();
-      }
-      if (null == right) {
-        throw new ModelException("LambdaMARTModel tree node is missing right");
-      } else {
-        right.validate();
-      }
-    }
-
-  }
-
-  public class RegressionTree {
-
-    private Float weight;
-    private RegressionTreeNode root;
-
-    public void setWeight(float weight) {
-      this.weight = new Float(weight);
-    }
-
-    public void setWeight(String weight) {
-      this.weight = new Float(weight);
-    }
-
-    public void setRoot(Object root) {
-      this.root = createRegressionTreeNode((Map<String,Object>)root);
-    }
-
-    public float score(float[] featureVector) {
-      return weight.floatValue() * root.score(featureVector);
-    }
-
-    public String explain(float[] featureVector) {
-      return root.explain(featureVector);
-    }
-
-    @Override
-    public String toString() {
-      final StringBuilder sb = new StringBuilder();
-      sb.append("(weight=").append(weight);
-      sb.append(",root=").append(root);
-      sb.append(")");
-      return sb.toString();
-    }
-
-    public RegressionTree() {
-    }
-
-    public void validate() throws ModelException {
-      if (weight == null) {
-        throw new ModelException("LambdaMARTModel tree doesn't contain a weight");
-      }
-      if (root == null) {
-        throw new ModelException("LambdaMARTModel tree doesn't contain a tree");
-      } else {
-        root.validate();
-      }
-    }
-  }
-
-  public void setTrees(Object trees) {
-    this.trees = new ArrayList<RegressionTree>();
-    for (final Object o : (List<Object>) trees) {
-      final RegressionTree rt = createRegressionTree((Map<String,Object>) o);
-      this.trees.add(rt);
-    }
-  }
-
-  public LambdaMARTModel(String name, List<Feature> features,
-      List<Normalizer> norms,
-      String featureStoreName, List<Feature> allFeatures,
-      Map<String,Object> params) {
-    super(name, features, norms, featureStoreName, allFeatures, params);
-
-    fname2index = new HashMap<String,Integer>();
-    for (int i = 0; i < features.size(); ++i) {
-      final String key = features.get(i).getName();
-      fname2index.put(key, i);
-    }
-  }
-
-  @Override
-  public void validate() throws ModelException {
-    super.validate();
-    if (trees == null) {
-      throw new ModelException("no trees declared for model "+name);
-    }
-    for (RegressionTree tree : trees) {
-      tree.validate();
-    }
-  }
-
- @Override
-  public float score(float[] modelFeatureValuesNormalized) {
-    float score = 0;
-    for (final RegressionTree t : trees) {
-      score += t.score(modelFeatureValuesNormalized);
-    }
-    return score;
-  }
-
-  // /////////////////////////////////////////
-  // produces a string that looks like:
-  // 40.0 = lambdamartmodel [ org.apache.solr.ltr.model.LambdaMARTModel ]
-  // model applied to
-  // features, sum of:
-  // 50.0 = tree 0 | 'matchedTitle':1.0 > 0.500001, Go Right |
-  // 'this_feature_doesnt_exist' does not
-  // exist in FV, Go Left | val: 50.0
-  // -10.0 = tree 1 | val: -10.0
-  @Override
-  public Explanation explain(LeafReaderContext context, int doc,
-      float finalScore, List<Explanation> featureExplanations) {
-    final float[] fv = new float[featureExplanations.size()];
-    int index = 0;
-    for (final Explanation featureExplain : featureExplanations) {
-      fv[index] = featureExplain.getValue();
-      index++;
-    }
-
-    final List<Explanation> details = new ArrayList<>();
-    index = 0;
-
-    for (final RegressionTree t : trees) {
-      final float score = t.score(fv);
-      final Explanation p = Explanation.match(score, "tree " + index + " | "
-          + t.explain(fv));
-      details.add(p);
-      index++;
-    }
-
-    return Explanation.match(finalScore, toString()
-        + " model applied to features, sum of:", details);
-  }
-
-  @Override
-  public String toString() {
-    final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
-    sb.append("(name=").append(getName());
-    sb.append(",trees=[");
-    for (int ii = 0; ii < trees.size(); ++ii) {
-      if (ii>0) sb.append(',');
-      sb.append(trees.get(ii));
-    }
-    sb.append("])");
-    return sb.toString();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LinearModel.java
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LinearModel.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LinearModel.java
new file mode 100644
index 0000000..0e8d190
--- /dev/null
+++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/LinearModel.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.ltr.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.Explanation;
+import org.apache.solr.ltr.feature.Feature;
+import org.apache.solr.ltr.norm.Normalizer;
+
+/**
+ * A scoring model that computes scores using a dot product. 
+ * Example models are RankSVM and Pranking.
+ * <p>
+ * Example configuration:
+ * <pre>{
+   "class" : "org.apache.solr.ltr.model.LinearModel",
+   "name" : "myModelName",
+   "features" : [
+       { "name" : "userTextTitleMatch" },
+       { "name" : "originalScore" },
+       { "name" : "isBook" }
+   ],
+   "params" : {
+       "weights" : {
+           "userTextTitleMatch" : 1.0,
+           "originalScore" : 0.5,
+           "isBook" : 0.1
+       }
+   }
+}</pre>
+ * <p>
+ * Background reading:
+ * <ul>
+ * <li> <a href="http://www.cs.cornell.edu/people/tj/publications/joachims_02c.pdf">
+ * Thorsten Joachims. Optimizing Search Engines Using Clickthrough Data.
+ * Proceedings of the ACM Conference on Knowledge Discovery and Data Mining (KDD), ACM, 2002.</a>
+ * </ul>
+ * <ul>
+ * <li> <a href="https://papers.nips.cc/paper/2023-pranking-with-ranking.pdf">
+ * Koby Crammer and Yoram Singer. Pranking with Ranking.
+ * Advances in Neural Information Processing Systems (NIPS), 2001.</a>
+ * </ul>
+ */
+public class LinearModel extends LTRScoringModel {
+
+  protected Float[] featureToWeight;
+
+  public void setWeights(Object weights) {
+    final Map<String,Double> modelWeights = (Map<String,Double>) weights;
+    for (int ii = 0; ii < features.size(); ++ii) {
+      final String key = features.get(ii).getName();
+      final Double val = modelWeights.get(key);
+      featureToWeight[ii] = (val == null ? null : new Float(val.floatValue()));
+    }
+  }
+
+  public LinearModel(String name, List<Feature> features,
+      List<Normalizer> norms,
+      String featureStoreName, List<Feature> allFeatures,
+      Map<String,Object> params) {
+    super(name, features, norms, featureStoreName, allFeatures, params);
+    featureToWeight = new Float[features.size()];
+  }
+
+  @Override
+  public void validate() throws ModelException {
+    super.validate();
+
+    final ArrayList<String> missingWeightFeatureNames = new ArrayList<String>();
+    for (int i = 0; i < features.size(); ++i) {
+      if (featureToWeight[i] == null) {
+        missingWeightFeatureNames.add(features.get(i).getName());
+      }
+    }
+    if (missingWeightFeatureNames.size() == features.size()) {
+      throw new ModelException("Model " + name + " doesn't contain any weights");
+    }
+    if (!missingWeightFeatureNames.isEmpty()) {
+      throw new ModelException("Model " + name + " lacks weight(s) for "+missingWeightFeatureNames);
+    }
+  }
+
+  @Override
+  public float score(float[] modelFeatureValuesNormalized) {
+    float score = 0;
+    for (int i = 0; i < modelFeatureValuesNormalized.length; ++i) {
+      score += modelFeatureValuesNormalized[i] * featureToWeight[i];
+    }
+    return score;
+  }
+
+  @Override
+  public Explanation explain(LeafReaderContext context, int doc,
+      float finalScore, List<Explanation> featureExplanations) {
+    final List<Explanation> details = new ArrayList<>();
+    int index = 0;
+
+    for (final Explanation featureExplain : featureExplanations) {
+      final List<Explanation> featureDetails = new ArrayList<>();
+      featureDetails.add(Explanation.match(featureToWeight[index],
+          "weight on feature"));
+      featureDetails.add(featureExplain);
+
+      details.add(Explanation.match(featureExplain.getValue()
+          * featureToWeight[index], "prod of:", featureDetails));
+      index++;
+    }
+
+    return Explanation.match(finalScore, toString()
+        + " model applied to features, sum of:", details);
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+    sb.append("(name=").append(getName());
+    sb.append(",featureWeights=[");
+    for (int ii = 0; ii < features.size(); ++ii) {
+      if (ii>0) sb.append(',');
+      final String key = features.get(ii).getName();
+      sb.append(key).append('=').append(featureToWeight[ii]);
+    }
+    sb.append("])");
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/MultipleAdditiveTreesModel.java
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/MultipleAdditiveTreesModel.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/MultipleAdditiveTreesModel.java
new file mode 100644
index 0000000..0046b46
--- /dev/null
+++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/MultipleAdditiveTreesModel.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.ltr.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.Explanation;
+import org.apache.solr.ltr.feature.Feature;
+import org.apache.solr.ltr.norm.Normalizer;
+import org.apache.solr.util.SolrPluginUtils;
+
+/**
+ * A scoring model that computes scores based on the summation of multiple weighted trees.
+ * Example models are LambdaMART and Gradient Boosted Regression Trees (GBRT) .
+ * <p>
+ * Example configuration:
+<pre>{
+   "class" : "org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+   "name" : "multipleadditivetreesmodel",
+   "features":[
+       { "name" : "userTextTitleMatch"},
+       { "name" : "originalScore"}
+   ],
+   "params" : {
+       "trees" : [
+           {
+               "weight" : 1,
+               "root": {
+                   "feature" : "userTextTitleMatch",
+                   "threshold" : 0.5,
+                   "left" : {
+                       "value" : -100
+                   },
+                   "right" : {
+                       "feature" : "originalScore",
+                       "threshold" : 10.0,
+                       "left" : {
+                           "value" : 50
+                       },
+                       "right" : {
+                           "value" : 75
+                       }
+                   }
+               }
+           },
+           {
+               "weight" : 2,
+               "root" : {
+                   "value" : -10
+               }
+           }
+       ]
+   }
+}</pre>
+ * <p>
+ * Background reading:
+ * <ul>
+ * <li> <a href="http://research.microsoft.com/pubs/132652/MSR-TR-2010-82.pdf">
+ * Christopher J.C. Burges. From RankNet to LambdaRank to LambdaMART: An Overview.
+ * Microsoft Research Technical Report MSR-TR-2010-82.</a>
+ * </ul>
+ * <ul>
+ * <li> <a href="https://papers.nips.cc/paper/3305-a-general-boosting-method-and-its-application-to-learning-ranking-functions-for-web-search.pdf">
+ * Z. Zheng, H. Zha, T. Zhang, O. Chapelle, K. Chen, and G. Sun. A General Boosting Method and its Application to Learning Ranking Functions for Web Search.
+ * Advances in Neural Information Processing Systems (NIPS), 2007.</a>
+ * </ul>
+ */
+public class MultipleAdditiveTreesModel extends LTRScoringModel {
+
+  private final HashMap<String,Integer> fname2index;
+  private List<RegressionTree> trees;
+
+  private RegressionTree createRegressionTree(Map<String,Object> map) {
+    final RegressionTree rt = new RegressionTree();
+    if (map != null) {
+      SolrPluginUtils.invokeSetters(rt, map.entrySet());
+    }
+    return rt;
+  }
+
+  private RegressionTreeNode createRegressionTreeNode(Map<String,Object> map) {
+    final RegressionTreeNode rtn = new RegressionTreeNode();
+    if (map != null) {
+      SolrPluginUtils.invokeSetters(rtn, map.entrySet());
+    }
+    return rtn;
+  }
+
+  public class RegressionTreeNode {
+    private static final float NODE_SPLIT_SLACK = 1E-6f;
+
+    private float value = 0f;
+    private String feature;
+    private int featureIndex = -1;
+    private Float threshold;
+    private RegressionTreeNode left;
+    private RegressionTreeNode right;
+
+    public void setValue(float value) {
+      this.value = value;
+    }
+
+    public void setValue(String value) {
+      this.value = Float.parseFloat(value);
+    }
+
+    public void setFeature(String feature) {
+      this.feature = feature;
+      final Integer idx = fname2index.get(this.feature);
+      // this happens if the tree specifies a feature that does not exist
+      // this could be due to lambdaSmart building off of pre-existing trees
+      // that use a feature that is no longer output during feature extraction
+      featureIndex = (idx == null) ? -1 : idx;
+    }
+
+    public void setThreshold(float threshold) {
+      this.threshold = threshold + NODE_SPLIT_SLACK;
+    }
+
+    public void setThreshold(String threshold) {
+      this.threshold = Float.parseFloat(threshold) + NODE_SPLIT_SLACK;
+    }
+
+    public void setLeft(Object left) {
+      this.left = createRegressionTreeNode((Map<String,Object>) left);
+    }
+
+    public void setRight(Object right) {
+      this.right = createRegressionTreeNode((Map<String,Object>) right);
+    }
+
+    public boolean isLeaf() {
+      return feature == null;
+    }
+
+    public float score(float[] featureVector) {
+      if (isLeaf()) {
+        return value;
+      }
+
+      // unsupported feature (tree is looking for a feature that does not exist)
+      if  ((featureIndex < 0) || (featureIndex >= featureVector.length)) {
+        return 0f;
+      }
+
+      if (featureVector[featureIndex] <= threshold) {
+        return left.score(featureVector);
+      } else {
+        return right.score(featureVector);
+      }
+    }
+
+    public String explain(float[] featureVector) {
+      if (isLeaf()) {
+        return "val: " + value;
+      }
+
+      // unsupported feature (tree is looking for a feature that does not exist)
+      if  ((featureIndex < 0) || (featureIndex >= featureVector.length)) {
+        return  "'" + feature + "' does not exist in FV, Return Zero";
+      }
+
+      // could store extra information about how much training data supported
+      // each branch and report
+      // that here
+
+      if (featureVector[featureIndex] <= threshold) {
+        String rval = "'" + feature + "':" + featureVector[featureIndex] + " <= "
+            + threshold + ", Go Left | ";
+        return rval + left.explain(featureVector);
+      } else {
+        String rval = "'" + feature + "':" + featureVector[featureIndex] + " > "
+            + threshold + ", Go Right | ";
+        return rval + right.explain(featureVector);
+      }
+    }
+
+    @Override
+    public String toString() {
+      final StringBuilder sb = new StringBuilder();
+      if (isLeaf()) {
+        sb.append(value);
+      } else {
+        sb.append("(feature=").append(feature);
+        sb.append(",threshold=").append(threshold.floatValue()-NODE_SPLIT_SLACK);
+        sb.append(",left=").append(left);
+        sb.append(",right=").append(right);
+        sb.append(')');
+      }
+      return sb.toString();
+    }
+
+    public RegressionTreeNode() {
+    }
+
+    public void validate() throws ModelException {
+      if (isLeaf()) {
+        if (left != null || right != null) {
+          throw new ModelException("MultipleAdditiveTreesModel tree node is leaf with left="+left+" and right="+right);
+        }
+        return;
+      }
+      if (null == threshold) {
+        throw new ModelException("MultipleAdditiveTreesModel tree node is missing threshold");
+      }
+      if (null == left) {
+        throw new ModelException("MultipleAdditiveTreesModel tree node is missing left");
+      } else {
+        left.validate();
+      }
+      if (null == right) {
+        throw new ModelException("MultipleAdditiveTreesModel tree node is missing right");
+      } else {
+        right.validate();
+      }
+    }
+
+  }
+
+  public class RegressionTree {
+
+    private Float weight;
+    private RegressionTreeNode root;
+
+    public void setWeight(float weight) {
+      this.weight = new Float(weight);
+    }
+
+    public void setWeight(String weight) {
+      this.weight = new Float(weight);
+    }
+
+    public void setRoot(Object root) {
+      this.root = createRegressionTreeNode((Map<String,Object>)root);
+    }
+
+    public float score(float[] featureVector) {
+      return weight.floatValue() * root.score(featureVector);
+    }
+
+    public String explain(float[] featureVector) {
+      return root.explain(featureVector);
+    }
+
+    @Override
+    public String toString() {
+      final StringBuilder sb = new StringBuilder();
+      sb.append("(weight=").append(weight);
+      sb.append(",root=").append(root);
+      sb.append(")");
+      return sb.toString();
+    }
+
+    public RegressionTree() {
+    }
+
+    public void validate() throws ModelException {
+      if (weight == null) {
+        throw new ModelException("MultipleAdditiveTreesModel tree doesn't contain a weight");
+      }
+      if (root == null) {
+        throw new ModelException("MultipleAdditiveTreesModel tree doesn't contain a tree");
+      } else {
+        root.validate();
+      }
+    }
+  }
+
+  public void setTrees(Object trees) {
+    this.trees = new ArrayList<RegressionTree>();
+    for (final Object o : (List<Object>) trees) {
+      final RegressionTree rt = createRegressionTree((Map<String,Object>) o);
+      this.trees.add(rt);
+    }
+  }
+
+  public MultipleAdditiveTreesModel(String name, List<Feature> features,
+      List<Normalizer> norms,
+      String featureStoreName, List<Feature> allFeatures,
+      Map<String,Object> params) {
+    super(name, features, norms, featureStoreName, allFeatures, params);
+
+    fname2index = new HashMap<String,Integer>();
+    for (int i = 0; i < features.size(); ++i) {
+      final String key = features.get(i).getName();
+      fname2index.put(key, i);
+    }
+  }
+
+  @Override
+  public void validate() throws ModelException {
+    super.validate();
+    if (trees == null) {
+      throw new ModelException("no trees declared for model "+name);
+    }
+    for (RegressionTree tree : trees) {
+      tree.validate();
+    }
+  }
+
+ @Override
+  public float score(float[] modelFeatureValuesNormalized) {
+    float score = 0;
+    for (final RegressionTree t : trees) {
+      score += t.score(modelFeatureValuesNormalized);
+    }
+    return score;
+  }
+
+  // /////////////////////////////////////////
+  // produces a string that looks like:
+  // 40.0 = multipleadditivetreesmodel [ org.apache.solr.ltr.model.MultipleAdditiveTreesModel ]
+  // model applied to
+  // features, sum of:
+  // 50.0 = tree 0 | 'matchedTitle':1.0 > 0.500001, Go Right |
+  // 'this_feature_doesnt_exist' does not
+  // exist in FV, Go Left | val: 50.0
+  // -10.0 = tree 1 | val: -10.0
+  @Override
+  public Explanation explain(LeafReaderContext context, int doc,
+      float finalScore, List<Explanation> featureExplanations) {
+    final float[] fv = new float[featureExplanations.size()];
+    int index = 0;
+    for (final Explanation featureExplain : featureExplanations) {
+      fv[index] = featureExplain.getValue();
+      index++;
+    }
+
+    final List<Explanation> details = new ArrayList<>();
+    index = 0;
+
+    for (final RegressionTree t : trees) {
+      final float score = t.score(fv);
+      final Explanation p = Explanation.match(score, "tree " + index + " | "
+          + t.explain(fv));
+      details.add(p);
+      index++;
+    }
+
+    return Explanation.match(finalScore, toString()
+        + " model applied to features, sum of:", details);
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+    sb.append("(name=").append(getName());
+    sb.append(",trees=[");
+    for (int ii = 0; ii < trees.size(); ++ii) {
+      if (ii>0) sb.append(',');
+      sb.append(trees.get(ii));
+    }
+    sb.append("])");
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/RankSVMModel.java
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/RankSVMModel.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/RankSVMModel.java
deleted file mode 100644
index 64e1216..0000000
--- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/model/RankSVMModel.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.ltr.model;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Explanation;
-import org.apache.solr.ltr.feature.Feature;
-import org.apache.solr.ltr.norm.Normalizer;
-
-/**
- * A scoring model that computes scores using a linear Support Vector Machine (SVM) algorithm.
- * <p>
- * Example configuration:
- * <pre>{
-   "class" : "org.apache.solr.ltr.model.RankSVMModel",
-   "name" : "myModelName",
-   "features" : [
-       { "name" : "userTextTitleMatch" },
-       { "name" : "originalScore" },
-       { "name" : "isBook" }
-   ],
-   "params" : {
-       "weights" : {
-           "userTextTitleMatch" : 1.0,
-           "originalScore" : 0.5,
-           "isBook" : 0.1
-       }
-   }
-}</pre>
- * <p>
- * Background reading:
- * <ul>
- * <li> <a href="http://www.cs.cornell.edu/people/tj/publications/joachims_02c.pdf">
- * Thorsten Joachims. Optimizing Search Engines Using Clickthrough Data.
- * Proceedings of the ACM Conference on Knowledge Discovery and Data Mining (KDD), ACM, 2002.</a>
- * </ul>
- */
-public class RankSVMModel extends LTRScoringModel {
-
-  protected Float[] featureToWeight;
-
-  public void setWeights(Object weights) {
-    final Map<String,Double> modelWeights = (Map<String,Double>) weights;
-    for (int ii = 0; ii < features.size(); ++ii) {
-      final String key = features.get(ii).getName();
-      final Double val = modelWeights.get(key);
-      featureToWeight[ii] = (val == null ? null : new Float(val.floatValue()));
-    }
-  }
-
-  public RankSVMModel(String name, List<Feature> features,
-      List<Normalizer> norms,
-      String featureStoreName, List<Feature> allFeatures,
-      Map<String,Object> params) {
-    super(name, features, norms, featureStoreName, allFeatures, params);
-    featureToWeight = new Float[features.size()];
-  }
-
-  @Override
-  public void validate() throws ModelException {
-    super.validate();
-
-    final ArrayList<String> missingWeightFeatureNames = new ArrayList<String>();
-    for (int i = 0; i < features.size(); ++i) {
-      if (featureToWeight[i] == null) {
-        missingWeightFeatureNames.add(features.get(i).getName());
-      }
-    }
-    if (missingWeightFeatureNames.size() == features.size()) {
-      throw new ModelException("Model " + name + " doesn't contain any weights");
-    }
-    if (!missingWeightFeatureNames.isEmpty()) {
-      throw new ModelException("Model " + name + " lacks weight(s) for "+missingWeightFeatureNames);
-    }
-  }
-
-  @Override
-  public float score(float[] modelFeatureValuesNormalized) {
-    float score = 0;
-    for (int i = 0; i < modelFeatureValuesNormalized.length; ++i) {
-      score += modelFeatureValuesNormalized[i] * featureToWeight[i];
-    }
-    return score;
-  }
-
-  @Override
-  public Explanation explain(LeafReaderContext context, int doc,
-      float finalScore, List<Explanation> featureExplanations) {
-    final List<Explanation> details = new ArrayList<>();
-    int index = 0;
-
-    for (final Explanation featureExplain : featureExplanations) {
-      final List<Explanation> featureDetails = new ArrayList<>();
-      featureDetails.add(Explanation.match(featureToWeight[index],
-          "weight on feature"));
-      featureDetails.add(featureExplain);
-
-      details.add(Explanation.match(featureExplain.getValue()
-          * featureToWeight[index], "prod of:", featureDetails));
-      index++;
-    }
-
-    return Explanation.match(finalScore, toString()
-        + " model applied to features, sum of:", details);
-  }
-
-  @Override
-  public String toString() {
-    final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
-    sb.append("(name=").append(getName());
-    sb.append(",featureWeights=[");
-    for (int ii = 0; ii < features.size(); ++ii) {
-      if (ii>0) sb.append(',');
-      final String key = features.get(ii).getName();
-      sb.append(key).append('=').append(featureToWeight[ii]);
-    }
-    sb.append("])");
-    return sb.toString();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/org/apache/solr/ltr/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/package-info.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/package-info.java
index 678ffd4..59aebe8 100644
--- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/package-info.java
+++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/package-info.java
@@ -33,7 +33,7 @@
  * defines how to combine the features in order to create a new
  * score for a document. A new Learning to Rank model is plugged
  * into the framework  by extending {@link org.apache.solr.ltr.model.LTRScoringModel},
- * (see for example {@link org.apache.solr.ltr.model.LambdaMARTModel} and {@link org.apache.solr.ltr.model.RankSVMModel}).
+ * (see for example {@link org.apache.solr.ltr.model.MultipleAdditiveTreesModel} and {@link org.apache.solr.ltr.model.LinearModel}).
  * </p>
  * <p>
  * The {@link org.apache.solr.ltr.LTRScoringQuery} will take care of computing the values of

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/java/overview.html
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/java/overview.html b/solr/contrib/ltr/src/java/overview.html
index d27aa9e..7b8198c 100644
--- a/solr/contrib/ltr/src/java/overview.html
+++ b/solr/contrib/ltr/src/java/overview.html
@@ -57,7 +57,7 @@ A {@link org.apache.solr.ltr.LTRScoringQuery} is created by providing an instanc
 defines how to combine the features in order to create a new
 score for a document. A new learning to rank model is plugged
 into the framework  by extending {@link org.apache.solr.ltr.model.LTRScoringModel},
-(see for example {@link org.apache.solr.ltr.model.LambdaMARTModel} and {@link org.apache.solr.ltr.model.RankSVMModel}).
+(see for example {@link org.apache.solr.ltr.model.MultipleAdditiveTreesModel} and {@link org.apache.solr.ltr.model.LinearModel}).
 </p>
 <p>
 The {@link org.apache.solr.ltr.LTRScoringQuery} will take care of computing the values of

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/features-linear-efi.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/features-linear-efi.json b/solr/contrib/ltr/src/test-files/featureExamples/features-linear-efi.json
new file mode 100644
index 0000000..e05542a
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/featureExamples/features-linear-efi.json
@@ -0,0 +1,17 @@
+[
+  {
+    "name": "sampleConstant",
+    "class": "org.apache.solr.ltr.feature.ValueFeature",
+    "params": {
+      "value": 5
+    }
+  },
+  {
+    "name" : "search_number_of_nights",
+    "class":"org.apache.solr.ltr.feature.ValueFeature",
+    "params" : {
+      "value": "${search_number_of_nights}"
+    }
+  }
+
+]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/features-linear.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/features-linear.json b/solr/contrib/ltr/src/test-files/featureExamples/features-linear.json
new file mode 100644
index 0000000..8cc2996
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/featureExamples/features-linear.json
@@ -0,0 +1,51 @@
+[
+  {
+     "name": "title",
+     "class": "org.apache.solr.ltr.feature.ValueFeature",
+     "params": {
+         "value": 1
+     }
+  },
+  {
+    "name": "description",
+    "class": "org.apache.solr.ltr.feature.ValueFeature",
+    "params": {
+       "value": 2
+    }
+  },
+  {
+    "name": "keywords",
+    "class": "org.apache.solr.ltr.feature.ValueFeature",
+    "params": {
+        "value": 2
+    }
+  },
+ {
+     "name": "popularity",
+     "class": "org.apache.solr.ltr.feature.ValueFeature",
+     "params": {
+         "value": 3
+     }
+ },
+ {
+     "name": "text",
+     "class": "org.apache.solr.ltr.feature.ValueFeature",
+     "params": {
+         "value": 4
+     }
+ },
+ {
+   "name": "queryIntentPerson",
+   "class": "org.apache.solr.ltr.feature.ValueFeature",
+   "params": {
+       "value": 5
+   }
+ },
+ {
+   "name": "queryIntentCompany",
+   "class": "org.apache.solr.ltr.feature.ValueFeature",
+   "params": {
+       "value": 5
+   }
+ }
+]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm-efi.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm-efi.json b/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm-efi.json
deleted file mode 100644
index e05542a..0000000
--- a/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm-efi.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
-  {
-    "name": "sampleConstant",
-    "class": "org.apache.solr.ltr.feature.ValueFeature",
-    "params": {
-      "value": 5
-    }
-  },
-  {
-    "name" : "search_number_of_nights",
-    "class":"org.apache.solr.ltr.feature.ValueFeature",
-    "params" : {
-      "value": "${search_number_of_nights}"
-    }
-  }
-
-]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm.json b/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm.json
deleted file mode 100644
index 8cc2996..0000000
--- a/solr/contrib/ltr/src/test-files/featureExamples/features-ranksvm.json
+++ /dev/null
@@ -1,51 +0,0 @@
-[
-  {
-     "name": "title",
-     "class": "org.apache.solr.ltr.feature.ValueFeature",
-     "params": {
-         "value": 1
-     }
-  },
-  {
-    "name": "description",
-    "class": "org.apache.solr.ltr.feature.ValueFeature",
-    "params": {
-       "value": 2
-    }
-  },
-  {
-    "name": "keywords",
-    "class": "org.apache.solr.ltr.feature.ValueFeature",
-    "params": {
-        "value": 2
-    }
-  },
- {
-     "name": "popularity",
-     "class": "org.apache.solr.ltr.feature.ValueFeature",
-     "params": {
-         "value": 3
-     }
- },
- {
-     "name": "text",
-     "class": "org.apache.solr.ltr.feature.ValueFeature",
-     "params": {
-         "value": 4
-     }
- },
- {
-   "name": "queryIntentPerson",
-   "class": "org.apache.solr.ltr.feature.ValueFeature",
-   "params": {
-       "value": 5
-   }
- },
- {
-   "name": "queryIntentCompany",
-   "class": "org.apache.solr.ltr.feature.ValueFeature",
-   "params": {
-       "value": 5
-   }
- }
-]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/lambdamart_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/lambdamart_features.json b/solr/contrib/ltr/src/test-files/featureExamples/lambdamart_features.json
deleted file mode 100644
index 3bc2c77..0000000
--- a/solr/contrib/ltr/src/test-files/featureExamples/lambdamart_features.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[
-    {
-        "name": "matchedTitle",
-        "class": "org.apache.solr.ltr.feature.SolrFeature",
-        "params": {
-            "q": "{!terms f=title}${user_query}"
-        }
-    },
-    {
-        "name": "constantScoreToForceLambdaMARTScoreAllDocs",
-        "class": "org.apache.solr.ltr.feature.ValueFeature",
-        "params": {
-            "value": 1
-        }
-    }
-]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/featureExamples/multipleadditivetreesmodel_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/featureExamples/multipleadditivetreesmodel_features.json b/solr/contrib/ltr/src/test-files/featureExamples/multipleadditivetreesmodel_features.json
new file mode 100644
index 0000000..92f3861
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/featureExamples/multipleadditivetreesmodel_features.json
@@ -0,0 +1,16 @@
+[
+    {
+        "name": "matchedTitle",
+        "class": "org.apache.solr.ltr.feature.SolrFeature",
+        "params": {
+            "q": "{!terms f=title}${user_query}"
+        }
+    },
+    {
+        "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs",
+        "class": "org.apache.solr.ltr.feature.ValueFeature",
+        "params": {
+            "value": 1
+        }
+    }
+]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/external_model.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/external_model.json b/solr/contrib/ltr/src/test-files/modelExamples/external_model.json
index c7f35c0..04ab229 100644
--- a/solr/contrib/ltr/src/test-files/modelExamples/external_model.json
+++ b/solr/contrib/ltr/src/test-files/modelExamples/external_model.json
@@ -1,5 +1,5 @@
 {
-    "class":"org.apache.solr.ltr.model.RankSVMModel",
+    "class":"org.apache.solr.ltr.model.LinearModel",
     "name":"externalmodel",
     "features":[
         { "name": "matchedTitle"}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/external_model_store.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/external_model_store.json b/solr/contrib/ltr/src/test-files/modelExamples/external_model_store.json
index 227a943..f8e6648 100644
--- a/solr/contrib/ltr/src/test-files/modelExamples/external_model_store.json
+++ b/solr/contrib/ltr/src/test-files/modelExamples/external_model_store.json
@@ -1,5 +1,5 @@
 {
-    "class":"org.apache.solr.ltr.model.RankSVMModel",
+    "class":"org.apache.solr.ltr.model.LinearModel",
     "name":"externalmodelstore",
     "store": "fstore2",
     "features":[

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/fq-model.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/fq-model.json b/solr/contrib/ltr/src/test-files/modelExamples/fq-model.json
index c0fa77a..b5d631f 100644
--- a/solr/contrib/ltr/src/test-files/modelExamples/fq-model.json
+++ b/solr/contrib/ltr/src/test-files/modelExamples/fq-model.json
@@ -1,5 +1,5 @@
 {
-	"class":"org.apache.solr.ltr.model.RankSVMModel",
+	"class":"org.apache.solr.ltr.model.LinearModel",
 	"name":"fqmodel",
 	"features":[
         {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel.json
deleted file mode 100644
index 782e641..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "1f",
-                "root": {
-                    "feature": "matchedTitle",
-                    "threshold": "0.5f",
-                    "left" : {
-                        "value" : "-100"
-                    },
-                    "right": {
-                        "feature" : "this_feature_doesnt_exist",
-                        "threshold": "10.0f",
-                        "left" : {
-                            "value" : "50"
-                        },
-                        "right" : {
-                            "value" : "75"
-                        }
-                    }
-                }
-            },
-            {
-                "weight" : "2f",
-                "root": {
-                    "value" : "-10"
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_external_binary_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_external_binary_features.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_external_binary_features.json
deleted file mode 100644
index ce884de..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_external_binary_features.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-  "name":"external_model_binary_feature",
-  "features":[
-    { "name": "user_device_smartphone"},
-    { "name": "user_device_tablet"}
-  ],
-  "params":{
-    "trees": [
-      {
-        "weight" : "1f",
-        "root": {
-          "feature": "user_device_smartphone",
-          "threshold": "0.5f",
-          "left" : {
-            "value" : "0"
-          },
-          "right" : {
-            "value" : "50"
-          }
-
-        }},
-      {
-        "weight" : "1f",
-        "root": {
-          "feature": "user_device_tablet",
-          "threshold": "0.5f",
-          "left" : {
-            "value" : "0"
-          },
-          "right" : {
-            "value" : "65"
-          }
-
-        }}
-    ]
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_feature.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_feature.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_feature.json
deleted file mode 100644
index 96e304f..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_feature.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_feature",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "1f",
-                "root": {
-                    "threshold": "0.5f",
-                    "left" : {
-                        "value" : "-100"
-                    },
-                    "right": {
-                        "value" : "75"
-                    }
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_features.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_features.json
deleted file mode 100644
index e534696..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_features.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_features",
-    "params":{
-        "trees": [
-            {
-                "weight" : "2f",
-                "root": {
-                    "value" : "-10"
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_left.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_left.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_left.json
deleted file mode 100644
index 5564bc3..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_left.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_left",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "1f",
-                "root": {
-                    "feature": "matchedTitle",
-                    "threshold": "0.5f",
-                    "right": {
-                        "value" : "75"
-                    }
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_params.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_params.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_params.json
deleted file mode 100644
index e48489d..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_params.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_params",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ]
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_right.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_right.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_right.json
deleted file mode 100644
index 672716d..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_right.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_right",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "1f",
-                "root": {
-                    "feature": "matchedTitle",
-                    "threshold": "0.5f",
-                    "left" : {
-                        "value" : "-100"
-                    }
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_threshold.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_threshold.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_threshold.json
deleted file mode 100644
index 2c6922f..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_threshold.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_threshold",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "1f",
-                "root": {
-                    "feature": "matchedTitle",
-                    "left" : {
-                        "value" : "-100"
-                    },
-                    "right": {
-                        "value" : "75"
-                    }
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_tree.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_tree.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_tree.json
deleted file mode 100644
index 6d1ae71..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_tree.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_tree",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "weight" : "2f"
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_trees.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_trees.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_trees.json
deleted file mode 100644
index 8576782..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_trees.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_trees",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_weight.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_weight.json b/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_weight.json
deleted file mode 100644
index 9dbda56..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/lambdamartmodel_no_weight.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "class":"org.apache.solr.ltr.model.LambdaMARTModel",
-    "name":"lambdamartmodel_no_weight",
-    "features":[
-        { "name": "matchedTitle"},
-        { "name": "constantScoreToForceLambdaMARTScoreAllDocs"}
-    ],
-    "params":{
-        "trees": [
-            {
-                "root": {
-                    "feature": "matchedTitle",
-                    "threshold": "0.5f",
-                    "left" : {
-                        "value" : "-100"
-                    },
-                    "right": {
-                        "value" : "75"
-                    }
-                }
-            }
-        ]
-    }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/linear-model-efi.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/linear-model-efi.json b/solr/contrib/ltr/src/test-files/modelExamples/linear-model-efi.json
new file mode 100644
index 0000000..018466e
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/linear-model-efi.json
@@ -0,0 +1,14 @@
+{
+  "class":"org.apache.solr.ltr.model.LinearModel",
+  "name":"linear-efi",
+  "features":[
+    {"name":"sampleConstant"},
+    {"name":"search_number_of_nights"}
+  ],
+  "params":{
+    "weights":{
+      "sampleConstant":1.0,
+      "search_number_of_nights":2.0
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/linear-model.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/linear-model.json b/solr/contrib/ltr/src/test-files/modelExamples/linear-model.json
new file mode 100644
index 0000000..6b46dca
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/linear-model.json
@@ -0,0 +1,30 @@
+{
+	"class":"org.apache.solr.ltr.model.LinearModel",
+	"name":"6029760550880411648",
+	"features":[
+        {"name":"title"},
+        {"name":"description"},
+        {"name":"keywords"},
+        {
+            "name":"popularity",
+            "norm": {
+                "class":"org.apache.solr.ltr.norm.MinMaxNormalizer",
+                "params":{ "min":"0.0f", "max":"10.0f" }
+            }
+        },
+        {"name":"text"},
+        {"name":"queryIntentPerson"},
+        {"name":"queryIntentCompany"}
+	],
+	"params":{
+	  "weights": {
+	    "title": 0.0000000000,
+	    "description": 0.1000000000,
+	    "keywords": 0.2000000000,
+	    "popularity": 0.3000000000,
+	    "text": 0.4000000000,
+	    "queryIntentPerson":0.1231231,
+	    "queryIntentCompany":0.12121211
+	  }
+	}
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel.json
new file mode 100644
index 0000000..37551a0
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel.json
@@ -0,0 +1,38 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "1f",
+                "root": {
+                    "feature": "matchedTitle",
+                    "threshold": "0.5f",
+                    "left" : {
+                        "value" : "-100"
+                    },
+                    "right": {
+                        "feature" : "this_feature_doesnt_exist",
+                        "threshold": "10.0f",
+                        "left" : {
+                            "value" : "50"
+                        },
+                        "right" : {
+                            "value" : "75"
+                        }
+                    }
+                }
+            },
+            {
+                "weight" : "2f",
+                "root": {
+                    "value" : "-10"
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_external_binary_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_external_binary_features.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_external_binary_features.json
new file mode 100644
index 0000000..cb8996e
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_external_binary_features.json
@@ -0,0 +1,38 @@
+{
+  "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+  "name":"external_model_binary_feature",
+  "features":[
+    { "name": "user_device_smartphone"},
+    { "name": "user_device_tablet"}
+  ],
+  "params":{
+    "trees": [
+      {
+        "weight" : "1f",
+        "root": {
+          "feature": "user_device_smartphone",
+          "threshold": "0.5f",
+          "left" : {
+            "value" : "0"
+          },
+          "right" : {
+            "value" : "50"
+          }
+
+        }},
+      {
+        "weight" : "1f",
+        "root": {
+          "feature": "user_device_tablet",
+          "threshold": "0.5f",
+          "left" : {
+            "value" : "0"
+          },
+          "right" : {
+            "value" : "65"
+          }
+
+        }}
+    ]
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_feature.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_feature.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_feature.json
new file mode 100644
index 0000000..2919f07
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_feature.json
@@ -0,0 +1,24 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_feature",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "1f",
+                "root": {
+                    "threshold": "0.5f",
+                    "left" : {
+                        "value" : "-100"
+                    },
+                    "right": {
+                        "value" : "75"
+                    }
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_features.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_features.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_features.json
new file mode 100644
index 0000000..ec4c37f
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_features.json
@@ -0,0 +1,14 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_features",
+    "params":{
+        "trees": [
+            {
+                "weight" : "2f",
+                "root": {
+                    "value" : "-10"
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_left.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_left.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_left.json
new file mode 100644
index 0000000..653d2ff
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_left.json
@@ -0,0 +1,22 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_left",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "1f",
+                "root": {
+                    "feature": "matchedTitle",
+                    "threshold": "0.5f",
+                    "right": {
+                        "value" : "75"
+                    }
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_params.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_params.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_params.json
new file mode 100644
index 0000000..4d50c4e
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_params.json
@@ -0,0 +1,8 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_params",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ]
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_right.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_right.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_right.json
new file mode 100644
index 0000000..acd2d83
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_right.json
@@ -0,0 +1,22 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_right",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "1f",
+                "root": {
+                    "feature": "matchedTitle",
+                    "threshold": "0.5f",
+                    "left" : {
+                        "value" : "-100"
+                    }
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_threshold.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_threshold.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_threshold.json
new file mode 100644
index 0000000..d0fc381
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_threshold.json
@@ -0,0 +1,24 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_threshold",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "1f",
+                "root": {
+                    "feature": "matchedTitle",
+                    "left" : {
+                        "value" : "-100"
+                    },
+                    "right": {
+                        "value" : "75"
+                    }
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_tree.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_tree.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_tree.json
new file mode 100644
index 0000000..507def3
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_tree.json
@@ -0,0 +1,15 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_tree",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "weight" : "2f"
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_trees.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_trees.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_trees.json
new file mode 100644
index 0000000..bb360dd
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_trees.json
@@ -0,0 +1,10 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_trees",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_weight.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_weight.json b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_weight.json
new file mode 100644
index 0000000..9048e6c
--- /dev/null
+++ b/solr/contrib/ltr/src/test-files/modelExamples/multipleadditivetreesmodel_no_weight.json
@@ -0,0 +1,24 @@
+{
+    "class":"org.apache.solr.ltr.model.MultipleAdditiveTreesModel",
+    "name":"multipleadditivetreesmodel_no_weight",
+    "features":[
+        { "name": "matchedTitle"},
+        { "name": "constantScoreToForceMultipleAdditiveTreesScoreAllDocs"}
+    ],
+    "params":{
+        "trees": [
+            {
+                "root": {
+                    "feature": "matchedTitle",
+                    "threshold": "0.5f",
+                    "left" : {
+                        "value" : "-100"
+                    },
+                    "right": {
+                        "value" : "75"
+                    }
+                }
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/bfa05b83/solr/contrib/ltr/src/test-files/modelExamples/ranksvm-model.json
----------------------------------------------------------------------
diff --git a/solr/contrib/ltr/src/test-files/modelExamples/ranksvm-model.json b/solr/contrib/ltr/src/test-files/modelExamples/ranksvm-model.json
deleted file mode 100644
index 774958a..0000000
--- a/solr/contrib/ltr/src/test-files/modelExamples/ranksvm-model.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-	"class":"org.apache.solr.ltr.model.RankSVMModel",
-	"name":"6029760550880411648",
-	"features":[
-        {"name":"title"},
-        {"name":"description"},
-        {"name":"keywords"},
-        {
-            "name":"popularity",
-            "norm": {
-                "class":"org.apache.solr.ltr.norm.MinMaxNormalizer",
-                "params":{ "min":"0.0f", "max":"10.0f" }
-            }
-        },
-        {"name":"text"},
-        {"name":"queryIntentPerson"},
-        {"name":"queryIntentCompany"}
-	],
-	"params":{
-	  "weights": {
-	    "title": 0.0000000000,
-	    "description": 0.1000000000,
-	    "keywords": 0.2000000000,
-	    "popularity": 0.3000000000,
-	    "text": 0.4000000000,
-	    "queryIntentPerson":0.1231231,
-	    "queryIntentCompany":0.12121211
-	  }
-	}
-}


Mime
View raw message