libeblearngui
/home/rex/ebltrunk/tools/libeblearngui/include/ebl_trainer_gui.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Yann LeCun and Pierre Sermanet *
00003  *   yann@cs.nyu.edu, pierre.sermanet@gmail.com *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source
00014  *       Initiative (http://www.opensource.org) must display the
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING 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 
00033 namespace ebl {
00034 
00036   // supervised_trainer_gui
00037 
00038   template <class Tnet, class Tdata, class Tlabel>  
00039   supervised_trainer_gui<Tnet, Tdata, Tlabel>::
00040   supervised_trainer_gui(const char *title_, bool scroll_)
00041     : _st(NULL), _ds(NULL), _last_ds(NULL), 
00042       datasource_wid(-1), datasource_wid2(-1), 
00043       datasource_wid3(-1), datasource_wid4(-1),
00044       internals_wid(-1), internals_wid2(-1), internals_wid3(-1),
00045       scroll(scroll_), scroll_added(false), pos(0), dsgui(NULL) {
00046     if (title_) {
00047       title0 = title_;
00048       title0 << ": ";
00049     }
00050   }
00051 
00052   template <class Tnet, class Tdata, class Tlabel>  
00053   supervised_trainer_gui<Tnet, Tdata, Tlabel>::~supervised_trainer_gui() {
00054     if (w) w->replace_scroll_box_with_copy(this);
00055   }
00056   
00057   template <class Tnet, class Tdata, class Tlabel>  
00058   void supervised_trainer_gui<Tnet, Tdata, Tlabel>::
00059   display_datasource(supervised_trainer<Tnet, Tdata, Tlabel> &st,
00060                      labeled_datasource<Tnet, Tdata, Tlabel> &ds,
00061                      infer_param &infp,
00062                      unsigned int nh, unsigned int nw, unsigned int h0, 
00063                      unsigned int w0, double zoom, int wid, const char *title,
00064                      bool scrolling) {
00065     // copy parameters
00066     _st = &st;
00067     _infp = &infp;
00068     _nh = nh;
00069     _nw = nw;
00070     _h0 = h0;
00071     _w0 = w0;
00072     if (!dsgui)
00073       dsgui = new labeled_datasource_gui<Tnet, Tdata, Tlabel>(false);
00074     // do a deep copy of dataset only when necessary
00075     if (scroll && !scrolling && (_last_ds != &ds)) {
00076       if (_ds)
00077         delete _ds;
00078       _ds = new labeled_datasource<Tnet, Tdata, Tlabel>(ds);
00079       dsgui->_ds = _ds;
00080     }
00081     _last_ds = &ds;
00082     // init datasource
00083     st.init(ds);
00084     vector<string*> lblstr;
00085     class_datasource<Tnet,Tdata,Tlabel> *cds =
00086       dynamic_cast<class_datasource<Tnet,Tdata,Tlabel>*>(&ds);
00087     if (cds)
00088       lblstr = cds->get_label_strings();
00089     idxdim d = ds.sample_dims();
00090     _h1 = h0 + nh * (d.dim(0) + 1) * 3;
00091     _w1 = w0 + nw * (d.dim(1) + 1) * 3;
00092     _zoom = zoom;
00093     // if no window given, create a new one or reuse previous one
00094     datasource_wid = (wid >= 0) ? wid : 
00095       ((datasource_wid >= 0) ? datasource_wid :
00096        new_window((title ? title : "Supervised Trainer")));
00097     select_window(datasource_wid);
00098     if (scroll && !scroll_added) {
00099       gui.add_scroll_box((scroll_box*) this);
00100       scroll_added = true;
00101     }
00102     disable_window_updates();
00103     if (wid == -1) // clear only if we created the window
00104       clear_window();
00105     // top left coordinates of datasets display 1 and 2
00106     unsigned int w01 = nh * (d.dim(1) + 2) + 5, h01 = h0 + 35;
00107     unsigned int w02 = (nh * (d.dim(1) + 2) + 5) * 2, h02 = h0 + 35;
00108     // working variables
00109     unsigned int h1 = h01, w1 = w01, nh1 = 0;
00110     unsigned int h2 = h02, w2 = w02, i2 = 0;
00111     bool correct;
00112 
00113     // display top
00114     gui << set_colors(255, 0, 0, 255, 255, 255, 255, 127) << gui_only();
00115     gui << at(h0, w0) << ds.name();
00116     gui << black_on_white();
00117     gui << at(h0 + 17, w0) << "Groundtruth";
00118     gui << at(h0 + 17, w01) << "Correct & incorrect answers";
00119     gui << at(h0 + 17, w02) << "Incorrect only";
00120     gui << white_on_transparent();
00121 
00122     // 0. display dataset with groundtruth labels
00123     dsgui->display(ds, nh, nw, h0 + 35, w0, zoom, datasource_wid, NULL, true);
00124 
00125     // loop on nh * nw first samples
00126     gui << white_on_transparent();
00127     // loop to reach pos
00128     ds.seek_begin();
00129     for (unsigned int p = 0; p < pos; ++p)
00130       ds.next(); // FIXME add a seek(p) method to ds
00131     for (unsigned int i = 0; (i < ds.size()) && (i2 < nh * nw); ++i) {
00132       // test sample
00133       ds.fprop_label_net(*st.label);
00134       correct = st.test_sample(ds, *st.label, *st.answers, infp);
00135       ds.next();
00136       idx<Tnet> m = st.machine.in1.x.select(0, 0);
00137 
00138       // 1. display dataset with incorrect and correct answers
00139       if (nh1 < nh) {
00140         draw_matrix_frame(m, (correct?0:128), 0, 0, h1, w1, zoom, zoom);
00141         if ((lblstr.size() > 0) && (lblstr[(int)st.answers->x.gget()]))
00142           gui << at(h1 + 2, w1 + 2)
00143               << (lblstr[(int)st.answers->x.gget()])->c_str();
00144         w1 += d.dim(2) + 2;
00145         if (((i + 1) % nw == 0) && (i > 1)) {  
00146           w1 = w01;
00147           h1 += d.dim(1) + 2;
00148           nh1++;
00149         }
00150       }
00151       // 2. display first nh * nw incorrect answers
00152       if (!correct) {
00153         draw_matrix_frame(m, (correct?0:128), 0, 0, h2, w2, zoom, zoom);
00154         if ((lblstr.size() > 0) && (lblstr[(int)st.answers->x.gget()]))
00155           gui << at(h2 + 2, w2 + 2)
00156               << (lblstr[(int)st.answers->x.gget()])->c_str();
00157         w2 += d.dim(2) + 2;
00158         if (((i2 + 1) % nw == 0) && (i2 > 1)) {  
00159           w2 = w02;
00160           h2 += d.dim(1) + 2;
00161         }
00162         i2++;
00163       }
00164     }
00165     enable_window_updates();
00166   }
00167 
00168   template <class Tnet, class Tdata, class Tlabel>  
00169   void supervised_trainer_gui<Tnet, Tdata, Tlabel>::
00170   display_internals(supervised_trainer<Tnet, Tdata, Tlabel> &st,
00171                     labeled_datasource<Tnet, Tdata, Tlabel> &ds,
00172                     infer_param &infp, gd_param &args,
00173                     unsigned int ninternals, unsigned int display_h0, 
00174                     unsigned int display_w0, double display_zoom, 
00175                     int wid, const char *title) {
00176     if (ninternals == 0) return ;
00177     internals_wid = (wid >= 0) ? wid : 
00178       ((internals_wid >= 0) ? internals_wid :
00179        new_window((title ? 
00180                        title : "Supervised Trainer: internal fprop states")));
00181     internals_wid2 = (wid >= 0) ? wid : 
00182       ((internals_wid2 >= 0) ? internals_wid2 :
00183        new_window((title ? 
00184                        title : "Supervised Trainer: internal bprop states")));
00185     internals_wid3 = (wid >= 0) ? wid : 
00186       ((internals_wid3 >= 0) ? internals_wid3 :
00187        new_window((title ? 
00188                        title : "Supervised Trainer: internal bbprop states")));
00189     // freeze and clear display updates
00190     select_window(internals_wid);
00191     disable_window_updates();
00192     clear_window();
00193     select_window(internals_wid2);
00194     disable_window_updates();
00195     clear_window();
00196     select_window(internals_wid3);
00197     disable_window_updates();
00198     clear_window();
00199     // prepare dataset
00200     st.init(ds);
00201     unsigned int wfdisp = 0, hfdisp = 0;
00202     unsigned int wfdisp2 = 0, hfdisp2 = 0;
00203     unsigned int wfdisp3 = 0, hfdisp3 = 0;
00204     
00205     // display first ninternals samples
00206     trainable_module_gui mg;
00207     bbstate_idx<Tnet> input(ds.sample_dims());
00208     bbstate_idx<Tlabel> label(ds.label_dims());
00209     for (unsigned int i = 0; (i < ds.size()) && (i < ninternals); ++i) {
00210       // prepare input
00211       ds.fprop(input, label);
00212       // // fprop and bprop
00213       // //st.test_sample(*st.input, st.label, infp);
00214       // // TODO: display is influencing learning, remove influence
00215       // //      st.learn_sample(*st.input, st.label.get(), args);
00216       // st.machine.fprop(*st.input, st.label, st.energy);
00217       // st.param.clear_dx();
00218       // st.machine.bprop(*st.input, st.label, st.energy);
00219       // st.param.clear_ddx();
00220       // st.machine.bbprop(*st.input, st.label, st.energy);
00221       
00222       // display fprop
00223       mg.display_fprop(st.machine, input, label, st.energy, 
00224                        hfdisp, wfdisp, display_zoom, (Tnet) -1.0, (Tnet) 1.0,
00225                        true, internals_wid);
00226       // display bprop
00227       select_window(internals_wid2);
00228       mg.display_bprop(st.machine, input, label, st.energy, 
00229                        hfdisp2, wfdisp2, display_zoom, (Tnet) -1.0, (Tnet) 1.0,
00230                        true, internals_wid2);
00231       // display bprop
00232       select_window(internals_wid3);
00233       mg.display_bbprop(st.machine, input, label, st.energy, 
00234                         hfdisp3, wfdisp3, display_zoom, (Tnet) -.01, (Tnet) .01,
00235                         true, internals_wid3);
00236       ds.next(); // next sample
00237       hfdisp += 10;
00238       hfdisp2 += 10;
00239       hfdisp3 += 10;
00240     }
00241     // unfreeze display updates
00242     select_window(internals_wid);
00243     enable_window_updates();
00244     select_window(internals_wid2);
00245     enable_window_updates();
00246     select_window(internals_wid3);
00247     enable_window_updates();
00248   }  
00249 
00250   template <class Tnet, class Tdata, class Tlabel>  
00251   void supervised_trainer_gui<Tnet, Tdata, Tlabel>::
00252   display_correctness(bool incorrect, bool up,
00253                       supervised_trainer<Tnet, Tdata, Tlabel> &st,
00254                       labeled_datasource<Tnet, Tdata, Tlabel> &ds,
00255                       infer_param &infp,
00256                       unsigned int nh, unsigned int nw,
00257                       bool print_raw_outputs, bool draw_all_jitter,
00258                       unsigned int h0, unsigned int w0, double zoom, int wid,
00259                       const char *title, bool scrolling) {
00260     if (!ds.bkeep_outputs) 
00261       eblerror("answers are not kept in datasource, activate keeping by "
00262                << "setting keep_outputs = 1");
00263     // copy parameters
00264     _st = &st;
00265     _infp = &infp;
00266     _nh = nh;
00267     _nw = nw;
00268     _h0 = h0;
00269     _w0 = w0;
00270     if (!dsgui)
00271       dsgui = new labeled_datasource_gui<Tnet, Tdata, Tlabel>(false);
00272     // do a deep copy of dataset only when necessary
00273     if (scroll && !scrolling && (_last_ds != &ds)) {
00274       if (_ds)
00275         delete _ds;
00276       _ds = new labeled_datasource<Tnet, Tdata, Tlabel>(ds);
00277       dsgui->_ds = _ds;
00278     }
00279     _last_ds = &ds;
00280     // init datasource
00281     st.init(ds);
00282     class_datasource<Tnet,Tdata,Tlabel> *cds =
00283       dynamic_cast<class_datasource<Tnet,Tdata,Tlabel>*>(&ds);
00284     // find out sample size
00285     idxdim d = ds.sample_dims();
00286     _h1 = h0 + nh * (d.dim(0) + 1) * 3;
00287     _w1 = w0 + nw * (d.dim(1) + 1) * 3;
00288     _zoom = zoom;
00289     string corname = (incorrect ? "incorrect" : "correct");
00290     title1 = title0;
00291     title1 << "Incorrect classifications, lowest energies first (pid "
00292            << pid() << ")";
00293     title2 = title0;
00294     title2 << "Incorrect classifications, highest energies first (pid "
00295            << pid() << ")";
00296     title3 = title0;
00297     title3 << "Correct classifications, lowest energies first (pid "
00298            << pid() << ")";
00299     title4 = title0;
00300     title4 << "Correct classifications, highest energies first (pid "
00301            << pid() << ")";
00302     // if no window given, create a new one or reuse previous one
00303     if (incorrect) { // incorrect window
00304       if (up) {
00305         datasource_wid2 = (wid >= 0) ? wid : 
00306           ((datasource_wid2 >= 0) ? datasource_wid2 :
00307            new_window((title ? title : title1.c_str())));
00308         select_window(datasource_wid2);
00309       } else {
00310         datasource_wid3 = (wid >= 0) ? wid : 
00311           ((datasource_wid3 >= 0) ? datasource_wid3 :
00312            new_window((title ? title : title2.c_str())));
00313         select_window(datasource_wid3);
00314       }
00315     } else { // correct window
00316       if (up) {
00317         datasource_wid = (wid >= 0) ? wid : 
00318           ((datasource_wid >= 0) ? datasource_wid :
00319            new_window((title ? title : title3.c_str())));
00320         select_window(datasource_wid);
00321       } else {
00322         datasource_wid4 = (wid >= 0) ? wid : 
00323           ((datasource_wid4 >= 0) ? datasource_wid4 :
00324            new_window((title ? title : title4.c_str())));
00325         select_window(datasource_wid4);
00326       }
00327     }
00328     if (scroll && !scroll_added) {
00329       gui.add_scroll_box((scroll_box*) this);
00330       scroll_added = true;
00331     }
00332     disable_window_updates();
00333     if (wid == -1) // clear only if we created the window
00334       clear_window();
00335     // top left coordinates of datasets display 1 and 2
00336     unsigned int w01 = w0, h01 = h0 + 20;
00337     unsigned int h1 = h01, w1 = w01, nh1 = 0;
00338 
00339     // get the indices of incorrect or correct samples ordered by their energies
00340     // this assumes that all samples have already been tested for correctness
00341     intg tally = idx_sum<intg>(ds.correct);
00342     if (incorrect) tally = ds.count_included_samples() - tally;
00343     idx<intg> indices(tally);
00344     idx<double> energies(tally);
00345     intg inc = 0, i = 0;
00346     if(!incorrect) {
00347       idx_bloop2(correct, ds.correct, ubyte, energy, ds.energies, double) {
00348         if (ds.included_sample(i)) {
00349           if (correct.get() == !incorrect) { // found correct or incorrect sample
00350             indices.set(i, inc);
00351             energies.set(energy.get(), inc);
00352             inc++;
00353           }
00354         }
00355         i++; // data iterator
00356       }
00357     }
00358     else return; // TODO: fix bug for incorrect samples.
00359 
00360 //     if (inc != indices.dim(0))
00361 //       eblerror("expected " << indices.dim(0) << " " << corname
00362 //             << " samples but found " << inc);
00363 
00364     // display top
00365     gui << set_colors(255, 0, 0, 255, 255, 255, 255, 127) << gui_only();
00366     gui << at(h0, w0) << ds.name() << ": ";
00367     gui << black_on_white();
00368     if (inc == 0) {
00369       gui << "no " << corname << " samples." << endl;
00370       enable_window_updates();
00371       return ;
00372     }
00373     // TODO: energies becomes unrealistic garbage sometimes, gets fp exceptions
00374     // gui << indices.dim(0) << " / " << ds.correct.dim(0) << " "
00375     //     << corname << " samples with energies max: "
00376     //     << (double) idx_max(energies) << " min: " << (double) idx_min(energies);
00377     gui << white_on_transparent();
00378 
00379     // sort indices by energy
00380     if (up) idx_sortup(energies, indices);
00381     else idx_sortdown(energies, indices);
00382     
00383     // loop on nh * nw first samples
00384     Tlabel answer, bgid = -1;
00385     if (cds)
00386       cds->get_class_id("bg");
00387     bbstate_idx<Tnet> input(ds.sample_dims());
00388     mstate<bbstate_idx<Tnet> > minput;
00389     bbstate_idx<Tlabel> label(ds.label_dims());
00390     bbstate_idx<Tnet> jitt(1, 1);
00391     uint hmax = 0;
00392     for (unsigned int i = 0; (i < indices.dim(0)) && (i < nh * nw); ++i) {
00393       if (indices.get(i) >= ds.raw_outputs.dim(0)) break ;
00394       idx<Tnet> raw = ds.raw_outputs.select(0, indices.get(i));
00395       idx<Tnet> answers = ds.answers.select(0, indices.get(i));
00396       idx<Tnet> target = ds.targets.select(0, indices.get(i));
00397       answer = (Tlabel) answers.get(0);
00398       ds.select_sample(indices.get(i));
00399       minput.clear();
00400       if (ds.mstate_samples()) ds.fprop_data(minput);
00401       else {
00402         ds.fprop_data(input);
00403         minput.push_back_new(input);
00404       }
00405       ds.fprop_label(label);
00406       uint ht = 0, wt = 0;
00407       
00408       // 1. display dataset with incorrect and correct answers
00409       if (nh1 < nh) {
00410         // draw sample
00411         for (uint a = 0; a < minput.size(); ++a) {
00412           idx<Tnet> m = minput[a].x.shift_dim(0, 2);
00413           draw_matrix(m, h1, w1 + wt, zoom, zoom);
00414           wt += m.dim(1) + 2;
00415           ht = std::max(ht, (uint) m.dim(0));
00416           hmax = std::max(hmax, (uint) m.dim(0));
00417         }
00418         ostringstream s;
00419         s.precision(2);
00420         if (cds)
00421           s << cds->get_class_name((int)answer) << " ";
00422         s << energies.get(i);
00423         gui << at(h1 + 2, w1 + 2) << s.str().c_str();
00424         // print raw outputs
00425         if (print_raw_outputs) {
00426           for (uint a = 0; a < raw.dim(0); ++a) {
00427             s.str(""); s << raw.get(a);
00428             gui << at(h1 + 17 + a * 15, w1 + 2) << s.str().c_str();
00429           }
00430         } else { // print outputs answers
00431           for (uint a = 0; a < answers.dim(0); ++a) {
00432             s.str(""); s << answers.get(a);
00433             gui << at(h1 + 17 + a * 15, w1 + 2) << s.str().c_str();
00434           }
00435         }
00436         // print target info
00437         //ds.fprop_jitter(jitt);
00438         for (uint a = 0; a < target.dim(0); ++a) {
00439           s.str(""); s << target.gget(a);
00440           gui << at(h1 + 17 + a * 15, w1 + wt - 35) << s.str().c_str();
00441         }
00442         // print correct info when incorrect
00443         if (incorrect && cds) {
00444           s.str("");
00445           s << "(" << cds->get_class_name((int) label.x.get(0)) << ")";
00446           gui << at(h1 + d.dim(0) - 15, w1 + 2) << s.str().c_str();
00447         }
00448         // draw scale box if not a background class
00449         if (answer != bgid && answers.dim(0) > 2) {
00450           float scale = answers.gget(2), hoff = 0, woff = 0;
00451           rect<float> r;
00452           if (scale > 0) {
00453             scale = 1 / scale;
00454             if (answers.dim(0) == 5) {
00455               hoff = answers.gget(3) * ht;
00456               woff = answers.gget(4) * ht;
00457             }
00458             r = rect<float>(h1 + hoff, w1 + woff, ht, wt);
00459             r.scale_centered(scale, scale);
00460             draw_box(r, 0, 0, 255);
00461             // draw all groundtruth
00462             if (draw_all_jitter) {
00463               ds.fprop_jitter(jitt);
00464               idx_bloop1(ji, jitt.x, Tnet) {
00465                 scale = ji.gget(0); // TODO: fix hardcoded offset
00466                 if (scale != 0) {
00467                   scale = 1 / scale;
00468                   hoff = ji.gget(1) * ht;// TODO: fix hardcoded offset
00469                   woff = ji.gget(2) * ht;// TODO: fix hardcoded offset
00470                   r = rect<float>(h1 + hoff, w1 + woff, ht, wt);
00471                   r.scale_centered(scale, scale);
00472                 draw_box(r, 0, 255, 0);
00473                 }
00474               }
00475             }
00476           }
00477           // draw groundtruth box
00478           scale = target.gget(2); // TODO: fix hardcoded offset
00479           if (scale != 0) {
00480             scale = 1 / scale;
00481             hoff = target.gget(3) * ht;// TODO: fix hardcoded offset
00482             woff = target.gget(4) * ht;// TODO: fix hardcoded offset
00483             r = rect<float>(h1 + hoff, w1 + woff, ht, wt);
00484             r.scale_centered(scale, scale);
00485             draw_box(r, 255, 0, 0);
00486           }
00487         }
00488         // if ((ds.lblstr) && (ds.lblstr->at(st.answer.x.get())))
00489         //   gui << at(h1 + 2, w1 + 2)
00490         //       << (ds.lblstr->at(st.answer.x.get()))->c_str();
00491         w1 += wt + 2;
00492         if (((i + 1) % nw == 0) && (i > 1)) {  
00493           w1 = w01;
00494           h1 += hmax + 2;
00495           nh1++;
00496           hmax = 0;
00497         }
00498       }
00499     }
00500     enable_window_updates();
00501   }
00502 
00504   // inherited methods to implement for scrolling capabilities
00505 
00506   template<class Tnet, class Tdata, class Tlabel>
00507   void supervised_trainer_gui<Tnet, Tdata, Tlabel>::display_next() {
00508     if (next_page()) {
00509       pos = MIN(_ds->size(), pos + _nh * _nw);
00510 
00511       display_datasource(*_st, *_ds, *_infp, _nh, _nw, _h0, _w0, _zoom,
00512                          -1, NULL, true);
00513     }
00514   }
00515 
00516   template<class Tnet, class Tdata, class Tlabel>
00517   void supervised_trainer_gui<Tnet, Tdata, Tlabel>::display_previous() {
00518     if (previous_page()) {
00519       pos = std::max((uint) 0, pos - _nh * _nw);
00520       display_datasource(*_st, *_ds, *_infp, _nh, _nw, _h0, _w0, _zoom,
00521                          -1, NULL, true);
00522     }
00523   }
00524 
00525   template<class Tnet, class Tdata, class Tlabel>
00526   unsigned int supervised_trainer_gui<Tnet, Tdata, Tlabel>::max_pages() {
00527     return dsgui->max_pages();
00528   }
00529 
00530   template<class Tnet, class Tdata, class Tlabel>
00531   supervised_trainer_gui<Tnet, Tdata, Tlabel>* 
00532   supervised_trainer_gui<Tnet, Tdata, Tlabel>::copy() {
00533     //  scroll_box0* supervised_trainer_gui<Tnet, Tdata, Tlabel>::copy() {
00534     cout << "supervsed_trainer_gui::copy."<<endl;
00535     supervised_trainer_gui<Tnet, Tdata, Tlabel> *stcopy = 
00536       new supervised_trainer_gui<Tnet, Tdata, Tlabel>(*this);
00537     stcopy->dsgui = dsgui->copy();
00538     stcopy->_ds = _ds;
00539     stcopy->_last_ds = _last_ds;
00540     stcopy->_st = _st;
00541     return stcopy;
00542   }
00543 
00544 } // end namespace ebl