PLearn 0.1
WordNetSenseDictionary.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // WordNetSenseDictionary.cc
00004 //
00005 // Copyright (C) 2004 Hugo Larochelle, Christopher Kermorvant
00006 // 
00007 // Redistribution and use in source and binary forms, with or without
00008 // modification, are permitted provided that the following conditions are met:
00009 // 
00010 //  1. Redistributions of source code must retain the above copyright
00011 //     notice, this list of conditions and the following disclaimer.
00012 // 
00013 //  2. Redistributions in binary form must reproduce the above copyright
00014 //     notice, this list of conditions and the following disclaimer in the
00015 //     documentation and/or other materials provided with the distribution.
00016 // 
00017 //  3. The name of the authors may not be used to endorse or promote
00018 //     products derived from this software without specific prior written
00019 //     permission.
00020 // 
00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031 // 
00032 // This file is part of the PLearn library. For more information on the PLearn
00033 // library, go to the PLearn Web site at www.plearn.org
00034 
00035 /* *******************************************************      
00036  * $Id: WordNetSenseDictionary.cc 6338 2006-10-23 20:33:09Z larocheh $ 
00037  ******************************************************* */
00038 
00039 // Authors: Hugo Larochelle, Christopher Kermorvant
00040 
00044 #include "WordNetSenseDictionary.h"
00045 #include <plearn/base/stringutils.h>
00046 #include <plearn/base/lexical_cast.h>
00047 
00048 namespace PLearn {
00049 using namespace std;
00050 
00051 char* cstr(string& str)
00052 {
00053     char* cstr = new char[str.size() + 1];
00054     for (unsigned int i = 0; i < str.size(); i++)
00055         *(cstr + i) = str[i];
00056     cstr[str.size()] = '\0';
00057     return cstr;
00058 }
00059 
00061 string getSynsetKey(SynsetPtr ssp)
00062 {
00063     if(!ssp) PLERROR("In getSynsetKey(): SynsetPtr is NULL");
00064     string ssk = tostring(ssp->words[0]);
00065     for(int i=1; i<ssp->wcount && i<3; i++)
00066         ssk = ssk + "," + tostring(ssp->words[i]);
00067     ssk = ssk + ":" + tostring(ssp->pos) + "#" + tostring(ssp->hereiam);
00068     return ssk;
00069 }
00070 
00072 SynsetPtr getSynsetPtr(string synset_key)
00073 {
00074     vector<string> splits = split(synset_key,",:#");
00075     char* srch_str = cstr(splits[0]);
00076     string pos = tostring(splits[splits.size()-2]);
00077     int pos_int = getpos(&pos[0]);
00078     long hereiam = tolong(splits[splits.size()-1]);
00079     SynsetPtr ret = read_synset(pos_int,hereiam,srch_str);
00080     delete(srch_str);
00081     return ret;
00082 }
00083 
00084 void extractSenses(string word, int wn_pos, string symbol_type, TVec<string>& senses,TVec<int>& tagcnts, TVec< TVec<string> >& ancestors, bool extract_ancestors)
00085 {
00086     char* cword = cstr(word);
00087     SynsetPtr ssp = NULL;
00088     IndexPtr idx = getindex(cword, wn_pos);
00089     ssp = findtheinfo_ds(cword, wn_pos, -HYPERPTR, ALLSENSES);
00090     
00091     if (ssp == NULL) 
00092     {
00093         if(idx) free_index(idx);
00094         delete cword;
00095         return;
00096     }
00097     
00098     int wnsn = 0;
00099     SynsetPtr head = ssp;
00100     // extract all senses for a given word
00101     while (ssp != NULL)
00102     {
00103         ++wnsn;
00104         if(symbol_type == "sense_key")
00105         {
00106             char *charsk = WNSnsToStr(idx, wnsn);
00107             senses.push_back(string(charsk));
00108             delete charsk;
00109         }
00110         else if(symbol_type == "synset_key")
00111         {
00112             senses.push_back(getSynsetKey(ssp));
00113         }
00114         else PLERROR("In extractSenses(): symbol_type %s not valid", symbol_type.c_str());
00115         if(extract_ancestors)
00116         {
00117             TVec< TVec<string> > this_anc_str;
00118             TVec< SynsetPtr > beg_anc(1);
00119             beg_anc[0] = ssp;
00120             if(wn_pos == NOUN)
00121                 extractAncestors(beg_anc,this_anc_str, WN_NOUN_NODE);
00122             else if(wn_pos == VERB)
00123                 extractAncestors(beg_anc,this_anc_str, WN_VERB_NODE);
00124             else if(wn_pos == ADJ)
00125                 extractAncestors(beg_anc,this_anc_str, WN_ADJ_NODE);
00126             else if(wn_pos == ADV)
00127                 extractAncestors(beg_anc,this_anc_str, WN_ADV_NODE);
00128             else
00129                 PLERROR("In extractSenses(): wn_pos %d invalid", wn_pos);
00130             ancestors.append(this_anc_str);
00131         }
00132         tagcnts.push_back(GetTagcnt(idx,wnsn));
00133         ssp = ssp->nextss;
00134     }
00135     free_syns(head);
00136     free_index(idx);
00137     delete cword;
00138 }
00139 
00140 void extractAncestors(TVec<SynsetPtr> anc, TVec< TVec<string> >& anc_str, string root_node)
00141 {
00142     SynsetPtr a;
00143     SynsetPtr extra_a;
00144     int i=0;
00145     anc_str.resize(anc.length());
00146     for(int k=0; k<anc_str.length(); k++)
00147         anc_str[k].resize(0);
00148 
00149     while(i<anc.length())
00150     {
00151         // Add sense
00152         a = anc[i];
00153         anc_str[i].push_back(getSynsetKey(a));
00154         // Go through one level and start looking for paths
00155         a = a->ptrlist; 
00156         while(a != NULL)
00157         {
00158             extra_a = a->nextss;
00159             while(extra_a != NULL)
00160             {
00161                 anc.push_back(extra_a);
00162                 anc_str.push_back(anc_str[i].copy());
00163                 extra_a = extra_a->nextss;
00164             }
00165             anc_str[i].push_back(getSynsetKey(a));
00166             a = a->ptrlist; 
00167         }
00168         anc_str[i].push_back(root_node);
00169         i++;
00170     }
00171 }
00172     
00173 string stemWord(string word)
00174 {
00175     char* input_word = cstr(word);
00176     char* lemma = morphword(input_word, NOUN);
00177     if (lemma == NULL)
00178     {
00179         lemma = morphword(input_word, VERB);
00180         if (lemma == NULL)
00181         {
00182             lemma = morphword(input_word, ADJ);
00183             if (lemma == NULL)
00184             {
00185                 lemma = morphword(input_word, ADV);
00186             }
00187         }
00188     }
00189  
00190     delete input_word;
00191     if (lemma == NULL) return word;
00192     else return removeblanks(tostring(lemma));
00193 }
00194 
00195 string stemWord(string word, int wn_pos)
00196 {
00197     char* input_word = cstr(word);
00198     char* lemma = morphword(input_word, wn_pos);
00199     delete input_word;
00200     if (lemma == NULL)
00201         return word;
00202     else
00203         return removeblanks(tostring(lemma));
00204 }
00205 
00206 void stemsOfWord(string word, int wn_pos, TVec<string>& stems)
00207 {
00208     stems.resize(0);
00209     stems.push_back(word);
00210     char* input_word = cstr(word);
00211     char* lemma = morphstr(input_word, wn_pos);
00212     string lemma_str;
00213     while(lemma)
00214     {
00215         lemma_str = removeblanks(tostring(lemma));
00216         if(lemma_str != word)
00217             stems.push_back(lemma_str);
00218         lemma = morphstr(NULL, wn_pos);
00219     }
00220     delete input_word;
00221 }
00222 
00223 void stemsOfWord(string word, TVec<string>& stems)
00224 {
00225     stems.resize(0);
00226     stems.push_back(word);
00227     char* input_word = cstr(word);
00228 
00229     char* lemma = morphstr(input_word, NOUN);
00230     string lemma_str;
00231     while(lemma)
00232     {
00233         lemma_str = removeblanks(tostring(lemma));
00234         if(lemma_str != word)
00235             stems.push_back(lemma_str);
00236         lemma = morphstr(NULL, NOUN);
00237     }
00238 
00239     lemma = morphstr(input_word, VERB);
00240     while(lemma)
00241     {
00242         lemma_str = removeblanks(tostring(lemma));
00243         if(lemma_str != word)
00244             stems.push_back(lemma_str);
00245         lemma = morphstr(NULL, VERB);
00246     }
00247 
00248     lemma = morphstr(input_word, ADJ);
00249     while(lemma)
00250     {
00251         lemma_str = removeblanks(tostring(lemma));
00252         if(lemma_str != word)
00253             stems.push_back(lemma_str);
00254         lemma = morphstr(NULL, ADJ);
00255     }
00256 
00257     lemma = morphstr(input_word, ADV);
00258     while(lemma)
00259     {
00260         lemma_str = removeblanks(tostring(lemma));
00261         if(lemma_str != word)
00262             stems.push_back(lemma_str);
00263         lemma = morphstr(NULL, ADV);
00264     }
00265 
00266     delete input_word;
00267 }
00268 
00269 
00270 WordNetSenseDictionary::WordNetSenseDictionary()    
00271     : options_stem_words(false), options_to_lower_case(false), symbol_type("sense_key"), use_wordnet_hierarchy(false)
00272 {
00273     if(wninit()<0)
00274         PLERROR("In WordNetSenseDictionary(): could not open WordNet database files");
00275 }
00276   
00277 PLEARN_IMPLEMENT_OBJECT(WordNetSenseDictionary,
00278                         "Dictionary of WordNet senses",
00279                         "The symbols in the instantiated dictionary are senses (not words!).\n");
00280   
00281 void WordNetSenseDictionary::declareOptions(OptionList& ol)
00282 {
00283     declareOption(ol, "options_stem_words", &WordNetSenseDictionary::options_stem_words, OptionBase::buildoption, "Indication that words given as options should be stemmed when looking at their possible senses");
00284     declareOption(ol, "options_to_lower_case", &WordNetSenseDictionary::options_to_lower_case, OptionBase::buildoption, "Indication that words given as options should be put to lower case");
00285     declareOption(ol, "symbol_type", &WordNetSenseDictionary::symbol_type, OptionBase::buildoption, "Type of representation (symbol) of the senses. The currently supported types are:\n"
00286         "   - \"sense_key\" (standard WordNet representation)\n"
00287         "   - \"synset_key\" (custom made representation, that consists of maximum three words from the synset, the POS category and the byte offset of the synset)\n"
00288         "Note that the Dictionary will always accept \"sense_key\" symbols as input, but will convert it automatically to\n"
00289         "the symbol_type representation and treat it as such.");
00290     declareOption(ol, "possible_values_for_word", &WordNetSenseDictionary::possible_values_for_word, OptionBase::learntoption, "Mapping of the possible id values for a given word");
00291     declareOption(ol, "use_wordnet_hierarchy", &WordNetSenseDictionary::use_wordnet_hierarchy, OptionBase::buildoption, "Indication that the WordNet hierarchy will be used");
00292     declareOption(ol, "children", &WordNetSenseDictionary::children, OptionBase::learntoption, "Synset children mapping");
00293     declareOption(ol, "parents", &WordNetSenseDictionary::parents, OptionBase::learntoption, "Synset parents mapping");
00294     declareOption(ol, "sense_prior", &WordNetSenseDictionary::sense_prior, OptionBase::learntoption, "Prior distribution for p(sense|word) given by WordNet");
00295 
00296     inherited::declareOptions(ol);
00297 }
00298   
00299 void WordNetSenseDictionary::build_()
00300 {
00301     if(use_wordnet_hierarchy && symbol_type == "sense_key")
00302         PLERROR("In WordNetSenseDictionary::build_(): cannot use WordNet hierarchy with symbol type \"sense_key\"");
00303 }
00304 
00305 // ### Nothing to add here, simply calls build_
00306 void WordNetSenseDictionary::build()
00307 {
00308     inherited::build();
00309     build_();
00310 }
00311 
00312 void WordNetSenseDictionary::getSensesFromWordNet(TVec<string> options)
00313 {
00314     string word = "";
00315     stems.resize(0);
00316     int wn_pos = -1;
00317     senses.resize(0);
00318     ancestors_vec.resize(0);
00319     tagcnts.resize(0);
00320 
00321     word = removeblanks(options[0]);
00322     // This may be unnecessary, given "options_stem_words"
00323     if(options_to_lower_case) word = lowerstring(word);    
00324     
00325     if(wn_pos < 0)
00326     {
00327         // Extract senses for all possible POS
00328 
00329         // NOUN
00330         extractSenses(word,NOUN,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00331         if(options_stem_words) 
00332         {
00333             stemsOfWord(word,NOUN,stems);
00334             for(int i=0; i<stems.length(); i++)
00335                 if(word != stems[i])
00336                     extractSenses(stems[i],NOUN,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00337         }
00338 
00339         // VERB
00340         extractSenses(word,VERB,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00341         if(options_stem_words) 
00342         {
00343             stemsOfWord(word,VERB,stems);
00344             for(int i=0; i<stems.length(); i++)
00345                 if(word != stems[i])
00346                     extractSenses(stems[i],VERB,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00347         }
00348        
00349         // ADJ
00350         extractSenses(word,ADJ,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00351         if(options_stem_words) 
00352         {
00353             stemsOfWord(word,ADJ,stems);
00354             for(int i=0; i<stems.length(); i++)
00355                 if(word != stems[i])
00356                     extractSenses(stems[i],ADJ,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00357         }
00358        
00359         // ADV
00360         extractSenses(word,ADV,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00361         if(options_stem_words) 
00362         {
00363             stemsOfWord(word,ADV,stems);
00364             for(int i=0; i<stems.length(); i++)
00365                 if(word != stems[i])
00366                     extractSenses(stems[i],ADV,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00367         }
00368 
00369         if(tagcnts.length()>0)
00370         {
00371             word_sense = word + " " + senses[0];
00372             if(sense_prior.find(word_sense) == sense_prior.end())
00373             {
00374                 int sum = 0;
00375                 for(int i=0; i<tagcnts.length(); i++)
00376                     sum += tagcnts[i];
00377                 for(int i=0; i<tagcnts.length();i++)
00378                 {
00379                     word_sense = word + " " + senses[i];
00380                     sense_prior[word_sense] = ((real)tagcnts[i])/sum;
00381                 }
00382             }
00383         }
00384     }
00385     else
00386     {
00387         // Extract senses only for wn_pos
00388         extractSenses(word,wn_pos,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00389         if(options_stem_words) 
00390         {
00391             stemsOfWord(word,wn_pos,stems);
00392             for(int i=0; i<stems.length(); i++)
00393                 if(word != stems[i])
00394                     extractSenses(stems[i],wn_pos,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy);
00395         }  
00396 
00397         PLERROR("In  WordNetSenseDictionary::getSensesFromWordNet(): producing sense_prior not implemented yet");
00398     }
00399 
00400     if(use_wordnet_hierarchy)
00401     {
00402         int n1,n2;
00403         string str1,str2;
00404         for(int i=0; i<ancestors_vec.length(); i++)
00405         {            
00406             for(int j=0; j<ancestors_vec[i].length(); j++)
00407             {                
00408                 if(j != ancestors_vec[i].length()-1)
00409                 {
00410                     str1 = ancestors_vec[i][j];
00411                     str2 = ancestors_vec[i][j+1];
00412                 }
00413                 else
00414                 {
00415                     str1 = ancestors_vec[i].last();
00416                     str2 = WN_ROOT_NODE;
00417                 }
00418                 n1 = inherited::getId(str1);
00419                 n2 = inherited::getId(str2);
00420                 
00421                 if(!isIn(str1) || !isIn(str2))
00422                     continue;
00423                 
00424                 if(parents.find(n1) == parents.end())
00425                 {
00426                     TVec<int> p(1);
00427                     p[0] = n2;
00428                     parents[n1] = p.copy();
00429                 }
00430                 else
00431                 {
00432                     if(parents[n1].find(n2)>=0)
00433                         parents[n1].push_back(n2);
00434                 }
00435                 if(children.find(n2) == children.end())
00436                 {
00437                     TVec<int> c(1);
00438                     c[0] = n1;
00439                     children[n2] = c.copy();
00440                 }
00441                 else
00442                 {
00443                     if(children[n2].find(n1)>=0)
00444                         children[n2].push_back(n1);
00445                 }
00446             }                        
00447         }
00448     }
00449 }
00450 
00451 int WordNetSenseDictionary::getId(string symbol, TVec<string> options)
00452 {
00453     // This permits converting an input symbol as sense_key
00454     // to some other symbol_type...
00455     if(strchr(symbol.c_str(),'%') && symbol_type != "sense_key")
00456     {
00457         if(symbol_type == "synset_key")
00458         {
00459             char* symbol_cstr = cstr(symbol);
00460             symbol = getSynsetKey(GetSynsetForSense(symbol_cstr));
00461             delete symbol_cstr;
00462         }
00463         else PLERROR("In getId(): symbol_type %s not valid", symbol_type.c_str());
00464     }
00465 
00466     // Convert WordNet sense number to sense symbol
00467     if(options.length() == 2 && pl_isnumber(symbol))
00468     {
00469         char* cword = cstr(options[0]);
00470         int wn_pos = -1;
00471         if(strstr(options[1].c_str(),"NN")) wn_pos = NOUN;
00472         if(strstr(options[1].c_str(),"VB")) wn_pos = VERB;
00473         if(strstr(options[1].c_str(),"JJ")) wn_pos = ADJ;
00474         if(strstr(options[1].c_str(),"RB")) wn_pos = ADV;
00475         if (wn_pos < 0) 
00476             PLERROR("In WordNetSenseDictionary::extractSenses(): %s is an invalid POS", options[1].c_str());
00477 
00478         SynsetPtr ssp = NULL;
00479         IndexPtr idx = getindex(cword, wn_pos);
00480         ssp = findtheinfo_ds(cword, wn_pos, -HYPERPTR, toint(symbol));
00481         
00482         if (ssp == NULL) 
00483             PLERROR("In WordNetSenseDictionary::extractSenses(): cannot find sense with number %s for word %s of POS %s", symbol.c_str(), options[0].c_str(), options[1].c_str());
00484         
00485         if(symbol_type == "sense_key")
00486         {
00487             char *charsk = WNSnsToStr(idx, toint(symbol));
00488             symbol = string(charsk);
00489             delete charsk;
00490         }
00491         else if(symbol_type == "synset_key")
00492         {
00493             symbol = getSynsetKey(ssp);
00494         }
00495         else PLERROR("In extractSenses(): symbol_type %s not valid", symbol_type.c_str());
00496         if(ssp->nextss != NULL)
00497             PLERROR("In extractSenses(): more than one possible sense for sense number %s of word %s of POS %s", symbol.c_str(), options[0].c_str(), options[1].c_str());
00498         free_syns(ssp);
00499         free_index(idx);
00500         delete cword;
00501     }
00502 
00503     ret = inherited::getId(symbol,options);
00504 
00505     // call getValues, which fills possible_values and gets the ids for all those senses, to do a compatibility check, if necessary
00506     if(update_mode == UPDATE && options.length() != 0)
00507     {
00508         getValues(options, possible_values);
00509         //getSensesFromWordNet(options); 
00510         if(possible_values.find(ret) < 0)
00511             PLWARNING("In WordNetSenseDictionary::getId(): sense %s is not among possible symbols",symbol.c_str());
00512     }
00513 
00514     /*
00515     // Extracting new senses. If dictionary can be updated and 
00516     // if symbol_type == "synset_key", then need to be sure that all 
00517     // synsets were inserted, even if the current synset is !!!
00518     if(update_mode == UPDATE && (symbol_type == "synset_key" || !isIn(symbol,options)) )
00519         for(int i=0; i<senses.length(); i++)
00520             inherited::getId(senses[i],options);         
00521     */
00522 
00523     return ret;
00524 }
00525 
00526 void WordNetSenseDictionary::getValues(TVec<string> options, Vec& values)
00527 { 
00528     if(options.length() == 0)
00529         return inherited::getValues(options,values);
00530     else
00531     {
00532         //refill_possible_values = 1;
00533         //if(options.length() > 1) PLERROR("In WordNetSenseDictionary::getSensesFromWordNet(): options.length()>1 not supported");
00534         if(possible_values_for_word.find(options[0]) == possible_values_for_word.end())
00535         {
00536             getSensesFromWordNet(options);
00537             values.resize(senses.length());
00538             for(int i=0; i<senses.length(); i++)
00539                 values[i] = inherited::getId(senses[i]);
00540             possible_values_for_word[options[0]] = values.copy();
00541         }
00542         else
00543         {
00544             values.resize(possible_values_for_word[options[0]].length());
00545             values << possible_values_for_word[options[0]];            
00546         }
00547     }
00548 }
00549 
00550 int WordNetSenseDictionary::size(TVec<string> options){
00551     if(options.length() == 0)
00552         return inherited::size();
00553     else       
00554     {
00555         getValues(options,possible_values);
00556         return possible_values.length();            
00557     }
00558 }
00559 
00560 void WordNetSenseDictionary::parentsOf(int sense, TVec<int>& the_parents){
00561     if(parents.find(sense) == parents.end())
00562         the_parents.resize(0);
00563     else
00564     {
00565         the_parents.resize(parents[sense].length());
00566         the_parents << parents[sense];
00567     }
00568 }
00569 
00570 void WordNetSenseDictionary::childrenOf(int sense, TVec<int>& the_children){
00571     if(children.find(sense) == children.end())
00572         the_children.resize(0);
00573     else
00574     {
00575         the_children.resize(children[sense].length());
00576         the_children << children[sense];
00577     }
00578 }
00579 
00580 int WordNetSenseDictionary::rootNode(){
00581     return inherited::getId(WN_ROOT_NODE);
00582 }
00583 
00584 real WordNetSenseDictionary::sensePrior(string word, string sense){
00585     word_sense = word + " " + sense;
00586     if(sense_prior.find(word_sense) == sense_prior.end())
00587         return 0;
00588     else
00589         return sense_prior[word_sense];
00590 }
00591 
00592 void WordNetSenseDictionary::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00593 {
00594     inherited::makeDeepCopyFromShallowCopy(copies);
00595     deepCopyField(possible_values, copies);
00596     deepCopyField(stems, copies);
00597     deepCopyField(senses, copies);
00598     deepCopyField(ancestors_vec, copies);
00599     deepCopyField(tagcnts, copies);
00600     deepCopyField(possible_values_for_word, copies);
00601     deepCopyField(children, copies);
00602     deepCopyField(parents, copies);
00603     deepCopyField(sense_prior, copies);
00604 }
00605 
00606 } // end of namespace PLearn
00607 
00608 
00609 /*
00610   Local Variables:
00611   mode:c++
00612   c-basic-offset:4
00613   c-file-style:"stroustrup"
00614   c-file-offsets:((innamespace . 0)(inline-open . 0))
00615   indent-tabs-mode:nil
00616   fill-column:79
00617   End:
00618 */
00619 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines