Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/FeatureCache.java |
— | — | @@ -11,6 +11,34 @@ |
12 | 12 | import de.brightbyte.wikiword.model.WikiWordConcept; |
13 | 13 | |
14 | 14 | public class FeatureCache<C extends WikiWordConcept, K> implements FeatureFetcher<C, K> { |
| 15 | + |
| 16 | + protected static class Manager<C extends WikiWordConcept, K> { |
| 17 | + protected int maxDepth; |
| 18 | + |
| 19 | + protected FeatureFetcher<C, K> root; |
| 20 | + protected List<FeatureCache<C, K>> stack; |
| 21 | + |
| 22 | + public Manager(FeatureFetcher<C, K> root, int maxDepth) { |
| 23 | + this.stack = new ArrayList<FeatureCache<C, K>>(maxDepth+1); |
| 24 | + this.maxDepth = maxDepth; |
| 25 | + this.root = root; |
| 26 | + } |
| 27 | + |
| 28 | + private FeatureFetcher<C, K> getTop() { |
| 29 | + if (stack.isEmpty()) return root; |
| 30 | + else return stack.get(stack.size()-1); |
| 31 | + } |
| 32 | + |
| 33 | + public synchronized FeatureCache<C, K> newCache() { |
| 34 | + FeatureCache<C, K> cache = new FeatureCache<C, K>( getTop() ); |
| 35 | + stack.add(cache); |
| 36 | + |
| 37 | + if (stack.size()>maxDepth) stack.remove(0); |
| 38 | + if (!stack.isEmpty()) stack.get(0).setParent(root); |
| 39 | + |
| 40 | + return cache; |
| 41 | + } |
| 42 | + } |
15 | 43 | |
16 | 44 | protected FeatureFetcher<C, K> parent; |
17 | 45 | |
— | — | @@ -54,11 +82,11 @@ |
55 | 83 | return features; |
56 | 84 | } |
57 | 85 | |
58 | | - public FeatureFetcher getParent() { |
| 86 | + public FeatureFetcher<C, K> getParent() { |
59 | 87 | return parent; |
60 | 88 | } |
61 | 89 | |
62 | | - public void setParent(FeatureCache<C, K> parent) { |
| 90 | + public void setParent(FeatureFetcher<C, K> parent) { |
63 | 91 | if (parent == null) throw new NullPointerException(); |
64 | 92 | if (parent == this) throw new IllegalArgumentException("can't be my own parent"); |
65 | 93 | //TODO: prevent cycles |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/MeaningFetcher.java |
— | — | @@ -1,10 +1,14 @@ |
2 | 2 | package de.brightbyte.wikiword.disambig; |
3 | 3 | |
4 | 4 | import java.util.List; |
| 5 | +import java.util.Map; |
5 | 6 | |
6 | 7 | import de.brightbyte.util.PersistenceException; |
| 8 | +import de.brightbyte.wikiword.model.TermReference; |
7 | 9 | import de.brightbyte.wikiword.model.WikiWordConcept; |
8 | 10 | |
9 | 11 | public interface MeaningFetcher<C extends WikiWordConcept> { |
10 | | - public List<C> getMeanings(String term) throws PersistenceException; |
| 12 | + public List<? extends C> getMeanings(String term) throws PersistenceException; |
| 13 | + |
| 14 | + public <X extends TermReference>Map<X, List<? extends C>> getMeanings(List<X> terms) throws PersistenceException; |
11 | 15 | } |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/SlidingCoherenceDisambiguator.java |
— | — | @@ -51,7 +51,7 @@ |
52 | 52 | Map<X, LocalConcept> disambig = new HashMap<X, LocalConcept>(meanings.size()); |
53 | 53 | |
54 | 54 | LabeledMatrix<LocalConcept, LocalConcept> similarities = new MapLabeledMatrix<LocalConcept, LocalConcept>(true); |
55 | | - FeatureCache<LocalConcept, Integer> features = getFeatureCache(meanings); |
| 55 | + FeatureFetcher<LocalConcept, Integer> features = getFeatureCache(meanings); |
56 | 56 | |
57 | 57 | for (int i= window; ; i++) { |
58 | 58 | int from = i-window; |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/StoredMeaningFetcher.java |
— | — | @@ -1,11 +1,14 @@ |
2 | 2 | package de.brightbyte.wikiword.disambig; |
3 | 3 | |
| 4 | +import java.util.HashMap; |
4 | 5 | import java.util.List; |
| 6 | +import java.util.Map; |
5 | 7 | |
6 | 8 | import de.brightbyte.data.cursor.DataSet; |
7 | 9 | import de.brightbyte.io.Output; |
8 | 10 | import de.brightbyte.util.PersistenceException; |
9 | 11 | import de.brightbyte.wikiword.model.LocalConcept; |
| 12 | +import de.brightbyte.wikiword.model.TermReference; |
10 | 13 | import de.brightbyte.wikiword.store.LocalConceptStore; |
11 | 14 | import de.brightbyte.wikiword.store.WikiWordConceptStore.ConceptQuerySpec; |
12 | 15 | |
— | — | @@ -31,6 +34,18 @@ |
32 | 35 | return m.load(); |
33 | 36 | } |
34 | 37 | |
| 38 | + public <X extends TermReference> Map<X, List<? extends LocalConcept>> getMeanings(List<X> terms) throws PersistenceException { |
| 39 | + Map<X, List<? extends LocalConcept>> meanings = new HashMap<X, List<? extends LocalConcept>>(); |
| 40 | + |
| 41 | + for (X t: terms) { |
| 42 | + List<LocalConcept> m = getMeanings(t.getTerm()); |
| 43 | + if (m!=null && m.size()>0) meanings.put(t, m); |
| 44 | + } |
| 45 | + |
| 46 | + return meanings; |
| 47 | + } |
| 48 | + |
| 49 | + |
35 | 50 | public Output getTrace() { |
36 | 51 | return trace; |
37 | 52 | } |
— | — | @@ -42,5 +57,5 @@ |
43 | 58 | protected void trace(String msg) { |
44 | 59 | if (trace!=null) trace.println(msg); |
45 | 60 | } |
46 | | - |
| 61 | + |
47 | 62 | } |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/MeaningCache.java |
— | — | @@ -0,0 +1,107 @@ |
| 2 | +package de.brightbyte.wikiword.disambig; |
| 3 | + |
| 4 | +import java.util.ArrayList; |
| 5 | +import java.util.HashMap; |
| 6 | +import java.util.List; |
| 7 | +import java.util.Map; |
| 8 | + |
| 9 | +import de.brightbyte.util.PersistenceException; |
| 10 | +import de.brightbyte.wikiword.model.TermReference; |
| 11 | +import de.brightbyte.wikiword.model.WikiWordConcept; |
| 12 | + |
| 13 | +public class MeaningCache<C extends WikiWordConcept> implements MeaningFetcher<C> { |
| 14 | + |
| 15 | + protected static class Manager<C extends WikiWordConcept> { |
| 16 | + protected int maxDepth; |
| 17 | + |
| 18 | + protected MeaningFetcher<? extends C> root; |
| 19 | + protected List<MeaningCache<C>> stack; |
| 20 | + |
| 21 | + public Manager(MeaningFetcher<? extends C> root, int maxDepth) { |
| 22 | + this.stack = new ArrayList<MeaningCache<C>>(maxDepth+1); |
| 23 | + this.maxDepth = maxDepth; |
| 24 | + this.root = root; |
| 25 | + } |
| 26 | + |
| 27 | + private MeaningFetcher<? extends C> getTop() { |
| 28 | + if (stack.isEmpty()) return root; |
| 29 | + else return stack.get(stack.size()-1); |
| 30 | + } |
| 31 | + |
| 32 | + public synchronized MeaningCache<C> newCache() { |
| 33 | + MeaningCache<C> cache = new MeaningCache<C>( getTop() ); |
| 34 | + stack.add(cache); |
| 35 | + |
| 36 | + if (stack.size()>maxDepth) stack.remove(0); |
| 37 | + if (!stack.isEmpty()) stack.get(0).setParent(root); |
| 38 | + |
| 39 | + return cache; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + protected MeaningFetcher<C> parent; |
| 44 | + |
| 45 | + protected Map<String, List<? extends C>> cache; |
| 46 | + |
| 47 | + public MeaningCache(MeaningFetcher<? extends C> parent) { |
| 48 | + if (parent==null) throw new NullPointerException(); |
| 49 | + this.setParent(parent); |
| 50 | + this.cache = new HashMap<String, List<? extends C>>(); |
| 51 | + } |
| 52 | + |
| 53 | + |
| 54 | + public MeaningFetcher<? extends C> getParent() { |
| 55 | + return parent; |
| 56 | + } |
| 57 | + |
| 58 | + public void setParent(MeaningFetcher<? extends C> parent) { |
| 59 | + if (parent == null) throw new NullPointerException(); |
| 60 | + if (parent == this) throw new IllegalArgumentException("can't be my own parent"); |
| 61 | + //TODO: prevent cycles |
| 62 | + |
| 63 | + this.parent = (MeaningFetcher<C>)(Object)parent; //XXX: ugly scast. generics are a pain. |
| 64 | + } |
| 65 | + |
| 66 | + public void clear() { |
| 67 | + cache.clear(); |
| 68 | + } |
| 69 | + |
| 70 | + |
| 71 | + public List<? extends C> getMeanings(String term) throws PersistenceException { |
| 72 | + List<? extends C> meanings = cache.get(term); |
| 73 | + |
| 74 | + if (meanings==null) { |
| 75 | + meanings = parent.getMeanings(term); |
| 76 | + cache.put(term, meanings); |
| 77 | + } |
| 78 | + |
| 79 | + return meanings; |
| 80 | + } |
| 81 | + |
| 82 | + |
| 83 | + public <X extends TermReference> Map<X, List<? extends C>> getMeanings(List<X> terms) throws PersistenceException { |
| 84 | + Map<X, List<? extends C>> meanings= new HashMap<X, List<? extends C>>(); |
| 85 | + List<X> todo = new ArrayList<X>(terms.size()); |
| 86 | + |
| 87 | + for (X t: terms) { |
| 88 | + List<? extends C> m = cache.get(t.getTerm()); |
| 89 | + if (m!=null) { |
| 90 | + meanings.put(t, m); |
| 91 | + continue; |
| 92 | + } else { |
| 93 | + todo.add(t); |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + Map<X, List<? extends C>> parentMeanings = parent.getMeanings(todo); //XXX: ugly cast, generics are a pain |
| 98 | + |
| 99 | + meanings.putAll(parentMeanings); |
| 100 | + |
| 101 | + for (Map.Entry<X, List<? extends C>> e: parentMeanings.entrySet()) { |
| 102 | + cache.put(e.getKey().getTerm(), e.getValue()); |
| 103 | + } |
| 104 | + |
| 105 | + return meanings; |
| 106 | + } |
| 107 | + |
| 108 | +} |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/PopularityDisambiguator.java |
— | — | @@ -42,7 +42,7 @@ |
43 | 43 | pop += Math.log(c.getCardinality()); |
44 | 44 | } |
45 | 45 | |
46 | | - pop = pop / disambig.size(); |
| 46 | + if (disambig.size()>0) pop = pop / disambig.size(); |
47 | 47 | |
48 | 48 | Result<X, LocalConcept> r = new Result<X, LocalConcept>(disambig, pop, "pop="+pop); |
49 | 49 | return r; |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/CoherenceDisambiguator.java |
— | — | @@ -9,6 +9,7 @@ |
10 | 10 | import java.util.Map.Entry; |
11 | 11 | |
12 | 12 | import de.brightbyte.data.Functor; |
| 13 | +import de.brightbyte.data.Functor2; |
13 | 14 | import de.brightbyte.data.LabeledMatrix; |
14 | 15 | import de.brightbyte.data.LabeledVector; |
15 | 16 | import de.brightbyte.data.MapLabeledMatrix; |
— | — | @@ -30,8 +31,9 @@ |
31 | 32 | protected double minScore = 0.1; //FIXME: magic number. should "somehow" match popularityFactor and similarityFactor |
32 | 33 | protected double popularityBias = 0.2; //FIXME: magic number. should "somehow" match popularityFactor and similarityFactor |
33 | 34 | |
| 35 | + protected FeatureCache.Manager<LocalConcept, Integer> featureCacheManager; |
| 36 | + |
34 | 37 | protected Similarity<LabeledVector<Integer>> similarityMeasure; |
35 | | - protected FeatureFetcher<LocalConcept, Integer> featureFetcher; |
36 | 38 | protected Measure<WikiWordConcept> popularityMeasure; |
37 | 39 | protected PopularityDisambiguator popularityDisambiguator; |
38 | 40 | |
— | — | @@ -48,7 +50,16 @@ |
49 | 51 | return Math.sqrt(Math.sqrt(sim)); //XXX: black voodoo magic ad hoc formula with no deeper meaing. |
50 | 52 | } |
51 | 53 | }; |
| 54 | + |
| 55 | + private Functor2.Double scoreCombiner = new Functor2.Double() { //NOTE: must map ([0:1][0:1]) to [0:1] and grow monotonously over both params. |
| 56 | + |
| 57 | + public double apply(double popf, double simf) { |
| 58 | + return popf * popularityBias + simf * ( 1 - popularityBias ); //linear combination |
| 59 | + //return = Math.sqrt( popf * simf ); //normalized produkt |
| 60 | + } |
52 | 61 | |
| 62 | + }; |
| 63 | + |
53 | 64 | public CoherenceDisambiguator(MeaningFetcher<LocalConcept> meaningFetcher, FeatureFetcher<LocalConcept, Integer> featureFetcher, boolean featuresAreNormalized) { |
54 | 65 | this(meaningFetcher, featureFetcher, WikiWordConcept.theCardinality, |
55 | 66 | featuresAreNormalized ? ScalarVectorSimilarity.<Integer>getInstance() : CosineVectorSimilarity.<Integer>getInstance()); //if pre-normalized, use scalar to calc cosin |
— | — | @@ -62,16 +73,12 @@ |
63 | 74 | if (featureFetcher==null) throw new NullPointerException(); |
64 | 75 | this.popularityMeasure = popularityMeasure; |
65 | 76 | this.similarityMeasure = sim; |
66 | | - this.featureFetcher = featureFetcher; |
| 77 | + this.featureCacheManager = new FeatureCache.Manager<LocalConcept, Integer>(featureFetcher, 10); //TODO: depth |
67 | 78 | this.popularityDisambiguator = new PopularityDisambiguator(meaningFetcher, popularityMeasure); |
68 | 79 | } |
69 | 80 | |
70 | | - public FeatureFetcher getFeatureFetcher() { |
71 | | - return featureFetcher; |
72 | | - } |
73 | | - |
74 | 81 | public void setFeatureFetcher(FeatureFetcher<LocalConcept, Integer> featureFetcher) { |
75 | | - this.featureFetcher = featureFetcher; |
| 82 | + this.featureCacheManager = new FeatureCache.Manager<LocalConcept, Integer>(featureFetcher, 10); //FIXME: depth |
76 | 83 | } |
77 | 84 | |
78 | 85 | public Similarity<LabeledVector<Integer>> getSimilarityMeasure() { |
— | — | @@ -116,9 +123,8 @@ |
117 | 124 | this.maxMeanings = maxMeanings; |
118 | 125 | } |
119 | 126 | |
120 | | - protected FeatureCache<LocalConcept, Integer> getFeatureCache(Map<? extends TermReference, List<? extends LocalConcept>> meanings) throws PersistenceException { |
121 | | - //TODO: keep a chain of n caches, resulting in LRU logic. |
122 | | - FeatureCache<LocalConcept, Integer> features = new FeatureCache<LocalConcept, Integer>(featureFetcher); |
| 127 | + protected FeatureFetcher<LocalConcept, Integer> getFeatureCache(Map<? extends TermReference, List<? extends LocalConcept>> meanings) throws PersistenceException { |
| 128 | + FeatureFetcher<LocalConcept, Integer> features = featureCacheManager.newCache(); |
123 | 129 | |
124 | 130 | //NOTE: pre-fetch all features in one go |
125 | 131 | List<LocalConcept> concepts = new ArrayList<LocalConcept>(meanings.size()*10); |
— | — | @@ -146,7 +152,7 @@ |
147 | 153 | //CAVEAT: because the map disambig can contain only one meaning per term, the same term can not occur with two meanings within the same term sequence. |
148 | 154 | |
149 | 155 | LabeledMatrix<LocalConcept, LocalConcept> similarities = new MapLabeledMatrix<LocalConcept, LocalConcept>(true); |
150 | | - FeatureCache<LocalConcept, Integer> features = getFeatureCache(meanings); |
| 156 | + FeatureFetcher<LocalConcept, Integer> features = getFeatureCache(meanings); |
151 | 157 | |
152 | 158 | List<Map<X, LocalConcept>> interpretations = getInterpretations(terms, meanings); |
153 | 159 | |
— | — | @@ -182,7 +188,7 @@ |
183 | 189 | |
184 | 190 | protected <X extends TermReference>Result<X, LocalConcept> getBestInterpretation(List<X> terms, Map<X, List<? extends LocalConcept>> meanings, |
185 | 191 | List<Map<X, LocalConcept>> interpretations, |
186 | | - LabeledMatrix<LocalConcept, LocalConcept> similarities, FeatureCache<LocalConcept, Integer> features) throws PersistenceException { |
| 192 | + LabeledMatrix<LocalConcept, LocalConcept> similarities, FeatureFetcher<LocalConcept, Integer> features) throws PersistenceException { |
187 | 193 | |
188 | 194 | List<Result<X, LocalConcept>> rankings = new ArrayList<Result<X, LocalConcept>>(); |
189 | 195 | |
— | — | @@ -238,7 +244,7 @@ |
239 | 245 | return interpretations; |
240 | 246 | } |
241 | 247 | |
242 | | - protected <X extends TermReference>Result<X, LocalConcept> getScore(Map<X, LocalConcept> interp, LabeledMatrix<LocalConcept, LocalConcept> similarities, FeatureCache<LocalConcept, Integer> features) throws PersistenceException { |
| 248 | + protected <X extends TermReference>Result<X, LocalConcept> getScore(Map<X, LocalConcept> interp, LabeledMatrix<LocalConcept, LocalConcept> similarities, FeatureFetcher<LocalConcept, Integer> features) throws PersistenceException { |
243 | 249 | double sim = 0; |
244 | 250 | double pop = 0; |
245 | 251 | |
— | — | @@ -293,9 +299,7 @@ |
294 | 300 | double popf = popularityFactor.apply(pop); |
295 | 301 | double simf = similarityFactor.apply(sim); |
296 | 302 | |
297 | | - //FIXME: functor! |
298 | | - double score = popf * popularityBias + simf * ( 1 - popularityBias ); |
299 | | - //double score = Math.sqrt( popf * simf ); //FIXME: functor! |
| 303 | + double score = scoreCombiner.apply(popf, simf); |
300 | 304 | |
301 | 305 | return new Result<X, LocalConcept>(interp, score, "simf="+simf+", popf="+popf+", sim="+sim+", pop="+pop); |
302 | 306 | } |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/disambig/AbstractDisambiguator.java |
— | — | @@ -1,6 +1,5 @@ |
2 | 2 | package de.brightbyte.wikiword.disambig; |
3 | 3 | |
4 | | -import java.util.HashMap; |
5 | 4 | import java.util.List; |
6 | 5 | import java.util.Map; |
7 | 6 | |
— | — | @@ -11,27 +10,18 @@ |
12 | 11 | |
13 | 12 | public abstract class AbstractDisambiguator<T extends TermReference, C extends WikiWordConcept> implements Disambiguator<T, C> { |
14 | 13 | |
15 | | - protected MeaningFetcher<? extends C> meaningFetcher; |
| 14 | + protected MeaningCache.Manager<C> meaningCacheManager; |
| 15 | + |
16 | 16 | protected Output trace; |
17 | 17 | |
18 | 18 | public AbstractDisambiguator(MeaningFetcher<? extends C> meaningFetcher) { |
19 | 19 | if (meaningFetcher==null) throw new NullPointerException(); |
20 | | - this.meaningFetcher = meaningFetcher; |
| 20 | + this.meaningCacheManager = new MeaningCache.Manager<C>(meaningFetcher, 10); |
21 | 21 | } |
22 | 22 | |
23 | | - protected <X extends T>Map<X, List<? extends C>> fetchMeanings(List<X> terms) throws PersistenceException { |
24 | | - Map<X, List<? extends C>> meanings = new HashMap<X, List<? extends C>>(); |
25 | | - |
26 | | - for (X t: terms) { |
27 | | - List<? extends C> m = meaningFetcher.getMeanings(t.getTerm()); |
28 | | - if (m!=null && m.size()>0) meanings.put(t, m); |
29 | | - } |
30 | | - |
31 | | - return meanings; |
32 | | - } |
33 | | - |
34 | 23 | public <X extends T>Result<X, C> disambiguate(List<X> terms) throws PersistenceException { |
35 | | - Map<X, List<? extends C>> meanings = fetchMeanings(terms); |
| 24 | + MeaningCache<C> mcache = meaningCacheManager.newCache(); |
| 25 | + Map<X, List<? extends C>> meanings = mcache.getMeanings(terms); |
36 | 26 | return disambiguate(terms, meanings); |
37 | 27 | } |
38 | 28 | |
Index: trunk/WikiWord/WikiWord/src/main/java/de/brightbyte/wikiword/model/PhraseOccuranceSequence.java |
— | — | @@ -70,7 +70,7 @@ |
71 | 71 | |
72 | 72 | for (PhraseOccurance p: candidates) { |
73 | 73 | i = p.getEndOffset(); |
74 | | - if (filter.matches(p.getPhrase())) { |
| 74 | + if (filter==null || filter.matches(p.getPhrase())) { |
75 | 75 | phrases.add(p); |
76 | 76 | continue outer; |
77 | 77 | } |
Index: trunk/WikiWord/WikiWordBuilder/src/main/java/de/brightbyte/wikiword/extract/WordSenseIndexer.java |
— | — | @@ -14,7 +14,6 @@ |
15 | 15 | import de.brightbyte.wikiword.disambig.SlidingCoherenceDisambiguator; |
16 | 16 | import de.brightbyte.wikiword.disambig.StoredFeatureFetcher; |
17 | 17 | import de.brightbyte.wikiword.disambig.StoredMeaningFetcher; |
18 | | -import de.brightbyte.wikiword.disambig.Disambiguator.Result; |
19 | 18 | import de.brightbyte.wikiword.model.LocalConcept; |
20 | 19 | import de.brightbyte.wikiword.model.PhraseOccurance; |
21 | 20 | import de.brightbyte.wikiword.model.PhraseOccuranceSequence; |
— | — | @@ -29,8 +28,8 @@ |
30 | 29 | protected PlainTextAnalyzer analyzer; |
31 | 30 | private int phraseLength; |
32 | 31 | |
33 | | - public WordSenseIndexer(boolean allowGlobal, boolean allowLocal) { |
34 | | - super(allowGlobal, allowLocal); |
| 32 | + public WordSenseIndexer() { |
| 33 | + super(false, true); |
35 | 34 | } |
36 | 35 | |
37 | 36 | @Override |
— | — | @@ -63,6 +62,7 @@ |
64 | 63 | disambiguator = new SlidingCoherenceDisambiguator( meaningFetcher, featureFetcher, true ); |
65 | 64 | |
66 | 65 | analyzer = PlainTextAnalyzer.getPlainTextAnalyzer(getCorpus(), tweaks); |
| 66 | + analyzer.initialize(); |
67 | 67 | |
68 | 68 | phraseLength = args.getIntOption("phrase-length", tweaks.getTweak("wikiSenseIndexer.phraseLength", 6)); |
69 | 69 | } |
— | — | @@ -75,4 +75,9 @@ |
76 | 76 | return result.toString(); //FIXME: annotate! |
77 | 77 | } |
78 | 78 | |
| 79 | + public static void main(String[] argv) throws Exception { |
| 80 | + WordSenseIndexer q = new WordSenseIndexer(); |
| 81 | + q.launch(argv); |
| 82 | + } |
| 83 | + |
79 | 84 | } |