001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.experiment.evaluation.retrieval;
031
032import java.util.Collection;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036import java.util.Set;
037
038import org.openimaj.data.identity.Identifiable;
039import org.openimaj.experiment.evaluation.AnalysisResult;
040import org.openimaj.experiment.evaluation.Evaluator;
041
042/**
043 * An implementation of an {@link Evaluator} for the evaluation of retrieval
044 * experiments using the Cranfield methodology.
045 * 
046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
047 * 
048 * @param <RESULT>
049 *            Type of analysed data
050 * @param <DOCUMENT>
051 *            Type of documents
052 * @param <QUERY>
053 *            Type of query
054 */
055public class RetrievalEvaluator<RESULT extends AnalysisResult, DOCUMENT extends Identifiable, QUERY>
056                implements
057                Evaluator<Map<QUERY, List<DOCUMENT>>, RESULT>
058{
059        protected RetrievalEngine<DOCUMENT, QUERY> engine;
060        protected Collection<QUERY> queries;
061        protected Map<QUERY, Set<DOCUMENT>> relevant; // in the future we might want
062                                                                                                        // a model more like trec
063                                                                                                        // qrels with relevance
064                                                                                                        // levels
065        protected RetrievalAnalyser<RESULT, QUERY, DOCUMENT> analyser;
066
067        /**
068         * Construct a new {@link RetrievalEvaluator} with a search engine, a set of
069         * queries to perform, relevant documents for each query, and a
070         * {@link RetrievalAnalyser} to analyse the results.
071         * 
072         * @param engine
073         *            the query engine
074         * @param queries
075         *            the queries
076         * @param relevant
077         *            the relevant documents for each query
078         * @param analyser
079         *            the analyser
080         */
081        public RetrievalEvaluator(RetrievalEngine<DOCUMENT, QUERY> engine, Collection<QUERY> queries,
082                        Map<QUERY, Set<DOCUMENT>> relevant, RetrievalAnalyser<RESULT, QUERY, DOCUMENT> analyser)
083        {
084                this.engine = engine;
085                this.queries = queries;
086                this.relevant = relevant;
087                this.analyser = analyser;
088        }
089
090        /**
091         * Construct a new {@link RetrievalEvaluator} with a search engine, relevant
092         * documents for each query, and a {@link RetrievalAnalyser} to analyse the
093         * results. The queries are determined automatically from the keys of the
094         * map of relevant documents.
095         * 
096         * @param engine
097         *            the query engine
098         * @param relevant
099         *            the relevant documents for each query
100         * @param analyser
101         *            the analyser
102         */
103        public RetrievalEvaluator(RetrievalEngine<DOCUMENT, QUERY> engine, Map<QUERY, Set<DOCUMENT>> relevant,
104                        RetrievalAnalyser<RESULT, QUERY, DOCUMENT> analyser)
105        {
106                this.engine = engine;
107                this.queries = relevant.keySet();
108                this.relevant = relevant;
109                this.analyser = analyser;
110        }
111
112        /**
113         * Construct a new {@link RetrievalEvaluator} with the given ranked results
114         * lists and sets of relevant documents for each query, and a
115         * {@link RetrievalAnalyser} to analyse the results.
116         * <p>
117         * Internally, this constructor wraps a simple {@link RetrievalEngine}
118         * implementation around the results, and determines the set of queries from
119         * the keys of the relevant document map.
120         * 
121         * @param results
122         *            the ranked results per query
123         * @param relevant
124         *            the relevant results per query
125         * @param analyser
126         *            the analyser
127         */
128        public RetrievalEvaluator(final Map<QUERY, List<DOCUMENT>> results, Map<QUERY, Set<DOCUMENT>> relevant,
129                        RetrievalAnalyser<RESULT, QUERY, DOCUMENT> analyser)
130        {
131                this.engine = new RetrievalEngine<DOCUMENT, QUERY>() {
132                        @Override
133                        public List<DOCUMENT> search(QUERY query) {
134                                return results.get(query);
135                        }
136                };
137
138                this.queries = relevant.keySet();
139                this.relevant = relevant;
140                this.analyser = analyser;
141        }
142
143        @Override
144        public Map<QUERY, List<DOCUMENT>> evaluate() {
145                final Map<QUERY, List<DOCUMENT>> results = new HashMap<QUERY, List<DOCUMENT>>();
146
147                for (final QUERY query : queries) {
148                        results.put(query, engine.search(query));
149                }
150
151                return results;
152        }
153
154        @Override
155        public RESULT analyse(Map<QUERY, List<DOCUMENT>> results) {
156                return analyser.analyse(results, relevant);
157        }
158}