SYNOD: An Environment for Neural Systems Simulations Language Interface and Tutorial
Markus Diesmann1, Marc-Oliver Gewaltig2, and Ad Aertsen1 1 Department
of Neurobiology The Weizmann Institute of Science 76100 Rehovot Israel
2 Institut
fur Neuroinformatik Ruhr-Universitat Bochum 44780 Bochum Germany
Computer simulations of neural systems, ranging from very detailed compartmental models of single neurons to large scale neural networks are quite common. Often their results are used as evidence for or against a certain theory. However, many neurosimulations share a number of problematic features. Results from one laboratory are hard to reproduce in another, since dierent neuron or network models are used, each with their own speci c software and implicit assumptions. Also, the mapping from measures obtained in simulations onto their 'real world' counterparts is not always well de ned. From this we draw two conclusions: First, the simulations should allow for identical processing of simulated and experimental data, preferably using the same analysis tools; we refer to such simulations as in virtu experiments. Second, and more important, it should be possible to implement dierent models, not necessarily at the same level of description, within a single common scheme. With this goal in mind, we developed a new conceptual framework for neural simulations, using an object-oriented approach. Based on these principles we implemented a C++ class library for neural system simulations (SYNOD), together with a language interface (SLI) for the description and execution of in virtu experiments. In this document we describe the language interface, and provide a tutorial for using the SYNOD/SLI environment to perform neural network simulations.
This document was typset by the authors, using Scienti c WorkPlace with LATEX 2" and translated into PostScript using dvips. PostScript is a trademark of Adobe Systems Incorporated. Scienti c WorkPlace is a trademark of TCI Software Research. All other brand and product names are trademarks of their respective companies. Parts of the gure on the back cover were taken with permission from the September 1979 issue of Scienti c American (Edward V. Evarts Brain Mechanisms of Movement, gure by Bunji c September 1979 by Scienti c American, Inc. All rights reserved. Tagawa). Copyright
Preface SYNOD is a simulation environment, developed for the study of arti cial neuronal systems. Its main objective is to create a exible and computationally ecient tool to simulate realistic neural networks models, providing the means to combine dierent levels of discription (e.g. ionic channels, stochastic point processes, population activity) within a single network. The SYNOD project was started in 1993 in the framework of the MSc studies of Marc-Oliver Gewaltig and Markus Diesmann at the Institut fur Neuroinformatik at the Ruhr-Universitat Bochum (Bochum, Germany), and has been operational since early 1994. This document describes its present status and provides an introduction to its usage. Further developments on SYNOD are currently being pursued in a joint eort at the Center for Brain Research at the Weizmann Institute of Science (Rehovot, Israel) and the afore mentioned Institut fur Neuroinformatik in Bochum. Some of SYNODs principles regarding the exchange of point events and the role of time in the simulation were described in the MSc thesis [9]. Parts of the conceptual framework of SYNOD were presented at the Gottingen Neurobiology Conference [8].
Conventions The term SYNOD is used for the simulation kernel as well as for the entire project. In situations where it is important to distinguish between the simulation kernel and the language interface, we use SLI for the SYNOD language interface and SYNOD/SLI for the project. Monospaced type is used for SLI code fragments and computer output, sans-serif type indicates SLI objects, and italics are used to emphasize portions of the text and to mark parts which have to be changed by the user. Names of C++ classes follow the usual convention regarding the usage of capitals (e.g. SpikeDetector). The characters (, ), [, ], f, and g play an important role in SLI. There is sometimes confusion about the naming of these characters, which are typically used in pairs. We adopted the de nitions in [22]: Character Name (,) parenthesis [,] bracket f,g brace Since we use many examples in this text, sentences which end with a colon followed by the example are common. We typeset the example as a separate paragraph. Thus, the end of the sentence is clearly marked. In these cases we ommit the full stop.
iii
iv
Acknowledgements We thank Prof. Moshe Abeles for his advice and for helpful discussions during the initial phase of the development of SYNOD, and for supplying us with the source code of his simulator. We also thank Prof. George Gerstein and Dr. Michael Erb for helpful discussions on the design and usage of neural system simulators, and Michael Neef, Nava Shaya, and Shlomit Afgin for helping us bridge the gaps between dierent compilers, computers, and local networks. Partial funding was received from the Human Frontier Science Program (HFSP), the Israel Academy of Science, and the German "Bundesministerium fur Forschung und Technologie" (BMFT). Rehovot and Bochum, April 1995 Markus Diesmann Marc-Oliver Gewaltig Ad Aertsen
Contents Preface Contents 1 Introduction
1.1 Towards a Generalized Concept : : : : : : : : : : : : : : : : 1.1.1 Network and Neuron : : : : : : : : : : : : : : : : : : 1.1.2 Dierent Neuron Models in one Net: Inheritance : : 1.1.3 Neuron and NeuronModel : : : : : : : : : : : : : : : 1.1.4 Meaning of Time in the Network : : : : : : : : : : : 1.1.5 Recording and Manipulation Methods : : : : : : : : 1.1.6 Experiments in virtu : Sessions : : : : : : : : : : : : 1.1.7 Time Course of an Experiment and Reproducibility 1.1.8 A Graphical User Interface? : : : : : : : : : : : : : : 1.1.9 A First Implementation of the Concept : : : : : : : 1.1.10 Discussion and Outlook : : : : : : : : : : : : : : : : 1.2 The Language Interface : : : : : : : : : : : : : : : : : : : : 1.2.1 Problems : : : : : : : : : : : : : : : : : : : : : : : : 1.2.2 Portability : : : : : : : : : : : : : : : : : : : : : : : 1.2.3 Future Versions : : : : : : : : : : : : : : : : : : : : : 1.2.4 How to obtain SYNOD/SLI : : : : : : : : : : : : : : 1.2.5 Copyright Restrictions : : : : : : : : : : : : : : : : :
2 The SYNOD Language Interface
2.1 Resources : : : : : : : : : : : : 2.2 The Scanner : : : : : : : : : : : 2.3 SLI Objects : : : : : : : : : : : 2.3.1 Names : : : : : : : : : : 2.3.2 Literals : : : : : : : : : 2.3.3 Numbers (double, long) 2.3.4 Strings : : : : : : : : : : 2.3.5 Arrays : : : : : : : : : : 2.3.6 Procedures : : : : : : : 2.3.7 Comments : : : : : : : : 2.4 The Parser : : : : : : : : : : : 2.5 The Interpreter : : : : : : : : : 2.5.1 The Operand Stack : : 2.5.2 The Execution Stack : : 2.5.3 The Dictionary : : : : : 2.5.4 The Evaluation Loop : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : v
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
: : : : : : : : : : : : : : : :
iii v 1 2 2 2 2 2 3 3 3 3 4 4 5 5 6 6 7 8
9
9 9 10 11 11 11 11 13 13 14 14 14 14 14 14 15
CONTENTS
vi
3 SLI Tutorial
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
4.1 Introduction : : : : : : : : : : : : : : : : : 4.2 Units and Measures : : : : : : : : : : : : 4.3 Some Important Concepts : : : : : : : : : 4.3.1 The Network : : : : : : : : : : : : 4.3.2 Background Noise : : : : : : : : : 4.3.3 Models and Neurons : : : : : : : : 4.3.4 Methods, Sessions and Simulations 4.4 An Example Simulation : : : : : : : : : : 4.4.1 Planning a Simulation : : : : : : : 4.4.2 Setting up a Network : : : : : : : 4.4.3 Setting up the Background : : : : 4.4.4 Creating Neurons : : : : : : : : : : 4.4.5 De ning the Net : : : : : : : : : : 4.4.6 Performing the Simulation : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
: : : : : : : : : : : : : :
3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10
Introduction : : : : : : : : : : : : First Steps : : : : : : : : : : : : Expressions : : : : : : : : : : : : Working with the Stack : : : : : De nitions, Literals and Names : Procedures : : : : : : : : : : : : Loops and Control Structures : : Conditionals : : : : : : : : : : : : Strings : : : : : : : : : : : : : : : Files and Programs : : : : : : : : 3.10.1 Filenames and Programs : 3.10.2 File Objects : : : : : : : : 3.10.3 Writing Data into Files :
: : : : : : : : : : : : :
: : : : : : : : : : : : :
4 Neural Simulations in SLI
5 SLI Reference to Neural Simulations 6 SLI Command Reference 7 Error Handling Bibliography Index
: : : : : : : : : : : : :
: : : : : : : : : : : : :
17
17 17 18 19 20 21 22 23 23 24 24 25 25
27
27 27 28 28 28 28 29 30 30 31 32 32 33 36
39 47 57 59 62
Chapter 1
Introduction When systems are too complicated to be treated analytically, simulations provide a good way of gaining insight into their behaviour. Moreover, observables that cannot be measured directly can be estimated from the simulations. Evidently, simulation results have to stand the comparison with the experiment. Thus, it can be checked under which conditions and to what precision the model holds. Computer simulations of neural systems, ranging from very detailed compartmental models of single neurons to large scale neural networks have become quite common. Often their results are used as evidence for or against a certain theory. An ideal simulation is a numerical representation of an exact model of the system under consideration. Observables that cannot be measured directly due to technical problems or other limitations can be estimated from the simulation. However, as recording technology advances, results yielded from simulations have to stand the comparison with future experiment. By observing how the model reacts to stimuli and comparing this to results from real experiments, it can be checked under which conditions and up to which precision the model is correct. Despite their popularity, many neuro-simulations share a number of problematic features. Results from one laboratory are hard to reproduce in another. Often, dierent neuron or network models are used, with their own speci c software and implicit assumptions. Also, the mapping from measures obtained in simulations onto their 'real world' counterparts is not always well de ned. From this we draw two conclusions: First, the simulations should allow for identical processing of simulated and experimental data, preferably using the same analysis tools; we refer to such simulations as in virtu experiments. Second, and more important, it should be possible to implement dierent models, not necessarily at the same level of description, in a single common scheme. We developed a new conceptual framework for neural simulations, using an object-oriented approach. Based on these principles we implemented a C++ class library for neural system simulations called SYNOD and a language interface SLI for the description and execution of in virtu experiments. Section 1.1 provides a general overview of the entire project. In the subsequent chapters we focus on the language interface. A later text will report on the technical details and future developments of SYNOD. Chapter 2 technically describes the language and the structure of the interpreter. Chapter 3 presents a brief tutorial for the SLI language. Chapter 4 provides an overview how neural simulations can be set up and performed. Chapters 5 and 6 contain the reference manuals for the SYNOD related and the remaining SLI operators. 1
CHAPTER 1. INTRODUCTION
2
1.1 Towards a Generalized Concept for Neural Systems Simulations
1.1.1 Network and Neuron
Usually neural network models make no dierence between the single neuron model and the network itself. Mixing these two aspects results in several limitations. Neuron models can describe the properties of a neuron at several dierent levels (e.g. ion channel dynamics, stochastic approaches). Once a decision about the level of description has been made, a neural network model is usally bound to this level. On the other hand, a lot of the work a network simulator has to perform is common to most network models. Neurons must be updated, some information about the neurons connections and some interaction mechanism must exist. We propose the use of four main classes1: Network, NeuronModel, Neuron, and Connection. To illustrate this approach, we will omit the Connection class under the constraint that we include some limitations for the class of possible neuron models that can be implemented in this framework. Let us assume that the neurons interact only by the spikes they emit: considered from outside the neuron, a spike is a point event. A connection between two neurons can be described by two parameters: a weight and a delay. All a neuron "sees" from the rest of the network at a certain point T in time, is the weighted sum over all point events arriving at the neuron. In the full formulation, the class Connection will ensure that neurons with dierent types of output can communicate with each other.
1.1.2 Dierent Neuron Models in one Net: Inheritance
In the simpli ed example, a neuron consists of a state vector Xv , and a list of target neurons with information about the particular connections (weight, delay). Further, a time-evolution operator that transforms the state of the neuron at time T to the state at time T + h, and nally a mechanism which computes the in uence of the rest of the network on the neuron at time T . Dierent neuron models can have dierent time-evolution operators and dierent state vectors. However, the remaining parts are the same for all neuron models. The technique of using a base class Neuron to build particular neuron models Neuron A and Neuron B is called derivation. Class Neuron A is derived from class Neuron.
1.1.3 Neuron and NeuronModel
Considering a network of neurons of a particular model, we notice that usually these neurons have a lot in common. Characteristic for a particular neuron model is the time-evolution operator, for example a set of dierential equations. The neurons dier only in their state vector Xv . This allows us to make a distinction between e.g. NeuronModel A and Neuron A. Neuron A basically contains the state vector. NeuronModel A contains all the (constant) parameters common to all neurons belonging to this model. In addition, it contains the time-evolution operator and a method for creating and initializing the neuron. Whenever the neuron is asked to change its state, it calls the operator contained in its neuron model class.
1.1.4 Meaning of Time in the Network: Ensuring Appropriate Scaling
The network is simply a list of neuron models plus a list of neurons. It is the Network class that ensures the parallel update of the neurons so that the network's state vector consistently transforms from its value at time T to the value at time T + h. In some neural network models the meaning of time is not clear. It is hard to interpret the results on a real time scale, and comparing the dynamics of dierent neuron models may be even harder. In our framework the class Network controls the interpretation of the computation step size. A (member) function 1
For a review of the terminology in object oriented design methods see [14] and [19].
1.1. TOWARDS A GENERALIZED CONCEPT
3
sets the stepsize h in which the time-evolution operator U (T; T + h) advances the (system) neuron in time. The Network is able to do this by calling a function TimeScale of every neuron model in the list of models. By this general mechanisms, even models which are not based on dierential equations can be scaled to the appropriate stepsize.
TimeScale
1.1.5 Recording and Manipulation Methods
So far, we have only described the objects needed to model the (neural) system in question. In an actual experiment the experimenter faces two distinct systems: First, the system to be examined and second, the machinery which is used to perform the observations on the system.Typically, the experimenter uses special technical devices to apply dierent types of stimuli and to record dierent dynamic variables. These devices can be quite complex, and the precise result of a measurement may well depend on their parameters (e.g. a spike detector). If we want to be able to compare results from 'real' experiments with simulated data it is obviously, necessary to make models of the dierent recording and manipulation methods and to incorporate these in the simulation. Base classes RecordingMethod and ManipulationMethod are derived from class Method, which contains the list of neurons the method has to be applied to, and a virtual action function. Member function Apply calls the action function for every neuron in the list.
1.1.6 Experiments in virtu : Sessions
Now, we have to combine the model of our neural system and the models of all methods to be applied simultaneously to a virtual experimental setup. Such an object is called a Session. It contains a pointer to the Network and a list of methods. Session provides a function Simulate to advance the system in time and allows the associated methods to interact with the neurons in every time step.
1.1.7 Time Course of an Experiment and Reproducibility: a Description Language
The description of our experimental setup, all the information which is collected in one Session, is static. Again, in a 'real' experiment the situation is more complicated. Stimuli may have to be applied at dierent times, and dierent observables are of interest at dierent stages of the experiment. We propose a full scale programing language to describe the time course of the entire experiment and all parameter settings. This allows us to formalize the description of a complete simulation in a single ASCII le, easily readable by the user. Simulations can be reproduced by just executing this le again. Exact descriptions of simulations can be exchanged between laboratories and old simulations can be used as building blocks for new, more complex ones. An interpreter for this language allows the user to build up and test a simulation step by step, and to get informtion about certain parameter settings in an interactive way.
1.1.8 A Graphical User Interface?
Most window systems (e.g. Motif) are prototypes of object oriented libraries. The interface to the application, however, is in most cases hopelessly intermingled with the code of the application. These days, the average life time of a window system is very limited, so the risk of having to rewrite the whole project due to environment changes is very high. Thus, one would like to have a clear-cut separation between the simulator and its GUI. We suggest that this interface could also be language-based, like for example Display-PostScript (DPS). This would make the two parts independent of each other. The simulator could be used without the GUI and dierent GUIs for dierent Window systems could co-exist. At the same time, this concept solves another problem of GUIs. Typically, the user sets up a simulation by mouse action and typing of parameters in dialog boxes. Usually, these actions eect the system immediately and produce graphical and numerical responses for the user. The problem is to invent a mechanism which makes it possible
4
CHAPTER 1. INTRODUCTION
to reproduce an interactive session. A language based interface provides a natural solution. As the session proceeds, commands are sent to the simulator. If some mechanism exists which record this sequence of commands (preferably in an ASCII le), the experiment can be repeated just by loading this le.
1.1.9 A First Implementation of the Concept
We have implemented most of the framework described above, in the form of a C++ library, called SYNOD. However, it implements only the simpli ed version of the framework, omitting the Connection class. An implementation of the description language, called SLI (SYNOD Language Interface) is also available. The current version of the SYNOD/SLI system was tested with dierent neuron models in simulations ranging from detailed single neuron investigations to complex syn re simulations, involving several thousand neurons (e.g. [3], [6], [7], and [11]).
SYNOD The C++ library SYNOD de nes the classes and procedures described above. It allows the formulation of complex neural simulations with comparatively short C++ code. The extension of the library with new neuron models and methods is possible without much eort.
SLI SLI is an interpreter which understands a set of commands and uses them to call procedures from the SYNOD library. SYNOD then performs simulations according to the rules speci ed by SLI. It was quite clear from the beginning that parts of SYNOD would change in future versions. This in uenced the layout of the interpreter. Adding new C++ functions to SLI is easy and only very little understanding of the principles underlying the interpreter is necessary. There is no conceptional dierence between user added and internal functions. SLI uses a language concept very similar to the one used by the page description language PostScriptTM. It includes procedures, variables, and control structures to allow for the setup of complex simulations.
1.1.10 Discussion and Outlook
The project outlined above consists of three layers: SYNOD, SLI, and a GUI. Because the basics of the rst two layers have now reached a reasonably stable state, current work focuses on the development of a GUI. The current implementation of the language interface is purely directive, meaning that SLI sends commands to SYNOD but is unable to react actively to signals from the simulation kernel. For the described concept of a GUI, the language has to be extended in this direction. Methods have to be able to send appropriate signals to SLI. The future development of SLI in general is outlined in section 1.2.3. In SYNOD itself several aspects still have to be formalized more thoroughly. A particular problem is, that all neuron models provide dierent sets of observables. One solution to this problem is to declare a superset of all observables in the base class. This approach is called fat interface. However, a more elegant solution is desirable. Furthermore the user may want to change parameters of the neuron models at run time. Again, dierent neuron models may have dierent parameters. The main problem here is to nd a formulation which can easily be used by SLI without the need for too much run time type information. It is very tempting to use recording methods as a mean for online analysis, for example an online dot-display. Yet, this practice would not conform with the idea that simulated and experimental data should be analysed and evaluated using the same tools. A closer interaction between the simulator and an analysis tool like NDA [21] could solve this problem.
1.2. THE LANGUAGE INTERFACE
5
1.2 The Language Interface It was quite clear from the beginning that parts of SYNOD would change in future versions. This in uenced our layout of the interpreter. Moreover, the need for eciency and the level of interaction between the interpreter and the simulator will force us to add new procedures to SLI which have to be implemented in C++. Hence, adding new C++ functions to SLI should be easy, and require only very little understanding of the principles underlying the interpreter. Furthermore, there should be no conceptional dierence between user added and internal functions (e.g. the stack procedure which dumps the contents of the operand stack to the standard output). Preceding investigations showed us that in order to be able to describe complex experiments, a full scale programming language with control structures, loops, arithmetic operations, and variables would be necessary. It should be possible to build up more complex setups from simpler ones. This lead us to the conclusion that it should also be possible to formulate new procedures in the language itself. In order to keep the implementation simple, we decided to use reverse polish notation (RPN) with a syntax very similar to the PostScript language. It meets all the requirements:
It can easily be implemented It provides a good performance It provides procedures, arrays and variables. A detailed description of RPN languages and their underlying principles can be found in [2],[12], and [5].
1.2.1 Problems In the current implementation of the GNU C++ compiler g++ (2.6.x), there is no consistent way of resolving templates. Without precautions, it is very dicult, if not impossible to predict, when a certain instance of a template is created and when the complete template (declaration and de nition) is needed. This situation is described in more detail in the g++ Manual, pages 164 . Several strategies are proposed to overcome this problem until it will nally be solved (announced for Version 2.7.0). We adopted the approach to control explicitely when and where a template is resolved. Each instance of a template is contained in a small le, which can then be linked into the program. The les contained in the folder templates have the only purpose to force an explicit instantiation of a given template. For this to work, the source codes have to be compiled with the compiler option -fno-implicit-templates. Hopefully, these les will become obsolete, after a consistent method of template resolution has been implemented. Error handling of SLI is still very rudimentary, but for most functions, especially the network functions, parameter and typechecking are consistently done. The string functions, however, are less safe. This necessitates careful checking of the parameters on the part of the user. Consistent error handling is more complicated in the lower levels of SYNOD/SLI. We will elaborate the error handling as soon as the exception mechanisms of C++ are more widely available. The current version of SLI does not have a proper prompt. Thus, it is sometimes dicult to see, when the interpreter is ready to receive new commands. The absence of the prompt is not an accident. It has its roots in the way the scanner - parser - interpreter - cycle is implemented. Usually the prompt is implemented together with a simple line editor in the language itself [16]. One of the next versions of SLI will provide this feature (see section 1.2.3 version 1.4.0).
CHAPTER 1. INTRODUCTION
6
Finally, we kindly request all users to report any bugs or problems, preferably with clear documentation regarding the circumstances under which they occured to either of the two email addresses:
[email protected] [email protected]
1.2.2 Portability
The current version of SLI is 1.2 and can be compiled with GNU g++ version 2.6.0 or higher. We have successfully compiled SLI under SUN OS4, SUN Solaris, SG Irix, and IBM OS/2. There should be no problem in porting SYNOD/SLI to other systems, since the source is completely written in ANSI C++. It should be legal to use all features accepted by the ANSI/ISO committee ([18], section 6.2). However, also in future versions we will be much more restrictive, because currently only a few compilers support all C++ features (see [18] for a discussion of the future development of C++).
1.2.3 Future Versions
The current version of SYNOD/SLI is far from complete. Right from the beginning of its development, parts of the system have been used in research. For this reason the current state of development also re ects the needs of the ongoing research projects the authors are involved in. These restrictions provided the possibility to validate the usability of the system from very early stages of its development on. Here we want to work out a schedule for basic changes and new features in future versions. The idea is to supply the reader with some form of guideline about what can be expected and, more important, what can not be expected. However, it has to be pointed out that this schedule is not xed. Rather, it is likely that it will be re ned with every new version. Moreover, it does re ect a time table only in the sense that versions with a higher index will appear later. The SLI version index consists of three parts: x.y.z . For a new version, one of the three parameters x, y, z of the current version will be increased by one and the parameters to the right of it set to zero. Parameters will be increased according to the following rules: x: reimplementation of major components changes in the syntax, major internal changes y: implementation of additional functions xes of conceptional bugs z: bug xes minor extensions
Schedule of future versions 1.3.0 In this version we intend to make several smaller changes to complete and to elaborate the extensions made in 1.2.
1. Within the framework of a stack machine, error handling can be implemented in the following way: A function that detects an error pushes a name for this error on the execution stack. This name is associated with a function which handles the error (e.g. cleans the execution stack, and prints a message). The current version already uses this concept, but only a subset of the operators actually use it. Error handling will be extended to a broader range of operators.
1.2. THE LANGUAGE INTERFACE
7
2. Users are explicitly encouraged to add their own C++ written SLI-functions. In principle this can be done by rst creating an appropriate SLI-function name and afterwards, by the use of the dictionary, associating a pointer to the C++-function to it. In version 1.2 the new names have to be declared in several les. This will be made easier in 1.3 . 3. Array access. In version 1.2 we introduced the string operators get, getinterval, put, and putinterval to allow access to string elements and substrings. In 1.3 it will be possible to apply these operators also to arrays. 1.4.0 This version is mainly devoted to the improvement of the interactive mode of the interpreter. 1. Implementation of operator token. The token function takes a string from the stack and returns the rst token contained in the string and the remaining string. To implement this function a generalization of class CharBu is neccesary. From a common base class CharBu two classes CharBuFile (the current class) and CharBuString can be derived. This operator allows consistent type conversion and input evaluation by providing SLI access to its own scanner. 2. A prompt and a simple line editor. With the aid of the token function and a function which reads a character directly from stdin we are now in a position to supply SLI with a prompt. The prompt will be ]t (] followed by a space). Also a simple line editor (using the UNIX termcap library) can now be de ned. The input cycle itself should be a SLI program (see [16] for an example). There are three other major additions which we have not scheduled yet. Further planning is necessary to decide about the order of their implementation: A link to MatLab. For excessive matrix operations and more advanced numerical computations a link to MatLab's computational engine should be helpful. A link to the NDA [21]. The analysis of data generated by SYNOD/SLI could presumably be made more easy by a direct link to the NDA program. An LL(1) parser [5]. The replacement of our current parser by an LL(1) parser would give us the possibility to describe composite objects in a more formal way and to easily add new language constructs which can not directly be interpreted by a stack machine. For example, we can think of in x mathematical expressions (e.g. delimited by ) like . The parser builds up a parse tree which is then be read out by the (RPN) interpreter, in this example 5 3 4 add mul (see e.g. [12]).
1.2.4 How to obtain SYNOD/SLI
SYNOD/SLI can be downloaded from our ftp sites at the Weizmann Institute of Science, Israel and at the University of Bochum, Germany. These are anonymous ftp sites, but you may have no permission to read any directories, so please follow the instructions below:
SYNOD/SLI is available at
ftp cd get gunzip tar -xvf
Ruhr Universitat Weizmann Institute Bochum of Science ftp.neuroinformatik.ruhr-uni-bochum.de iris.weizmann.ac.il
name : anonymous password : your email address pub/outgoing/gewaltig pub/brain/synod synod-x.y.tar.gz synod-x.y.tar.gz synod-x.y.tar
CHAPTER 1. INTRODUCTION
8
For x.y you should substitute the rst part of SYNOD/SLI's version number x.y.z (see section 1.2.3 for the de nition of version numbers). The current version number is 1.2.0 . Beware: Since we included some binaries, this le is rather long (approx. 1MB), so make sure that the net is not too busy when you download the data. The archive le containing SYNOD/SLI and the documentation is called synod-x.y.tar.gz .
This text is also available in the same directory as sliman.ps.gz
and will be updated more frequently.
1.2.5 Copyright Restrictions
SYNOD itself is public domain and protected by the GNU license agreement. However, we presently hesitate to distribute the full source code of SLI. Maybe we will make it public domain, once it has reached a more stable state and is published to some extent. Nevertheless, we do not want to slow down research and are very much interested in the development of a GUI. This is why we organized the interpreter in a way that only a minimal portion of the source code is necessary to enable users to integrate their own C++ procedures. Because we still have to make some changes to these parts of the program, we currently distribute a compiled version without any source code.
Chapter 2
The SYNOD Language Interface This section describes some technical aspects of the SYNOD language interface (SLI). It gives full access to SYNOD's main features and allows the setup and evaluation of network simulations. SLI uses many concepts from the PostScript language. Like PostScript, SLI uses reversed polish notation (RPN) for the execution of commands. The basic commands and most of the control structures of SLI are adapted from PostScript. Additionally, SLI contains a number of structures and commands which enable the de nition and execution of complex network simulations. For a more detailed introduction into the general concepts, we refer to the "PostScript Reference Manual" [2]. Like PostScript, SLI 'consumes' a program as it reads it. SLI does not create an internal representation of the program the way procedural programing languages like C or C++ do. The state of the SLI interpreter is determined by the state of its stacks and its dictionary.
2.1 Resources There are two ways to invoke the SLI interpreter. The rst way is to start the interpreter and to enter commands directly via the keyboard. The second way is to read the instructions from a le that contains SLI commands. Such a le will be called program. The le extension used for SLI programs is '.sli'. The environment variable SLIHOME contains the default path from which SLI programs are read. If a le cannot be found in the directory speci ed in SLIHOME, a second directory, speci ed in the environment variable SLIUSER is searched. The current version of SLI is not able to handle lenames containing drive speci cations (e.g. a:/test.sli).
2.2 The Scanner The scanner reads characters from the input stream and groups them into tokens. Tokens are described in section (2.3). This section describes, how the scanner reads tokens. The scanner can be implemented as a DFA [5], with a small addition to handle balanced parenthesis inside strings where the parenthesis itself are the quotation characters. The input stream is grouped into tokens. Objects in the input stream are preferably separated by white spaces, although it is not in all cases necessary. The scanner separates two objects, whenever an unique choice can be made. Using a symbol processor (see section 2.3.4), the scanner translates the text representation of names and literal names to their terminal id. The symbol processor is also responsible for the handling of strings. First the text representation of the string is sent to the symbol processor. The symbol processor then returns an id for the string to the scanner. Finally, the scanner passes a token with type string and token value id to the parser. 9
10
CHAPTER 2. THE SYNOD LANGUAGE INTERFACE
2.3 SLI Objects SLI objects are stored in a data structure called token [4]. A token contains a particular piece of data and information about its generic type. These types are called terminals and can be one of the following: 1. array 2. beginarray Marks the begin of an array. 3. beginproc Marks the begin of a procedure object. 4. endarray Marks the end of an array. 5. endproc Marks the end of a procedure object. 6. endsymbol The end of the current input stream has been reached. 7. func C++ function. 8. i le User input stream. 9. literal Literal name. 10. manipmethod Manipulation method. 11. manipsession Manipulation session. 12. name 13. nil The NoToken, it is used internally to mark the end of arrays and procedures. Note, the symmetry between arrays and strings (see section 2.3.4). 14. null Empty token (NullToken). 15. o le User output stream. 16. proc Procedure object. 17. recordmethod Recording method.
2.3. SLI OBJECTS
11
18. recordsession Recording session 19. session Combined session. 20. string 21. double 22. long 23. xi le Executable input stream. 24. xproc Executable procedure. Some of these terminals specify begin and endsymbols of aggregate types like arrays and procedures; they are used for communication between scanner and parser. The parser hands down only a small set of all possible terminals, in particular: name, literal, double, long, string, array, and endsymbol. The remaining types are used by the interpreter. We will now describe in more detail those terminals that have a lexical representation and are used by the scanner:
2.3.1 Names Names are objects that start with a letter and may contain digits or underscores . As the word indicates, names name SLI objects. Every SLI object is assigned a name that is used for referencing this object. Some objects like les and methods can be dynamically created. Here the user has to assign an appropriate name with the operator def.
2.3.2 Literals
Literals or literal names are names which start with a slash =. They are used when the actual name of a variable is needed in contrast to its value. They are comparable to lvalues in compiler generation theory (cf. [5] and [13]). They follow the same lexical conventions as names, except for the leading slash.
2.3.3 Numbers (double, long) SLI distinguishes two types of numbers: long integers (we will call them just integers) and double oats (doubles). Numbers follow the C conventions as described in [13]. Conversions to the internal format are performed by C/C++ standard library functions.
2.3.4 Strings Strings are enclosed in parentheses (and ). They may contain any character, except for single parentheses. Balanced pairs of parenthesis, however, are possible. Line feed characters are inserted into the string. End of le (eof ) characters are not allowed inside a string. The backslash escapes nn and nt are now implemented. Nesting strings are a specialty of PostScript (and SLI). Strings are created by the Scanner (see section 2.2), or by the SLI string operators.
CHAPTER 2. THE SYNOD LANGUAGE INTERFACE
12
Examples: (This is a string) (This is a string (like before)) (This is a string with a line feed)
(This is a stringnnwith a line feed)
PostScript and SLI Strings
Although strings in PostScript and SLI look exactly the same, there is a fundamental dierence between them (the same is true for arrays). In SLI strings, names and all objects which are composed of characters are handled by a single machinery: the ESP (ElementStringProcessor). The ESP can operate on strings of objects of any kind. The machine operating on strings of characters is derived from this template and called SP (SymbolProcessor). The ESP implements all low level operations on strings, controls memory allocation and garbage collection. From the point of view of the ESP, strings are unique objects, each represented by an integer value (id). Copying a string object only means to copy the integer value representing it and to increase a reference counter. Only if the ESP is asked to change a string, which is referenced more than once, the contents is copied. The ESP changes the copied string and generates a new integer id for it. All other references to the old value of the string keep the old value. As a consequence operations with xed strings like comparing and copying are fast, while access to parts of the string and the insertion of a new string are slow. An object composed of many strings, where most of the strings appear more than once (like a list of tokens) can be coded eciently. Unlike in PostScript, changes made in a copied string do not aect the contents of other variables containing a copy of the same string. Seen from the point of view of the user: In PostScript, copying a string means to copy the pointer to it, in SLI it means to copy the contents. In SLI it is not necessary to allocate a string of xed length with the string command before using it. ESP automatically increases the size of the string if necessary, and lls unused elements with the NullCharacter which can be determined by the user. Note, the NullCharacter is not the NoCharacter which also has to be de ned by the user. The user is not allowed to use the NoCharacter because ESP marks the end of strings with it (a natural choice for NoCharacter is (char)0). The user is allowed to use the NullCharacter as a normal character ((char)32 may be a good choice).
Dierences in detail
1. There is no need for a string function in SLI. Strings are created when they are needed. For example, an attempt to insert 'A' at position 3 into an empty string () 3 65 put, results in a string of length 4. The rst 3 characters (positions 0 to 2) are lled with the NullCharacter (ttt A). 2. In contrast to the PostScript version of put and putinterval, the SLI versions return the target string. 3. In SLI there is no need for a special copy function. However, the eect of PostScript copy function (apart from its side eects) can be implemented by putinterval. 4. cvi and cvr are implemented, but do not work correctly in every situation. For example, string (3.3e1) cvi gives 3 and not 33 as expected (see [2]). This is because we are simply using the C/C++ input operator and not the SLI Scanner to perform this transformation. In version 1.3, where the token function (which is essentialy a call to the Scanner) will be available, we will x this problem.
2.3. SLI OBJECTS
13
COMSKEE style string operations
The PostScript style string operations are designed for strings of static size. However, the symbol processor in SLI supports dynamic strings. In a later version we will implement the two string operators of the COMSKEE language [15], taking full advantage of dynamic strings. The SLI call of these operators are: 1. string i j chskget. Returns substring string(i),-,string(j) of string, or the empty string if i>j. 2. string1 i j string2 chskput. Returns string1, where substring string1(i),-,string1(j) is replaced by string2. If i=j+1 string2 is inserted into string1 starting at position i. Note, in COMSKEE string indices run from 1 to length. However, in SLI indices start with 0 and the last index is length-1. Before an operator is executed, SLI carries out an index check. Valid indices are 1 ] exec Hello World
We see, that the procedure is pushed on the stack and is executed by the command exec. Note that, once a procedure is pushed on the stack, there is no way of examining it anymore. The output operator = is not capable of displaying the contents of a procedure object. Once the procedure is executed, it is removed from the stack and cannot be used anymore. This is a problem if we want to execute the procedure more than once. Of course, we could use the dup operator to create copies of the procedure on the stack, but this is certainly not a very ecient solution. Like any other object, procedures can be assigned to names for later and/or repeated execution, as shown in the next example: ] /MyProc ] { ] (Hello World \n) = ] } def ] MyProc Hello World
Procedure objects can be considered as subroutines. Once assigned to a name, they can be called over and over again, whenever needed. This provides the possibility to write modular programs. But beware, it is easy to create an in nite loop that traps SLI forever. So don't try the following example1 (or try it and kill SLI with [Ctrl-C]): /ProcA { ProcB } def 1
% call a subroutine
From now on we will ommit the prompt in procedure de nitions.
CHAPTER 3. SLI TUTORIAL
22 /ProcB { ProcA } def ] ProcA
% call another subroutine
% lost in space...
ProcB could have been de ned before ProcA, since procedures are just arrays of tokens. Unlike many procedural languages such as Pascal or C, SLI does not require forward declarations,. The only important rule is, that a function is de ned before it is actually called.
3.7 Loops and Control Structures Loops and control structures are commands that take procedure objects as arguments. The simplest loop is performed by the command loop: ] {(Hello World \n) =} ] loop Hello Hello Hello Hello Hello Hello Hello Hello
World World World World World World World World
loop performs the procedure repeatedly and thus in the example, an in nite succession of sentences Hello World is printed. The only way to leave a loop-structure is to execute the command exit inside the procedure. This is illustrated in the following example: ] 0 ] { 1 add dup ] loop
(Hello World \n) = 10 eq {exit} if }
It prints the ten times the sentence. First, the initial value 0 is pushed on the operand stack. The procedure adds 1 in each cycle and takes care that one copy of the counter stays on the stack to serve as the initial value for the next cycle. After the message has been printed, the stop value 10 is pushed and is compared with the counter. If the counter is equal to 10, the nested procedure is executed. This procedure then executes the command exit, and interrupts the loop. The last example can be implemented much easier, using the repeat command. repeat takes two arguments: a positive integer, and a procedure object. The integer determines how often the procedure is executed. Thus, in order to print ten times Hello World, we write: ] 10 ] { (Hello World \n) = } ] repeat
Sometimes one needs to know the counter of the loop. Also, one may want to in uence the step size of the iterations. For this purpose, SLI oers a for loop, which is called as follows: start step stop proc for
3.8. CONDITIONALS
23
for executes the procedure proc as long as an internal counter, initialized to the start value and incremented by step, has not exceeded the stop value (please refer to reference [2] for the exact termination conditions). In each cycle, the current value of the counter is pushed automatically. This value can be consumed by the procedure. Actually, in very long loops, the counter must be removed by the procedure in order to avoid stack over ow. The following example prints the rst ten square numbers: ] 1 1 10 ] { dup mul = (\n)= } ] for
3.8 Conditionals Conditionals are used, when the execution of a command should depend on certain variable conditions, such as the state of a variable or the relation between two objects. In the last section we have already seen an example of a conditional statement. SLI oers two commands for this purpose: 1. if if takes two arguments, a boolean and a procedure object. The procedure object is executed only if the boolean equals true. 2. ifelse ifelse takes three arguments: rst a boolean and then two procedures. The rst procedure is executed if the boolean is true, and the second procedure is executed if the boolean is false. Booleans are objects which take only one of the two values true or false. SLI oers a full set of comparison operators and the logical operators and, or, and not. The following table shows the available comparisons and their C equivalents. All comparisons take two arguments or arbitrary type and push a boolean (true or false). SLI eq ne gt lt ge le
C/C++ ==
!=
> < >=