Simula is a few things:
- A network modeling library.
- A dataflow programming language based on this network library.
- A graphical editor for networks, presently designed to be an editor for said programming language.
The Simula network model is defined in model.py, and contains the core
classes for that model.
All of the classes are centered around the Node, which is a vertex in a
graph. All Nodes have directed, labelled edges stored in members incoming
and outgoing as maps of sets. the labels come in pairs, intuitively
representing the label exiting one vertex and the label entering another; each
element of the set is a (vertex, vertlabel) tuple. It is an invariant that, if
(A, x) is in B.outgoing[y], then (B, y) is in A.incoming[x].
Nodes are typically contained in a Graph, which is little more than a set
of Nodes. Graphs allow a network topology to be saved to an ElementTree
via the .save() method, and later restored from an ElementTree using the
.restore() method. Both of these methods call implementations of the same in
the Graph's Nodes, which are expected to serialize the node to an XML
Element. Subclasses may override these methods (and call the base
implementation first); subclasses of Graph may also set the NODE_CLS class
member to the type of Node or derivative that it would like to use in
.restore(). The NODE_CLS constructor should have the same parameters as
Node.
To assist with network design and validation, Nodes have a schema member,
which, if not None, is a Schema object, which has an identifying name and
methods .getIns() and .getOuts() which return the expected incoming labels
and outgoing labels for edges to nodes with this schema as lists of
Connectors. A Connector is a simple immutable class which contains a name
(which functions as the label) and other optional parameters (as type and
unique) mostly used by the language. For convenience, Schemas also possess
.getInMap() and .getOutMap() methods which return mappings from names to
corresponding Connectors, though ordering information is lost.
The Simula executive (Simulator, Stepper, etc.) is in sim.py. Schemata
compatible with the language may be found in lib.py, which imports other
lib_*.py files based on the presence of certain supporting Python modules.
Many Producers and Consumers can be found in procon.py.
Executable graphs, or Simulators, inherit from Graph directly, and so can
be saved and restored as with Graph. The node class is SNode, a simulation
node, which extends Node by having members values, newvalues, and
props; the former two are used to hold current and next-tick values for
outputs from this SNode, and the latter contains the values assigned to this
node's properties (see below).
To execute a Simulator, first call .reset(), then .run(). If you would
like to only run it for a certain number of ticks (or until some condition),
instead call .step() followed by .advance() for each tick.
Simulators have, in addition to SNodes, busses (member bus and newbus),
Producers, Consumers, a Stepper, and a Monitor. The busses are accessed
from within the language through the GetBus and SetBus simulation schemata,
and are typically used to communicate values between the outside environment
and the simulation. Producers and Consumers (generally "transducers") are
similar to SNodes, but can influence the simulation rate (via
.stepsNeeded() and .delayNeeded()), and are typically used to interact with
some environment or other library. The Stepper determines how (and in what
order) SNodes' .step() and .advance() methods are called, and the
Monitor reports errors during the simulation in a flexible manner.
A SNode's functional behavior is almost exclusively managed by its
SimSchema, a Schema derivative that has support for Property objects and
an additional category (cat) member. Property objects represent schemata
for properties; various objects (including Simulators, SNodes, transducers,
etc.) have a props member which contains keys corresponding to a
Property name, whose type is that of the type member of the
Property, and whose value is initialized to default if not otherwise
set. The Property constructor also accepts arbitrary keyword arguments,
which become members of the object; this may be used to hint legal values
(such as min and max for numeric types) or encode documentation.
The file sim.py will, when called as a script, load a simulation from a file
specified as its only argument and .run() it until interrupted.
The Simula editor is completely contained in editor.py, but depends on all
other modules. It is based on PyQt5 (and so requires Qt5 to run). Called as a
script, editor.py opens the editor window. If given a filename as an
argument, it loads that initially; otherwise, it generates a sample simulation.
The editor is a multi-document interface with tabs and a palette; most of the interface is centered about the graphics view in the center, which visualizes the network and can be scrolled by clicking and dragging, or zoomed using the mousewheel. New nodes (with specified schemata) can be dragged in from the palette to the left, or created by invoking the context menu in an empty part of the scene and selecting "Add". Hovering over a schema in the palette will show its documentation, derived from the schema's docstring. Invoking the context menu on a specific node will allow one to access the "Properties" dialog or remove the node; properties can also be accessed by double-clicking the node. The "pins" of a node correspond to the inputs and outputs of its schema, and are ordered top to bottom; inputs are on the left, and outputs are on the right. Pins are colored for the type of data they produce, but the type system is presently mostly advisory. A pin with a cyan border is a "polyinput" that can accept multiple input connections; most other pins (without this border) can only accept one connection. To create a connection, click and drag between an output pin and an input pin; to delete a connection, click and drag between the connected pins. A wire, drawn as a curve, will indicate which connections are made.
Simulation properties may be accessed via "Properties" in the "Simulation" menu on the menubar. Soon, a simulation control dialog will also be accessible therein.
The "File" menu of the menubar contains the functionality to save and load the
simulations, as well as open new, blank simulations. Files so saved can later
be run with sim.py, and be edited with any standard XML or text editor.
"Quit" will exit the editor.
- Improve documentation
- Add more (useful) functionality to the programming language, including schemata and transducers for various Python data-handling libraries
- Libraries which need to be added or improved: socket, numpy, scipy, pyaudio, rtmidi2, PyQt5 (simulation-populated GUIs and controls); more may be requested
- Decouple the editor and programming language, such that the editor can be used to make "plain old" networks as needed.
- Allow the editor to run simulations
- Fix TODOs in various interface code
- Standardize schemata for transducers
- Standardize category and node namespace, especially for the
lib_*.pyextension libraries - Improve handling of "null" (
None) values in the simulation, and provide more intuitive input synchronization methods - Make subsimulation creation, editing, and interaction more intuitive (display editor in editor? display a file select dialog for the property?)
- Make the property editors in all places more intuitive and expressive
- Detect modifications to simulations in the editor in non-obnoxious, easily-handled ways
- Create a standard library of common, optimized functions/simulations as either files or nodes
- Test and optimize :)