25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more...
Home   Projects   Up   Sitemap   Search   Blog   Forum+Chat   About Us   Privacy   Terms of Use   Feedback   FAQ   Images   Services   Ads   Donate   Humor

The "Visible Neural Network", programs for Microsoft Visual C++ .NET, Borland C/C++, ObjectWindows, and MSDOS (BGI graphics)

This is a set of artificial neural network programs. The later versions are multilayer perceptron (MLP) backpropagation neural networks in which the number of layers and nodes are user-selectable.

I call this the "Visible Neural Network" project because all the versions have color graphic displays that allow you to watch changes in the states of the network nodes and connections as they "think". There are screenshots below.

The programs are intended as learning tools for those interested in working with the source code. They are not currently configured to solve useful problems. I wrote them to get hands-on experience working with neural net code to learn how it works.

That evolved into a desire to write a neural net framework that would hopefuly be as simple as possible, straightforward and non-convoluted in its design, heavily commented to make its flow and workings understandable and to document why certain decisions were made, and extensible so it could potentially be adapted to useful applications.

I think the latest Visual C++ version is the most understandable neural net framework of the open source ones I've seen online. That doesn't make it the best, and it is undoubtedly not the most powerful or flexible, but it has the most understandable source code to work with of the ones I've looked at.


The four versions of the neural net program:

By following links from here and in the table below, you can find all the program source code on web pages.

For building the projects, the code is also provided in two zip download files. One zip (farther down this page) contains the code for all the Borland versions. A separate zip (follow the link in the next paragraph) has the Microsoft Visual C++ version.

  1. Microsoft Visual C++ .NET CLR. Current version and the only one that will have any further development. I converted it to Visual C++ from the last OWL version (#2 below), so it includes all its latest improvements in methods and terminology. The project consists of two parts: 1) Network classes that are platform independent. 2) A Windows application that demonstrates their functioning.
     
  2. Borland C++ 4.0 and ObjectWindows Library 2.0 for Windows. Before using this as the source for the Visual C++ conversion, I gave it a major upgrade/rewrite to bring it into conformity with standard practices and terminology of MLP neural networks. There is a change log. The platform-independent network classes are separate from the Windows GUI program that uses them.
     
  3. Borland C++ 4.0 for MSDOS, BGI graphics. The old MSDOS versions of the program have not been upgraded to standard MLP practices and terminology. It should be fairly straightforward to import the new network classes into them, but I haven't done it.
     
  4. Borland C 4.0 for MSDOS, BGI graphics. This is also obsolete and unmaintained. 

Download all the Borland C/C++ neural network programs

Download neural.zip (about 89 KB)

The zip contains all the Borland C++ versions (OWL and MSDOS), but not the Microsoft Visual C++ version. (Its zip file is available on its own project home page.)

The zip file contains the code unique to the project, but also required are library files elsewhere on this site. Links to those files are in the web page source code listings that you can get to by following the links in the table below. 

The projects end up being rather large.

WNEURAL.CPP Borland OWL 2.0, main source file. The code listing begins farther down this page, which is Page 1.
NETWORK.H, NETWORK.CPP Code for the neural network itself. No Windows-specific code.
LIBRARY.CPP Project node that pulls in required library routines.
MSGWIND.H A window to which program sends status reports.
WNEURAL.DEF Windows module definition file.
WNEURAL.RC Windows resources.
WNEURAL.RH Windows resource header.
WNEURAL.RTF RTF source for the project's WinHelp file. This file is based on the HELPFILE.DOT Microsoft Word template, which provides a macro useful while building WinHelp files.
WNEURAL.HLP Compiled WinHelp file.
WNEURAL.HPJ Instructions for the help compiler.
TEMP.NET Example data file shows the text format of a network saved to disk.
DNEURAL.CPP Borland C++ 4.0 for MSDOS with BGI graphics. This was the final standalone MSDOS version, before the conversion to OWL/Windows. It's all in one source file, but there's a lot of code, so it might require two project nodes to compile.
DNEURAL.C Borland C 4.0 for MSDOS. This was the last C version before the conversion to C++. It is peculiar in that it uses linked lists instead of BIDS vector container classes. This was because the very first version of this program (which has been lost) was in DeSmet C, and a linked list was the only flexible type of container available.
NEURAL(Excel5).XLS Excel 5 worksheet, shows the derivation of one calculation (imports to any Excel version).
NEURAL.TXT Just has a text pointer to the web page on this site where the complex.doc file (containing project notes) is located.
AND.TRS, ANDOR.TRS, BAD.TRS, IDENTITY.TRS, IMP.TRS, INVERT.TRS, LOGIC.TRS, NAND.TRS, NOR.TRS, OR.TRS, TEST.TRS, TRAINING.TXT, XOR.TRS Training sets (data files) for training the network on simple logic problems.
COPYING.TXT GNU GPL license.

 

The project notes for these neural net programs are in a file called COMPLEX.DOC. It is available in several formats from the COMPLEX.DOC download page.

If you want to review it first, there is a web page showing the Neural Net section of COMPLEX.DOC.

If that web page looks like it will be interesting to you, you might want the entire COMPLEX.DOC file. It has discussions of neural nets throughout it.


Screenshots:

1) Before calculation begins, showing 5 input nodes, 3 hidden layers of 8 nodes each, and 1 output node. Node numbers are color coded by type (Sensory, Processor, Output).

Screenshot of the MLP neural network program before calculation begins.

2) During calculation. Connection colors encode levels of (output * connection strength). Nodes in this screenshot are shown as circles. If filled in, the node's net input is > 0; if empty, its input is <= 0. Display can be turned off for greater speed, or set to 2 other useful levels (or a third useless one).

Screenshot of the MLP neural network program during calculation.

3) This screenshot of the project nodes in the Borland C++ 4.0 IDE might help you set up the project:

Screenshot of the neural network project node hierarchy in the Borland C++ 4.0 IDE.


If you are looking for a more powerful and flexible neural network program than this one and are knowledgeable enough not to be discouraged by a large assortment of options and settings, the Fast Artificial Neural Network Library (FANN) looks like the one I would choose due to its organization, commenting, and documentation. It is at http://sourceforge.net/.


WNEURAL.CPP - Borland OWL Windows version, Page 1

wneural.cpp

/*	wneural.cpp 		WINDOWS	target		4-24-2007
	This is part of the WNeural project, Version 2.0.
	Copyright (C)1995-99, 2007 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	Version 3 as published by the Free Software Foundation.
	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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

(adapted from wlife2d.cpp)
A multilayer perceptron neural network that you can watch on the screen as it "thinks".

------
TO DO:

messagewindow is still probably best method for status reports,
but could alternatively write text info to aux (open it as a file) for viewing from Terminal.exe,
or use DDE to send status messages to an open MSWord temporary document
the way WAdapt.cpp does. (don't use OutputDebugString: you don't want event log output,
and don't use OpenComm, which is not Win32 portable).
you can en/disable output w/a toggle BOOL.  unconditionally call OutputStatus(...),
which only within itself tests the member BOOL for whether to actually output anything.

--------------------
NOTES:

--draws directly to the window to avoid having to wait for Invalidate()
--This probably is a good candidate for Doc/View.  The network is the document,
  which would maintain its own running status text, to appear in a text view.
  The graphic view would show the net, though its continual updating might be slow.
  Then you could also have graph views (training times, node activity, or whatever).

FILES:
-- *.TRS		Training set data, problems and solutions.
-- *.NET		Node connection data for rebuilding a network from disk.

*/
#include <owl\owlpch.h>
#include <owl\chooseco.h>
#include <owl\opensave.h>
#include <owl\radiobut.h>
#include <owl\edit.h>
#include <owl\inputdia.h>
#include <classlib\arrays.h>
#include <math.h>
#pragma hdrstop

#include "c:\bcs\my.h"

#include "c:\bcs\library\stopwatc.h"
#include "c:\bcs\library\filearay.h"
#include "network.h"
#include "msgwind.h"
#include "wneural.rh"

#define IDHELP 		998					// redefine a constant from bwcc.h, needed here

static const char AppName[] = "Neural Network";

//////////////////////////////////////////////////////////////////////////////
// SMessageWindow constructor
// derived from TDialog because with a base of TWindow, accelerators were intercepted.
// to keep the edit control the same size as the dialog, could resize it in the dialog's
// EvSize, by directly setting the TEdit's w,h attributes?  (yes. see wshowfs.cpp)
// statuswindow.text scrolls, but the display of it doesn't.  always top lines displayed.
// use ed->SetSelection()
SMessageWindow::SMessageWindow(TWindow* parent, TResId resid) : TDialog(parent, resid)
{
ed = new TEdit(this,IDC_TRANSEDIT,2001);
isopen = TRUE;                          	// assumes autocreate is used
}
//----------------------------------------------------------------------------
// accept a string, add it to the text for the TEdit text, and redisplay.
// modified from serialco.cpp
// Always append /r/n.  You cannot use endl.  (for one thing, this isn't a stream)
// #error there is now LFtoCRLF() for the conversion.  change wherever useful.
SMessageWindow& SMessageWindow::operator << (const string& s)
{
text += s;                		// text can be any length, but displayed info cannot,
int length = text.length();
if(length > 2000)   						// if it's now too long,
	text = text.substr(length - 2000);  	// save only the latest 2000 chars
// #error use ed->SetText() or ed->SetSelection(-1,-1); ed->Insert()
if(isopen)
	SetDlgItemText(IDC_TRANSEDIT,text.c_str()); // transfer it to the edit control
return(*this);
}
//----------------------------------------------------------------------------
// Remember the distinction between destroying the interface element and
// destroying the object.  As long as the object hasn't been destroyed, nothing
// should get corrupted.  << adds text to the string, which shows at the next Create().
void SMessageWindow::Destroy(int retValue)
{
TDialog::Destroy(retValue);
isopen = FALSE;
}
//----------------------------------------------------------------------------
BOOL SMessageWindow::Create()
{
BOOL retval = TDialog::Create();                // can only set text after it's open
if(text.length())
	SetDlgItemText(IDC_TRANSEDIT,text.c_str()); // transfer it to the edit control
isopen = TRUE;
return(retval);
}
//----------------------------------------------------------------------------
//					end class SMessageWindow
//////////////////////////////////////////////////////////////////////////////
// A window in which a neural net operates, and to which its graphics output is sent.
class SNeuralWindow : public TWindow
{
public:
	SNeuralWindow(TWindow* parent = 0);
	~SNeuralWindow();

	BOOL IdleAction(long idlecount);

protected:
	// pointers
	NeuralNet* net;
	SMessageWindow* statuswindow;		// for ongoing status reports

	// other variables
	Stopwatch sw;		// for timing things
	long Iterations;	// counts calls to net->Run() for training count/timing calculations

	int INodes;			// User normally wants to reuse these values.
	int H1Nodes;        // They are the node counts for each layer of the network.
	int H2Nodes;
	int H3Nodes;
	int ONodes;

	BOOL autocycle;		// a master switch to force IdleAction to do nothing

	// normal functions
	void erasewindow();					// an immediate substitute for Invalidate()
	void drawnodes(TDC& pdc, BOOL showconnect);	// display the entire network graphically
	void AssignNodeLocs();				// assign each Node its screen location.

	// overridden virtuals
	BOOL CanClose();
	void Paint(TDC&, BOOL, TRect&);

	// event handlers
	void EvSize(UINT sizeType, TSize& size);
	void EvRButtonDown(UINT, TPoint&);
	void CmFileStart();
	void CmFileExit() { GetApplication()->GetMainWindow()->CloseWindow(); }
	void CmNetNew();
	void CmNetLoad();
	void CmNetSaveAs();
	void CmTrainerLoad();
	void CmViewPeekLevel();
	void CmViewNodeNumbers();
	void CmViewMessages();
	void CmHelpIndex();
	void CmHelpAbout();

DECLARE_RESPONSE_TABLE(SNeuralWindow);
};
DEFINE_RESPONSE_TABLE1(SNeuralWindow, TWindow)
	EV_WM_SIZE,
	EV_WM_RBUTTONDOWN,
	EV_COMMAND(CM_FILESTART,CmFileStart),
	EV_COMMAND(CM_FILEEXIT,CmFileExit),
	EV_COMMAND(CM_NETNEW,CmNetNew),
	EV_COMMAND(CM_NETLOAD,CmNetLoad),
	EV_COMMAND(CM_NETSAVEAS,CmNetSaveAs),
	EV_COMMAND(CM_TRAINERLOAD,CmTrainerLoad),
	EV_COMMAND(CM_VIEWPEEKLEVEL,CmViewPeekLevel),
	EV_COMMAND(CM_VIEWNODENUMBERS,CmViewNodeNumbers),
	EV_COMMAND(CM_VIEWMESSAGES,CmViewMessages),
	EV_COMMAND(CM_HELPINDEX,CmHelpIndex),
	EV_COMMAND(CM_HELPABOUT,CmHelpAbout),
END_RESPONSE_TABLE;
//----------------------------------------------------------------------------
// constructor
SNeuralWindow::SNeuralWindow(TWindow* parent) : TWindow(parent)
{
SetBkgndColor(TColor::Black);

// initialize pointers
net = new NeuralNet();

// when minimized, the messagewindow has an empty icon.
statuswindow = new SMessageWindow(this,TResId(MESSAGESWINDOW));
statuswindow->EnableAutoCreate();

// initialize other variables
autocycle = FALSE;
Iterations = 0L;

INodes = 2;						// User normally wants to reuse these values.
H1Nodes = 4;
H2Nodes = 0;
H3Nodes = 0;
ONodes = 1;

}						//constructor
//----------------------------------------------------------------------------
// destructor
SNeuralWindow::~SNeuralWindow()
{
if(statuswindow)
	delete statuswindow;
if(net)
	delete net;
}	                    	//destructor
//----------------------------------------------------------------------------
void SNeuralWindow::EvSize(UINT /* sizeType */, TSize& /* size */)
{
AssignNodeLocs();
Invalidate(TRUE);
}		                      	//EvSize
//----------------------------------------------------------------------------
// Load a training set file.
// this is now redundant. CmTrainerLoad does the same thing.
void SNeuralWindow::CmFileStart()
{
CmTrainerLoad();
//erasewindow();	// #error remove if not needed
}							//CmFileStart
//----------------------------------------------------------------------------
// fill the window with black.  You frequently don't want to wait for Invalidate to work.
// I think this is no longer used.
void SNeuralWindow::erasewindow()
{
TRect clientrect = GetClientRect(); 	// current size of the display window
TClientDC dc(*this);
dc.FillRect(TRect(0,0,clientrect.Width(),clientrect.Height()),TBrush(TColor::Black));
}  	        						// erasewindow
//----------------------------------------------------------------------------
/* Assign each Node its location on the screen. Call it whenever a new net is created or
loaded or the screen is resized.
*/
void SNeuralWindow::AssignNodeLocs()
{
if(!net || !net->nw.GetItemsInContainer())
	return;

int ystart = 16;		// the top node is cut in half if it's 0, so what should it be?
int h = GetClientRect().Height() - ystart;
int w = GetClientRect().Width();

int dyin = h / max(net->Count(INPUT), 1); 		// spacing of nodes down the screen
int dyh1 = h / max(net->Count(HIDDEN,1), 1);	// max prevents divide by zero
int dyh2 = h / max(net->Count(HIDDEN,2), 1);
int dyh3 = h / max(net->Count(HIDDEN,3), 1);
int dyout = h / max(net->Count(OUTPUT), 1);

int dxcolumns = (w - 30) / 4; 		// spacing of columns (by node type) across the screen
int dxfull = 20; // when a single column fills up, next column starts this many pixels to right
				 // #error not yet implemented.
				 // The screen display is mostly for small runs. At some point, there are
				 // too many nodes and you just can't display them well.

int lasttype = -1;		// move over to next column when type changes
for(int i = 0 ; i < net->nw.GetItemsInContainer() ; i++)
{
	Node* n = net->nw[i];
	switch(n->Type)
	{
		case INPUT:
			n->x = 10;
			if(n->Type != lasttype)
				n->y = ystart;				// start at column top
			else
				n->y = net->nw[i-1]->y + dyin;
			break;
		case HIDDEN:
			n->x = n->HiddenLayer * dxcolumns;
			switch(n->HiddenLayer)
			{
				case 1:
					if(n->Type != lasttype)
						n->y = ystart;				// start at column top
					else
						n->y = net->nw[i-1]->y + dyh1;
					break;
				case 2:
					if(n->HiddenLayer != net->nw[i-1]->HiddenLayer)
						n->y = ystart;
					else
						n->y = net->nw[i-1]->y + dyh2;
					break;
				case 3:
					if(n->HiddenLayer != net->nw[i-1]->HiddenLayer)
						n->y = ystart;
					else
						n->y = net->nw[i-1]->y + dyh3;
					break;
			}
			break;
		case OUTPUT:
			n->x = w - 20;
			if(n->Type != lasttype)
				n->y = ystart;				// start at column top
			else
				n->y = net->nw[i-1]->y + dyout;
			break;
	}
	// This calculation for ->y was reliable, and if enabled replaces all the
	// y calculations above. It also creates a more visually varied screen display.
	//n->y = random(h);

	lasttype = n->Type;
}
}  	        						// AssignNodeLocs
//----------------------------------------------------------------------------
// display the entire network graphically
void SNeuralWindow::drawnodes(TDC& dc, BOOL showconnect)	// whether to show connections
{												// FALSE if just updating node fills
dc.SetBkColor(TColor::Black);
dc.SetMapMode(MM_TEXT);

TColor color;
int nwcount = net->nw.GetItemsInContainer();
for(int i = 0 ; i < nwcount ; i++)
{
	Node* n = net->nw[i];
	switch(n->Type)			// set color for this node
	{
		case INPUT:  color = TColor::LtRed; 	break;
		case HIDDEN: color = TColor::LtGreen; 	break;
		case OUTPUT: color = TColor::White;		break;
	}
	if(net->shownodenumbers)	// display as numbers
	{
		dc.SetTextColor(color);
		dc.TextOut(n->x, n->y, tostring(i).c_str());
	}
	else						// or display as circles.  Much slower than using #s.
	{
		// if inputs positive, fills it in.  Pen draws outline, Brush fills interior.
		dc.SelectObject(TPen(color));
		dc.SelectObject(TBrush((n->input > 0.) ? color : TColor::Black));

		dc.Ellipse(n->x - 5, n->y - 5, n->x + 5, n->y + 5);

		dc.RestorePen();
		dc.RestoreBrush();
	}
	if(!showconnect)
		continue;

	// Now draw the connections into this node, colored according to
	// (source node's output * the connection weight).
	// To do so, it must draw the connections using inlist, so it
	// draws the connections coming IN to this node.
	int count = n->inlist.GetItemsInContainer();
	for(int j = 0 ; j < count ; j++)
	{
		Node* source = n->inlist[j].from;
		double output = source->output;
		double weight = n->inlist[j].weight;
		double total = output * weight;

/*#error unfinished.
This is the eventual coloration: they need to be scaled somehow
blue(0-255)  = - more than 1.0
red(0-255)   = - 0 to -1
green(0-255) = + 0 to 1
white(0-255) = + more than 1.0
*/
		if(total < -1.0) color = TColor(0,0,255);
		else if((total >= -1.0) && (total <  0.0)) color = TColor(255,0,0);
		else if((total >=  0.0) && (total <= 1.0)) color = TColor(0,255,0);
		else color = TColor(255,255,255);

		dc.SelectObject(TPen(color));
		dc.MoveTo(n->inlist[j].from->x, n->inlist[j].from->y);
		dc.LineTo(n->x, n->y);

		dc.RestorePen();
	}
}		// end for(all nodes in network)
}								// drawnodes
//----------------------------------------------------------------------------
// might eventually be useful for something
void SNeuralWindow::EvRButtonDown(UINT modKeys, TPoint& point)
{
TWindow::EvRButtonDown(modKeys,point);			// probably does nothing
Invalidate(TRUE);								// for now, just force repainting
}						//EvRButtonDown
//----------------------------------------------------------------------------
// toggle how to display the nodes (as #s or as dots)
void SNeuralWindow::CmViewNodeNumbers()
{
net->shownodenumbers = !net->shownodenumbers;
CheckMenuItem(GetApplication()->GetMainWindow()->GetMenu(),CM_VIEWNODENUMBERS,
					MF_BYCOMMAND | (net->shownodenumbers ? MF_CHECKED : MF_UNCHECKED));
Invalidate(TRUE);							// force a total redraw
}                    	//CmViewNodeNumbers
//----------------------------------------------------------------------------
// set how often to redraw graphic node display
void SNeuralWindow::CmViewPeekLevel()
{
char buf[10];
ostrstream(buf,sizeof(buf)) << net->graphicon << ends;
if(TInputDialog(this,"Graphic Node Display Update Frequency",
					"Enter 0 (fast) to 3 (slow): ", buf,sizeof(buf)).Execute() == IDOK)
{
	int i = atoi(buf);
	if((i >= 0) && (i <= 3))
		net->graphicon = i;
}
}                       //CmViewPeekLevel
//----------------------------------------------------------------------------
void SNeuralWindow::CmViewMessages()
{
if(statuswindow->isopen)
	statuswindow->Destroy();
else
	statuswindow->Create();
CheckMenuItem(GetApplication()->GetMainWindow()->GetMenu(),CM_VIEWMESSAGES,
					MF_BYCOMMAND | (statuswindow->isopen ? MF_CHECKED : MF_UNCHECKED));
}                   	//CmViewMessages
//----------------------------------------------------------------------------
void SNeuralWindow::CmHelpIndex()
{
	WinHelp("wneural.hlp",HELP_INDEX,0);
}
//----------------------------------------------------------------------------
/* rewire the network to our specifications   */
void SNeuralWindow::CmNetNew()
{
char buf[80] = {0};
int i = INodes, j = H1Nodes, k = H2Nodes, l = H3Nodes, m = ONodes;
ostrstream(buf,sizeof(buf)) << INodes  << " "
							<< H1Nodes << " "
							<< H2Nodes << " "
							<< H3Nodes << " "
							<< ONodes  << ends;
if(TInputDialog(this,"Number of Nodes of to Create",
					"Enter # of nodes in each layer:\n"
					"Input (>0) Hidden1 (>=0) Hidden2 (>=0) Hidden3 (>=0) Output (>0)\n"
					"If any hidden layer is 0, all subsequent hidden layers must be 0.",
					buf,sizeof(buf)).Execute() == IDOK)
{
	istrstream(buf) >> i >> j >> k >> l >> m;
	if(net->CreateNetwork(i, j, k, l, m))
	{
		// User normally wants to reuse these values, so save them now that we know they're legal values.
		INodes = i;
		H1Nodes = j;
		H2Nodes = k;
		H3Nodes = l;
		ONodes = m;

		AssignNodeLocs();
		autocycle = TRUE;
		Iterations = 0L;
		sw.reset();			// because cycling will start with the next IdleAction
		sw.start();
	}
	else
		MessageBox("Please review the rules for node counts and try again.",
					"Cannot create specified network.",MB_OK);
}
Invalidate(TRUE);			   	// erase the old display if present
}               		   		//CmNetNew
//----------------------------------------------------------------------------
// load a previously saved network architecture from disk
void SNeuralWindow::CmNetLoad()
{
// note this static would be shared by all class objects if you allow multiples
static TOpenSaveDialog::TData fd(OFN_FILEMUSTEXIST,
				"Networks (*.NET)|*.NET|All Files (*.*)|*.*|",0,"","NET");
if((TFileOpenDialog(this,fd)).Execute() == IDOK)
{
	if(net)
	{
		ifstream(fd.FileName) >> *net;	// overwrite the net from the file
		AssignNodeLocs();
		Iterations = 0L;
		autocycle = TRUE;
		sw.reset();			// because cycling will start with the next IdleAction
		sw.start();
	}
}
}       	     	        	//CmNetLoad
//----------------------------------------------------------------------------
// save the current network to disk as a .NET file
void SNeuralWindow::CmNetSaveAs()
{
static TOpenSaveDialog::TData fd(OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST,
				"Networks (*.NET)|*.NET|All Files (*.*)|*.*|",0,"","NET");
if(TFileSaveDialog(this,fd).Execute() == IDOK)
	ofstream(fd.FileName) << *net;
}		                   	  	//CmNetSaveAs
//----------------------------------------------------------------------------
// load training set from disk.
void SNeuralWindow::CmTrainerLoad()
{
static TOpenSaveDialog::TData fd(OFN_FILEMUSTEXIST,
				"Training Sets (*.TRS)|*.TRS|All Files (*.*)|*.*|",0,"","TRS");
if((TFileOpenDialog(this,fd)).Execute() != IDOK)    // if user cancelled
{
	if(fd.Error)                                    // check for error
		MessageBox("Dialog box returned error.","Warning",MB_OK);
	return;                                      	// and return
}
// user selected file(s), so process them
//#error this hasn't been modified to match the removal of MULTISELECT. Maybe no mods needed.
FileArray FileList;						// accumulates the file names
FileList.AddDialogList(fd.FileName);
while(FileList.GetItemsInContainer())	// load the training files (there will be only 1)
{
	string buf(FileList.GetNext());		// copy file name
	buf.to_upper();						// toupper for error messages
	if(!buf.contains(".TRS"))
	{
		buf += "\nFile extension must be .TRS";
		MessageBox(buf.c_str(),"Cannot Load This File",MB_OK);
		continue;
	}
	if(!net->LoadCases(buf.c_str()))
	{
		buf += "\nFile not found, corrupt, or training sets not unique.";
		MessageBox(buf.c_str(),"File Error",MB_OK);
		continue;
	}
}
if(net->Cases.GetItemsInContainer())
{
	Iterations = 0L;
	autocycle = TRUE;
	sw.reset();			// because cycling will start with the next IdleAction
	sw.start();
}
else
	MessageBox("No sets were successfully loaded.","Warning: Training data set empty",MB_OK);
}               	       			// CmTrainerLoad
//----------------------------------------------------------------------------
// show the "About" window
void SNeuralWindow::CmHelpAbout()
{
MessageBox("Copyright 1995-97, 2007 Steven Whitney",GetApplication()->GetName(),MB_OK);
}
//----------------------------------------------------------------------------
// allow saving network before shutdown
BOOL SNeuralWindow::CanClose()
{
BOOL status = TWindow::CanClose();  	// processes children
if(status == FALSE)
	return(FALSE);

if(net)
	if(MessageBox("Save net to disk?","Network has changed",MB_YESNO) == IDYES)
		CmNetSaveAs();

return(TRUE);
}       		              	//CanClose
//----------------------------------------------------------------------------
void SNeuralWindow::Paint(TDC& pdc, BOOL /* erase */, TRect& invalidarea) // OWLPG:37
{
// don't waste system time if !net or this isn't the active application, or is minimized
if(!net || IsIconic() || (GetActiveWindow() != GetApplication()->GetMainWindow()->HWindow))
	return;

pdc.FillRect(invalidarea, TBrush(TColor::Black));
drawnodes(pdc, TRUE);
}  		                		//Paint
//----------------------------------------------------------------------------
BOOL SNeuralWindow::IdleAction(long /* idlecount */)
{
if(!autocycle || !net)
	return(TRUE);

//putchar(7);		// for testing whether it's cycling under all conditions (it is)
string statusmsg, errormsg;
int result = net->Run();
switch(result)
{
	case 0:				// processing is in progress. No problem.
		Iterations++;	// this counts the calls to Run(), and overstates actual iterations.
		if((net->graphicon == 3) ||
			((net->graphicon == 2) && net->CurrentCaseWasSolved()))
				Invalidate(TRUE);
		break;
	case 1:				// FINISHED because the network is fully trained (training run)
		sw.stop();
		statusmsg = string("Time: ") + tostring(sw.trialtime()) + " seconds."
				 + "  Trials: " + tostring(Iterations);
		*statuswindow << string("Trained-  ") << statusmsg << "\r\n";
		MessageBox(statusmsg.c_str(),"Network is trained on all input Cases.",MB_OK);
		autocycle = FALSE;
		if(net->graphicon == 1)
			Invalidate(TRUE);
		break;
	case 2:				// FINISHED because all Cases in a REAL RUN have been processed.
		sw.stop();
		statusmsg = "The result data are in the output file.";
		statusmsg += string("Time: ") + tostring(sw.trialtime()) + " seconds."
				 + "  Cases solved: " + tostring(Iterations);
		*statuswindow << string("All Cases calculated-  ") << statusmsg << "\r\n";
		MessageBox(statusmsg.c_str(),"All dataset Cases have been processed.",MB_OK);
		autocycle = FALSE;
		break;
	// errors
	case -1:
		errormsg = "The network is not created, contains no Nodes.";
		break;
	case -2:
		errormsg = "There is no input dataset loaded. (Cases is empty).";
		break;
	case -3:
		errormsg = "Network and dataset incompatible: Input nodes != Inputs.";
		break;
	case -4:
		errormsg = "Network and dataset incompatible: Output nodes != Outputs.";
		break;
}
if(errormsg.length())
{
	errormsg += "\nWhen you resolve the problem, it will automatically try again.";
	MessageBox(errormsg.c_str(),"Network cannot start processing because...",MB_OK);
	autocycle = FALSE;			// prevent displaying this message repeatedly.
}

/*
// set up window caption
string caption = string(GetApplication()->GetName()) + string(" - ");
if(Parent)
	Parent->SetCaption(caption.c_str());
*/

return(TRUE);		// always return true, or it won't get called again
}
//----------------------------------------------------------------------------
// 							end class SNeuralWindow
//////////////////////////////////////////////////////////////////////////////
// The application object
class TMyApp : public TApplication
{
public:
	TMyApp(const char far *title) : TApplication(title) {}	// title used in case of error

protected:
	virtual BOOL IdleAction(long idlecount);
	virtual void InitMainWindow()
	{
		TFrameWindow* frame = new TFrameWindow(0,GetName(),new SNeuralWindow);
		frame->AssignMenu(TResId(MENU_1));
		frame->Attr.AccelTable = TResId(MENU_1);
		nCmdShow = SW_SHOWNORMAL;
		SetMainWindow(frame);
	}
};
//----------------------------------------------------------------------------
BOOL TMyApp::IdleAction(long /* idlecount */)
{
TApplication::IdleAction(0);
return TRUE;
}
//----------------------------------------------------------------------------
// 							end class TMyApp
//////////////////////////////////////////////////////////////////////////////
// OwlMain
int OwlMain(int /* argc */, char** /* argv */)
{
randomize();
string::set_case_sensitive(0);
string::set_paranoid_check(1);

// Run() calls: InitApplication(), InitInstance() { (which calls: InitMainWindow() },
// then displays main window.
TMyApp* myapp = new TMyApp(AppName);
int retval = myapp->Run();
delete myapp;
return(retval);
}              					//OwlMain

wneural.rtf source text for the WinHelp file

# $ K + Neural Net Help Contents

OverviewHID_OVERVIEW

File FormatsHID_FILEFORMATS

File Menu CommandsHID_FILEMENUCOMMANDS
Net Menu CommandsHID_NETMENUCOMMANDS
Trainer Menu CommandsHID_TRAINERMENUCOMMANDS
View Menu CommandsHID_VIEWMENUCOMMANDS
References and Further ReadingHID_FURTHERREADING

# $ K + Overview

Neural Net is a multilayer perceptron backpropagation (MLP) neural network. You can view a graphical display of the network as it thinks. Faster cycling means the training sets are being solved more readily.

In the graphic display, input nodes are RED, on left side of screen, hidden nodes are GREEN in center, and output nodes are WHITE at right. Flow is from input nodes through hidden nodes, to output nodes. Connection coloring indicates the (output of the output node * the weight of the connection). Coloration and boundaries can be specified in the source code file. Currently WHITE = highly stimulating GREEN = stimulating RED = inhibitory BLUE = highly inhibitory.

Neural Net (WNeural) Version 2.0 is a major rewrite, and is not compatible with Version 1.0 (which did not have a version number associated with it).

# $ K + File Formats

.TRS

Training set data, problems and solutions. A problem set consists of a list of inputs that constitute a problem, and a list of the outputs that constitute the correct answer for that problem. Format for each problem set is simply 2 lines, as below. A training set usually consists of multiple problems. The training set file has multiple sets of 2-line pairs:

input1 input2 input3... input values for problem 1
answer1 answer2... output values that are the correct answer for problem 1
input1 input2 input3... input values for problem 2
answer1 answer2... output values that are the correct answer for problem 2
etc.

A .TRS file with only 1 line can be used to test a network after it is trained. That is, the 1-line file contains only inputs, no correct answers, so the net will just accept the input and generate an answer.

.NET

Node connection data for rebuilding a network from disk, including how many of each type of node the network has (on line 1) and (on all following lines) information about the connections between the nodes.

Format:

inodecount h1count h2count h3count onodecount TOLERANCE\n
node# nodeBias nodeLearningRate\n
node# nodeBias nodeLearningRate\n
etc...
fromnode# tonode# weight\n
fromnode# tonode# weight\n
etc...

# $ K + File Menu Commands

Start

Begin training a network on a problem (training) set. You can use File|Start as your first action when entering the program. It always prompts you for the training set file(s) to use. If there is an existing network from a previous run, it will train that net on the problem set. If there is no previous net (the first run), you are prompted for some parameters to create one. If you wish to create a new network at a later time, use Net|NewHID_NETMENUCOMMANDS.

Exit

Terminate the program and exit to Windows.

# $ K + Net Menu Commands

New

Creates a new network, replacing any currently existing network (and losing it, if you have not saved it). You are prompted for the number of nodes to use in each of the network layers.

Whenever you create or load a new network or load a training set, the program checks to see if the two are compatible. If they are not, it displays the reason. If everything is ok, processing of the training set begins immediately.

Load

Create a new network from a disk file, using the numbers of nodes specified there and the specified connection data.

SaveAs

Save the current network to a disk file with the extension .NET.

The following menu items, if they still appear in the main window, are not functional and do nothing:

Add Connection

Delete Connection

RipOut Node

 

# $ K + Trainer Menu Commands

Load

Specify training set file(s) on which the network should be trained.

# $ K + View Menu Commands

Node #s

Toggles whether nodes are displayed as dots or as numbers. Numbers indicate node positions in the sequential order. (Nodes can only output to nodes later in the sequence than themselves.) Dots will flicker according to whether the summed input to that node is currently > 0 (dot filled in) or negative (dot empty). This significantly slows down processing.

Messages

Whether to show or hide the status report window.

Peek Level

How often to update the graphic display of the net:

0 no display FAST, but invisible
1 each time the last set is solved connection colors don't change, but new ones do appear
2 each time each set is solved a little slow, but the cycling shows up
3 every iteration of every set very SLOW

# $ K + For Further Reading

Books:

Choas Under Control, David Peak and Michael Frame
Complexity, M. Mitchell Waldrop

Magazine Articles:

Computer Programs:


library.cpp

/*	library.cpp		3-4-99
	Copyright (C)1999 Steven Whitney.
	Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
	Initially published by http://25yearsofprogramming.com.

	The library for the WNEURAL project. This should be a project node.

*/
#include <owl\owlpch.h>
#include <owl\chooseco.h>
#include <owl\opensave.h>
#include <owl\radiobut.h>
#include <owl\edit.h>
#include <owl\inputdia.h>
#include <classlib\arrays.h>
#include <math.h>
#include <stdio.h>				// for remaining putchar, gets calls
#include "c:\bcs\my.h"
#pragma hdrstop

#include "c:\bcs\mylib.cpp"
#include "c:\bcs\library\stopwatc.cpp"
#include "c:\bcs\library\filearay.cpp"


//////////////////////////////////////////////////////////////////////////////

msgwind.h

/*	msgwind.h				6-16-97
	Copyright (C)1997 Steven Whitney.
	Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
	Initially published by http://25yearsofprogramming.com.

	This is part of the WNEURAL project.

	SMessageWindow class declaration. The program's status reports go to a SMessageWindow.

*/
#ifndef  __MSGWIND_H
#define  __MSGWIND_H

#include <owl\owlpch.h>
#include <owl\edit.h>
#include "c:\bcs\my.h"
#pragma hdrstop

//////////////////////////////////////////////////////////////////////////////
// dialog box to replace EasyWin window.  For status messages.
class SMessageWindow : public TDialog
{
public:
	SMessageWindow(TWindow* parent, TResId resid);	// constructor
	SMessageWindow& operator << (const string&);  	// add a string to the text

	BOOL isopen;                           	// interface element currently displayed?
	BOOL Create();
	void Destroy(int retValue = IDCANCEL);

protected:
	TEdit* ed;
	string text;
};
//----------------------------------------------------------------------------
#endif		// msgwind.h

WNEURAL.RC

/*

WNEURAL.RC is part of the WNEURAL project.
Copyright (C)1995-99 Steven Whitney.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
Initially published by http://25yearsofprogramming.com.

produced by Borland Resource Workshop

*/

#include "wneural.rh"

MESSAGESWINDOW DIALOG 31, 66, 335, 231
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION "Message Window"
FONT 8, "MS Sans Serif"
{
 EDITTEXT IDC_TRANSEDIT, 0, 0, 335, 231, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | NOT WS_TABSTOP | NOT WS_BORDER | WS_VSCROLL | WS_HSCROLL
}


IDD_INPUTDIALOG DIALOG 35, 50, 280, 133
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_SETFONT
FONT 8, "Helv"
{
 LTEXT "", ID_PROMPT, 6, 6, 268, 78
 EDITTEXT ID_INPUT, 9, 89, 261, 12, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL
 DEFPUSHBUTTON "&OK", IDOK, 102, 111, 40, 14
 PUSHBUTTON "&Cancel", IDCANCEL, 148, 111, 40, 14
}


MENU_1 MENU 
{
 POPUP "&File"
 {
  MENUITEM "&Start...\tS", CM_FILESTART
  MENUITEM "E&xit", CM_FILEEXIT
 }

POPUP "&Net"
 {
  MENUITEM "&New\tN", CM_NETNEW
  MENUITEM "&Load...", CM_NETLOAD
  MENUITEM "&SaveAs...", CM_NETSAVEAS
  MENUITEM SEPARATOR
  MENUITEM "&Add Connection...\tA", CM_NETADDCONNECTION
  MENUITEM "&Delete Connection...\tD", CM_NETDELETECONNECTION
  MENUITEM SEPARATOR
  MENUITEM "&Rip Out Node...\tR", CM_NETRIPNODE
 }

POPUP "&Trainer"
 {
  MENUITEM "&Load...\tT", CM_TRAINERLOAD
 }

POPUP "&View"
 {
  MENUITEM "&Node Numbers\t#", CM_VIEWNODENUMBERS, CHECKED
  MENUITEM "&Messages\tM", CM_VIEWMESSAGES, CHECKED
  MENUITEM SEPARATOR
  MENUITEM "&Peek level...\tP", CM_VIEWPEEKLEVEL
 }

POPUP "&Help"
 {
  MENUITEM "&Index\tF1", CM_HELPINDEX
  MENUITEM "&Keyboard", CM_HELPKEYBOARD
  MENUITEM "&Commands", CM_HELPCOMMANDS
  MENUITEM "&Procedures", CM_HELPPROCEDURES
  MENUITEM "&Using help", CM_HELPUSING_HELP
  MENUITEM SEPARATOR
  MENUITEM "&About...", CM_HELPABOUT
 }

}

STRINGTABLE 
{
 CM_FILEEXIT, "Exit program"
 CM_VIEWPEEKLEVEL, "How often to update graphic node display"
 CM_NETLOAD, "Load previously saved network architecture"
 CM_NETSAVEAS, "Save current network's architecture to a file"
 CM_NETNEW, "Create a new randomly wired network"
 CM_TRAINERLOAD, "Load a training set from disk"
 CM_VIEWNODENUMBERS, "View nodes as numbers, or dots (slower)"
 CM_FILESTART, "Start training the net on the training set"
}

MENU_1 ACCELERATORS 
{
 VK_F1, CM_HELPINDEX, VIRTKEY
 "p", CM_VIEWPEEKLEVEL
 "m", CM_VIEWMESSAGES
 "t", CM_TRAINERLOAD
 "#", CM_VIEWNODENUMBERS
 "n", CM_NETNEW
 "s", CM_FILESTART
 "a", CM_NETADDCONNECTION
 "d", CM_NETDELETECONNECTION
 "r", CM_NETRIPNODE, ASCII
}

WNEURAL.RH

/*

WNEURAL.RH is part of the WNEURAL project.
Copyright (C)1995-99 Steven Whitney.
Published under GNU GPL (General Public License) Version 3, with ABSOLUTELY NO WARRANTY.
Initially published by http://25yearsofprogramming.com.

produced by Borland Resource Workshop

*/
#include <owl\inputdia.rh>

#define MENU_1	1
#define CM_NETRIPNODE	7
#define CM_NETDELETECONNECTION	6
#define CM_NETADDCONNECTION	5
#define CM_FILESTART	4
#define CM_VIEWNODENUMBERS	105
#define CM_TRAINERLOAD	104
#define CM_VIEWPEEKLEVEL	1
#define CM_NETNEW	3
#define CM_NETSAVEAS	103
#define CM_NETLOAD	102
#define CM_VIEWMESSAGES	2
#define CM_HELPABOUT	24348
#define CM_HELPUSING_HELP	24347
#define CM_HELPPROCEDURES	24346
#define CM_HELPCOMMANDS	24345
#define CM_HELPKEYBOARD	24344
#define CM_HELPINDEX	24343
#define CM_FILEEXIT	24342
#define MESSAGESWINDOW	3
#define IDC_TRANSEDIT	101

wneural.hpj

[OPTIONS]
CONTENTS=HID_CONTENTS
TITLE=Neural Net Help

[CONFIG]
BrowseButtons()

[FILES]
wneural.rtf

wneural.def

EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 8000
STACKSIZE 8000

The next page (Network.cpp) has the source code for the classes that implement the neural network.

 

 

Valid HTML 4.01 Transitional Valid CSS
View content labeling at ICRA.
Copyright ©2008 Steven Whitney. Last modified 10/07/2008.