Logo Search packages:      
Sourcecode: gambit version File versions  Download package

dlefglogit.cc

//
// $Source: /cvsroot/gambit/gambit/sources/gui/dlefglogit.cc,v $
// $Date: 2006/01/13 20:56:24 $
// $Revision: 1.12 $
//
// DESCRIPTION:
// Dialog for monitoring progress of logit equilibrium computation
//
// This file is part of Gambit
// Copyright (c) 2005, The Gambit Project
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include <fstream>

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif  // WX_PRECOMP
#include <wx/txtstrm.h>
#include <wx/tokenzr.h>

#include "wx/sheet/sheet.h"
#include "dlefglogit.h"

class gbtLogitBehavList : public wxSheet {
private:
  gbtGameDocument *m_doc;
  Gambit::List<double> m_lambdas;
  Gambit::List<Gambit::MixedBehavProfile<double> > m_profiles;

  // Overriding wxSheet members for data access
  wxString GetCellValue(const wxSheetCoords &);
  wxSheetCellAttr GetAttr(const wxSheetCoords &p_coords, wxSheetAttr_Type) const;
  
  // Overriding wxSheet members to disable selection behavior
  bool SelectRow(int, bool = false, bool = false)
    { return false; }
  bool SelectRows(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCol(int, bool = false, bool = false)
    { return false; }
  bool SelectCols(int, int, bool = false, bool = false)
    { return false; }
  bool SelectCell(const wxSheetCoords&, bool = false, bool = false)
    { return false; }
  bool SelectBlock(const wxSheetBlock&, bool = false, bool = false)
    { return false; }
  bool SelectAll(bool = false) { return false; }

  // Overriding wxSheet member to suppress drawing of cursor
  void DrawCursorCellHighlight(wxDC&, const wxSheetCellAttr &) { }

public:
  gbtLogitBehavList(wxWindow *p_parent, gbtGameDocument *p_doc);
  virtual ~gbtLogitBehavList();

  void AddProfile(const wxString &p_text, bool p_forceShow);
};

gbtLogitBehavList::gbtLogitBehavList(wxWindow *p_parent, 
                             gbtGameDocument *p_doc) 
  : wxSheet(p_parent, -1), m_doc(p_doc)
{
  CreateGrid(0, 0);
  SetRowLabelWidth(40);
  SetColLabelHeight(25);
}

gbtLogitBehavList::~gbtLogitBehavList()
{ }

wxString gbtLogitBehavList::GetCellValue(const wxSheetCoords &p_coords)
{
  if (IsRowLabelCell(p_coords)) {
    return wxString::Format(wxT("%d"), p_coords.GetRow() + 1);
  }
  else if (IsColLabelCell(p_coords)) {
    if (p_coords.GetCol() == 0) {
      return wxT("Lambda");
    }
    else {
      Gambit::GameAction action = m_doc->GetGame()->GetAction(p_coords.GetCol());
      return (wxString::Format(wxT("%d: "), 
                         action->GetInfoset()->GetNumber())+
            wxString(action->GetLabel().c_str(), *wxConvCurrent));
    }
  }
  else if (IsCornerLabelCell(p_coords)) {
    return wxT("#");
  }


  if (p_coords.GetCol() == 0) {
    return wxString(Gambit::ToText(m_lambdas[p_coords.GetRow()+1],
                           m_doc->GetStyle().NumDecimals()).c_str(),
                *wxConvCurrent);
  }
  else {
    const Gambit::MixedBehavProfile<double> &profile = m_profiles[p_coords.GetRow()+1];
    return wxString(Gambit::ToText(profile[p_coords.GetCol()],
                           m_doc->GetStyle().NumDecimals()).c_str(), 
                *wxConvCurrent);
  }
}

static wxColour GetPlayerColor(gbtGameDocument *p_doc, int p_index)
{
  if (p_index == 0)  return *wxBLACK;

  Gambit::GameAction action = p_doc->GetGame()->GetAction(p_index);
  return p_doc->GetStyle().GetPlayerColor(action->GetInfoset()->GetPlayer()->GetNumber());
}

wxSheetCellAttr gbtLogitBehavList::GetAttr(const wxSheetCoords &p_coords, 
                                 wxSheetAttr_Type) const
{
  if (IsRowLabelCell(p_coords)) {
    wxSheetCellAttr attr(GetSheetRefData()->m_defaultRowLabelAttr);
    attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
    attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
    attr.SetOrientation(wxHORIZONTAL);
    attr.SetReadOnly(true);
    return attr;
  }
  else if (IsColLabelCell(p_coords)) {
    wxSheetCellAttr attr(GetSheetRefData()->m_defaultColLabelAttr);
    attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD));
    attr.SetAlignment(wxALIGN_CENTER, wxALIGN_CENTER);
    attr.SetOrientation(wxHORIZONTAL);
    attr.SetReadOnly(true);
    attr.SetForegroundColour(GetPlayerColor(m_doc, p_coords.GetCol()));
    return attr;
  }
  else if (IsCornerLabelCell(p_coords)) {
    return GetSheetRefData()->m_defaultCornerLabelAttr;
  }

  wxSheetCellAttr attr(GetSheetRefData()->m_defaultGridCellAttr);
  attr.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL)); 
  attr.SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTER);
  attr.SetOrientation(wxHORIZONTAL);
  if (p_coords.GetCol() > 0) {
    Gambit::GameAction action = m_doc->GetGame()->GetAction(p_coords.GetCol());
    attr.SetForegroundColour(m_doc->GetStyle().GetPlayerColor(action->GetInfoset()->GetPlayer()->GetNumber()));
    if (action->GetInfoset()->GetNumber() % 2 == 0) {
      attr.SetBackgroundColour(wxColour(250, 250, 250));
    }
    else {
      attr.SetBackgroundColour(wxColour(225, 225, 225));
    }
  }
  else {
    attr.SetForegroundColour(*wxBLACK);
    attr.SetBackgroundColour(wxColour(250, 250, 250));
  }
                     
  attr.SetReadOnly(true);
  return attr;
}

void gbtLogitBehavList::AddProfile(const wxString &p_text,
                           bool p_forceShow)
{
  if (GetNumberCols() == 0) {
    AppendCols(m_doc->GetGame()->BehavProfileLength() + 1);
  }

  Gambit::MixedBehavProfile<double> profile(m_doc->GetGame());

  wxStringTokenizer tok(p_text, wxT(","));

  m_lambdas.Append((double) Gambit::ToNumber(std::string((const char *) tok.GetNextToken().mb_str())));

  for (int i = 1; i <= profile.Length(); i++) {
    profile[i] = Gambit::ToNumber(std::string((const char *) tok.GetNextToken().mb_str()));
  }

  m_profiles.Append(profile);
  if (p_forceShow || m_profiles.Length() - GetNumberRows() > 20) {
    AppendRows(m_profiles.Length() - GetNumberRows());
    MakeCellVisible(wxSheetCoords(GetNumberRows() - 1, 0));
  }

  // Lambda tends to get large, so this column usually needs resized
  AutoSizeCol(0);
}


const int GBT_ID_TIMER = 1000;
const int GBT_ID_PROCESS = 1001;

BEGIN_EVENT_TABLE(gbtLogitBehavDialog, wxDialog)
  EVT_END_PROCESS(GBT_ID_PROCESS, gbtLogitBehavDialog::OnEndProcess)
  EVT_IDLE(gbtLogitBehavDialog::OnIdle)
  EVT_TIMER(GBT_ID_TIMER, gbtLogitBehavDialog::OnTimer)
  EVT_BUTTON(wxID_SAVE, gbtLogitBehavDialog::OnSave)
END_EVENT_TABLE()

#include "bitmaps/stop.xpm"

gbtLogitBehavDialog::gbtLogitBehavDialog(wxWindow *p_parent, 
                               gbtGameDocument *p_doc)
  : wxDialog(p_parent, -1, wxT("Compute quantal response equilibria"),
           wxDefaultPosition),
    m_doc(p_doc), m_process(0), m_timer(this, GBT_ID_TIMER)
{
  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

  wxBoxSizer *startSizer = new wxBoxSizer(wxHORIZONTAL);

  m_statusText = new wxStaticText(this, wxID_STATIC,
                          wxT("The computation is currently in progress."));
  m_statusText->SetForegroundColour(*wxBLUE);
  startSizer->Add(m_statusText, 0, wxALL | wxALIGN_CENTER, 5);

  m_stopButton = new wxBitmapButton(this, wxID_CANCEL, wxBitmap(stop_xpm));
  m_stopButton->SetToolTip(_("Stop the computation"));
  startSizer->Add(m_stopButton, 0, wxALL | wxALIGN_CENTER, 5);
  Connect(wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
        wxCommandEventHandler(gbtLogitBehavDialog::OnStop));

  sizer->Add(startSizer, 0, wxALL | wxALIGN_CENTER, 5);

  m_behavList = new gbtLogitBehavList(this, m_doc);
  m_behavList->SetSizeHints(wxSize(600, 400));
  sizer->Add(m_behavList, 0, wxALL | wxALIGN_CENTER, 5);

  wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
  m_saveButton = new wxButton(this, wxID_SAVE, 
                        wxT("Save correspondence to .csv file"));
  m_saveButton->Enable(false);
  buttonSizer->Add(m_saveButton, 0, wxALL | wxALIGN_CENTER, 5);
  m_okButton = new wxButton(this, wxID_OK, wxT("OK"));
  buttonSizer->Add(m_okButton, 0, wxALL | wxALIGN_CENTER, 5);
  m_okButton->Enable(false);

  sizer->Add(buttonSizer, 0, wxALL | wxALIGN_RIGHT, 5);

  SetSizer(sizer);
  sizer->Fit(this);
  sizer->SetSizeHints(this);
  Layout();
  CenterOnParent();
  Start();
}

void gbtLogitBehavDialog::Start(void)
{
  m_process = new wxProcess(this, GBT_ID_PROCESS);
  m_process->Redirect();

  m_pid = wxExecute(wxT("gambit-logit"), wxEXEC_ASYNC, m_process);
  
  std::ostringstream s;
  m_doc->GetGame()->WriteEfgFile(s);
  wxString str(wxString(s.str().c_str(), *wxConvCurrent));
  
  // It is possible that the whole string won't write on one go, so
  // we should take this possibility into account.  If the write doesn't
  // complete the whole way, we take a 100-millisecond siesta and try
  // again.  (This seems to primarily be an issue with -- you guessed it --
  // Windows!)
  while (str.length() > 0) {
    wxTextOutputStream os(*m_process->GetOutputStream());

    // It appears that (at least with mingw) the string itself contains
    // only '\n' for newlines.  If we don't SetMode here, these get
    // converted to '\r\n' sequences, and so the number of characters
    // LastWrite() returns does not match the number of characters in
    // our string.  Setting this explicitly solves this problem.
    os.SetMode(wxEOL_UNIX);
    os.WriteString(str);
    str.Remove(0, m_process->GetOutputStream()->LastWrite());
    wxMilliSleep(100);
  }
  m_process->CloseOutput();

  m_timer.Start(1000, false);
}

void gbtLogitBehavDialog::OnIdle(wxIdleEvent &p_event)
{
  if (!m_process)  return;

  if (m_process->IsInputAvailable()) {
    wxTextInputStream tis(*m_process->GetInputStream());

    wxString msg;
    msg << tis.ReadLine();
    m_behavList->AddProfile(msg, false);
    m_output += msg;
    m_output += wxT("\n");

    p_event.RequestMore();
  }
  else {
    m_timer.Start(1000, false);
  }
}

void gbtLogitBehavDialog::OnTimer(wxTimerEvent &p_event)
{
  wxWakeUpIdle();
}

void gbtLogitBehavDialog::OnEndProcess(wxProcessEvent &p_event)
{
  m_stopButton->Enable(false);
  m_timer.Stop();

  while (m_process->IsInputAvailable()) {
    wxTextInputStream tis(*m_process->GetInputStream());

    wxString msg;
    msg << tis.ReadLine();

    if (msg != wxT("")) {
      m_behavList->AddProfile(msg, true);
      m_output += msg;
      m_output += wxT("\n");
    }
  }

  if (p_event.GetExitCode() == 0) {
    m_statusText->SetLabel(wxT("The computation has completed."));
    m_statusText->SetForegroundColour(wxColour(0, 192, 0));
  }
  else {
    m_statusText->SetLabel(wxT("The computation ended abnormally."));
    m_statusText->SetForegroundColour(*wxRED);
  }

  m_okButton->Enable(true);
  m_saveButton->Enable(true);
}

void gbtLogitBehavDialog::OnStop(wxCommandEvent &)
{
  // Per the wxWidgets wiki, under Windows, programs that run
  // without a console window don't respond to the more polite
  // SIGTERM, so instead we must be rude and SIGKILL it.
  m_stopButton->Enable(false);

#ifdef __WXMSW__
  wxProcess::Kill(m_pid, wxSIGKILL);
#else
  wxProcess::Kill(m_pid, wxSIGTERM);
#endif  // __WXMSW__
}

void gbtLogitBehavDialog::OnSave(wxCommandEvent &)
{
  wxFileDialog dialog(this, _("Choose file"), wxT(""), wxT(""),
                  wxT("CSV files (*.csv)|*.csv|"
                    "All files (*.*)|*.*"),
                  wxSAVE | wxOVERWRITE_PROMPT);

  if (dialog.ShowModal() == wxID_OK) {
    std::ofstream file((const char *) dialog.GetPath().mb_str());
    file << ((const char *) m_output.mb_str());
  }
}

Generated by  Doxygen 1.6.0   Back to index