Let’s try our hand at writing a Cicada script to wrap around our C routine. It’s our first time scripting so we will probably have a few bugs.
NN.cicada
neural_network :: {
numNeurons :: int
numInputs :: numOutputs :: numHiddens
weights :: [] [] double
activity :: [] double
init :: {
if trap( { numInputs, numOutputs, numHiddens } = args ) /= passed then (
print("usage: myNN.init(inputs, outputs, hidden neurons)\n")
return 1 )
numNeurons = numInputs + numOutputs + numHiddens + 1
activity[^numNeurons]
weights[^0][^0] | to speed up the resize
weights[^numNeurons][^numNeurons]
return
}
run :: {
numArgs :: rtrn :: int
step_size :: learning_rate :: double
inputs :: outputs :: [] double
inputs[^1] = 1 // the 'bias' input
code
numArgs = top(args)
if trap(
inputs[^numInputs + 1]
inputs[<2, numInputs+1>] = args[1][*]
if numArgs == 4 then (
outputs[^numOutputs]
outputs[<1, numOutputs>] = args[2][*]
{ step_size, learning_rate } = { args[3], args[4] } )
else if numArgs == 2 then &
step_size = args[2]
else throw(1)
) /= passed then (
print("usage: myNN.run(input, step_size OR ",
"input, target output, step_size, learning_rate)\n")
return 1 )
if numArgs == 2 then &
rtrn = $RunNetwork(weights, activity, inputs, step_size)
else &
rtrn = $RunNetwork(weights, activity, inputs, step_size, outputs, learning_rate)
if rtrn == 1 then print("run() did not converge; try lowering step size?\n")
return
}
init(0, 0, 0)
}
We should save NN.cicada in same directory that contains the cicada application, start.cicada and user.cicada. start.cicada runs the command prompt, and user.cicada pre-loads a number of useful functions.
Assuming we are at Cicada’s command prompt, we can try out our new wrapper by typing:
> run("NN")
Error: left-hand argument expected in file NN
32: inputs[^1] = 1 // the 'bias' input
^
Hmm.. we’re obviously not finished with NN.cicada yet. Fortunately compile-time errors like the one above are usually easy to sort out. In our case we accidentally wrote a C-style comment ‘//’ in place of a Cicada comment ‘|’, which Cicada interpreted as a pair of division signs. Make the straightforward fix to NN.cicada
inputs[1] = 1 | the 'bias' input
and try again.
> run("NN")
Error: member 'numHiddens' not found in file NN
4: numInputs :: numOutputs :: numHiddens
^
We have made progress: NN.cicada successfully ‘compiled’ and began running --- before impaling itself on line 4 and duly filing a complaint. There’s no debugger, but we can often troubleshoot runtime crashes using the command prompt.
> neural_network.numHiddens
Error: member 'numHiddens' not found
With a little knowledge of the scripting language we would see that we tried to define numInputs and numOutputs to be of type numHiddens, which would be allowed except that numHiddens itself was never defined. What we meant to do was to define all three of these variables to be of type int. Make the following correction to line 4:
numInputs :: numOutputs :: numHiddens :: int
and re-run our script.
> run("NN")
usage: myNN.init(inputs, outputs, hidden neurons)
This time the script successfully ‘compiled’ and ran, or at least did something (unexpected) -- it printed out a usage message even though we never tried initializing a neural network. Let’s see if init() works when we’re actually trying to run it.
> neural_network.init(3, 4, 5)
>
So far so good(?). There should now be 13 neurons in our network (including the ‘bias’ neuron).
> neural_network.activity
{ }
So something is definitely wrong. At this stage we might want to look at a number of other variables that should have been set, and the easiest way to do that is to ‘go’ inside our network.
> go(neural_network)
> weights
{ }
> numNeurons
0
> go()
The last line takes us back to our normal workspace. So our init() call was a dud -- nothing happened. So let’s put a trace statement in the coding section of the init() function.. wherever that is... ho ho, well we forgot the code marker, which explains why there isn’t any coding section. That should go at the beginning of init(); the method should now begin:
init :: {
code
if trap( { numInputs, numOutputs, numHiddens } = args ) /= passed then (
etc. For the last time, go back and try
> run("NN"), neural_network.init(3, 4, 5)
> neural_network.activity
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
Finally we see what we were hoping for: an array of neurons, initialized to a resting state and ready to start processing.
Last update: May 8, 2024