Hi @Katie! Welcome to the forum.
Yes, it would be useful for you to provide your code. Based on your error, my guess is that you’re not creating both a LearningRuleType as well as an Operator for your learning rule.
Here’s a sketch of how to create a custom learning rule. You might want to look at nengo/learning_rules.py at master · nengo/nengo · GitHub and nengo/learning_rules.py at master · nengo/nengo · GitHub for more inspiration, as well as details on how to do things like add filtering to your inputs, have an error input (like we do with PES), and other more advanced features.
import nengo
import numpy as np
from nengo.builder.builder import Builder
from nengo.builder.operator import Operator
from nengo.builder.learning_rules import get_pre_ens, get_post_ens
class MyRule(nengo.learning_rules.LearningRuleType):
# you can add custom attributes, constructor, etc. here
# see nengo/learning_rules.py for examples of how we do current learning rules
# This attribute controls what the learning rule modifies.
modifies = "weights"
class SimMyRule(Operator):
# Currently, this is a simplified copy of SimOja in nengo/builder/learning_rules.py
# This class, along with the build_my_rule function below, will define how your
# rule is implemented.
def __init__(self, pre, post, weights, delta, learning_rate, tag=None):
super().__init__(tag=tag)
self.learning_rate = learning_rate
self.sets = []
self.incs = []
self.reads = [pre, post, weights]
self.updates = [delta]
@property
def delta(self):
return self.updates[0]
@property
def pre(self):
return self.reads[0]
@property
def post(self):
return self.reads[1]
@property
def weights(self):
return self.reads[2]
@property
def _descstr(self):
return f"pre={self.pre}, post={self.post} -> {self.delta}"
def make_step(self, signals, dt, rng):
weights = signals[self.weights]
pre = signals[self.pre]
post = signals[self.post]
delta = signals[self.delta]
alpha = self.learning_rate * dt
# This is where the action happens!
def step_simmyrule():
# perform forgetting
post_squared = alpha * post * post
delta[...] = -weights * post_squared[:, None]
# perform update
delta[...] += np.outer(alpha * post, pre)
return step_simmyrule
@Builder.register(MyRule)
def build_my_rule(model, my_rule_type, rule):
conn = rule.connection
pre_activities = model.sig[get_pre_ens(conn).neurons]["out"]
post_activities = model.sig[get_post_ens(conn).neurons]["out"]
model.add_op(
SimMyRule(
pre_activities,
post_activities,
model.sig[conn]["weights"],
model.sig[rule]["delta"],
learning_rate=my_rule_type.learning_rate,
)
)
with nengo.Network() as net:
a = nengo.Ensemble(100, 1)
b = nengo.Ensemble(100, 1)
conn = nengo.Connection(
a.neurons,
b.neurons,
transform=np.ones((100, 100)),
learning_rule_type=MyRule(learning_rate=1e-3),
)
with nengo.Simulator(net) as sim:
sim.run(1.0)