1

I have a graph which represents a big circuit network. Each wire/device can either be "active" (it is - through some path of wires and devices - connected to power) or "inactive" (it isn't connected to power). No ground wires or voltages are modeled. It's either active or inactive.

There are different kinds of devices. Devices can have any number of plugs. plugs connect devices together through wires. The exact layout of the wires is not important. It only matters which plugs are connected together.
A few examples for devices:

  • Lamp
    Used for easily displaying certain states of the circuit. It has a single plug which controls the state of the lamp. As seen in the screenshots below active devices and wires are displayed in red, inactive in blue.
  • PowerSupply
    Always active. It has a single plug which will always output a signal. Therefore, a wire which is attached to that plug will always become active and devices attached to that wire will also become active.
  • Input
    Represents some way of interfacing with the outside world. Can be set active or inactive by the process which runs the simulation. It will reflect its state on its only plug.
  • Relay
    This device does something more complicated. It consists of multiple parts: It has a coil-plug which controls the state of what would be the coil in a real relay. It has any number of switches which are controlled by the state of the relay. Each has three plugs like an SPDT switch. When the relay is inactive, incoming signals from the normally-closed-plug will be forwarded to the common-plug and vice versa and signals from the normally-opened-plug will not be forwarded. When the relay is active it connects the common- and normally-opened-plug instead.

I would like to walk through this graph as fast as possible. The states of the devices and wires in the graph before the traversal is the "current generation". Traversing the graph calculates the next generation's states. After the traversal is complete the states of the devices and Wires are set to the new states. Updating the states is done in two parts (calculating the states and applying them) to avoid problems e.g. where a relay might change its state and affect the traversal immediately. Like when you run Game of Life but instead of writing the results into a new array and copying everything back once everything is calculated, you gradually overwrite the last generation's data and create wrong results.
The traversal should basically do this:

  • First assume that all the objects (devices, plugs, wires) will be inactive in the next generation. They will be marked as active when they are reached by the traversal.
  • Then find all the plugs which are definitely powered. Like the powersupply-plugs, among other devices which can output signals. These are the starting points for the traversal.
  • If it's allready marked as active, skip it. Objects can be reached multiple times by the traversal. It would result in infinite loops/recursions if that case would not be caught.
  • Mark it as active.
  • Notify the device it's attached to that the plug became active. Depending on what plug it is on what device, do something. e.g. the common-plug on one of the switches of a relay. If the relay is active, recurse for the normally-opened-plug, otherwise for the normally-closed-plug
  • Notify the attached wire, mark it as active and recurse for all other plugs which are attached to the wire.
  • When all starting points are done assign the "next generation" state to the actual state of each of the objects.

Here is an example of a simple circuit. Everything is inactive at first:
Input 0 Generation 0
In the next generation the wire-network on the left, the wire on the top and the left lamp all become active because they are in some way connected to power:
Input 0 Generation 1
Here I have activated the input on the bottom:
Input 1 Generation 0
In the next generation the relay becomes active. Mind how the wires on the right side did not change. The relay was still inactive when this generation was calculated:
Input 1 Generation 1
In the next generation the wires do change:
Input 1 Generation 2

The currently existing graph is not optimized for fast traversal. It is optimized for ease of editing and for showing more detailed information, like the little arrows on the red wires which show where the signal came from. And there are lots of "unnecessary" steps which could be optimized. There are devices which just connect wires to other wires in a different part of the circuit. This is just for optics as it makes it clearer which parts of the circuit form a logical unit. And because the wires need to be easily editable each bend or intersection is a device which does nothing functionally but can be dragged on the screen. The wire-network on the left side of the screenshots above is actually 4 separate wires and 2 of these pseudo-devices.
Because of this it is way too slow. With the biggest network I have I get at best 3 generations per second.

I am writing a program wich automates some testing of the circuits by setting the inputs, calculating generations until the circuit is stable and making sure the circuits are in the expected state. It would be way too slow to use the existing solution.

The initial setup of the graph and whatever huha needs to be done can be slow as it is only going to be done once and the structure of the graph is never going to change after that. But calculating the generations should be very fast. There should also be a way of detecting that nothing has changed because I don't know the signal latency of the circuits ahead of time.

What data should the graph hold and what tricks can I use to optimize for speed? (high memory usage is not a problem at all)
And how can I distribute the load on multiple threads? Doing it single-threaded is easy enough but how do I make sure multiple threads don't do something stupid, without ruining the performance by throwing locks everywhere? Maybe it is possible to determine in the setup-stage which parts of the graph should be done by what thread?

I use .NET (Framework 4.0) but any general tips are appreciated.

Niko O
  • 406
  • 3
  • 15
  • What is the purpose of the generation with the relay 'active' but not having any affect on its output? – Pete Kirkham Sep 27 '16 at 14:48
  • @PeteKirkham See the part about Game of Life. Each traversal calculates a new generation by looking only at the previous generation's state. If you use state which is currently updated you run into all sorts of troubles. In this case the result would depend on which part was traversed first. The coil plug first: the relay turns on and once the switch part is calculated the signal will be interrupted. The switch part first: The signal can pass through. Then the relay turns off. – Niko O Sep 27 '16 at 22:07

0 Answers0