Error registering a new learning rule

Hi

I am attempting to create a new learning rule and register it with the builder. I am getting the following validation error:

ValidationError: Connection.learning_rule_type: ‘SimDecay{<bound method SimDecay._descstr of <SimDecay at 0x11561f310>>}’ must be a learning rule type or a dict or list of such types

Is this a known error? If not, would it be useful for me to include a copy of my code to see if I’m doing something wrong?

Thanks for your help,

Katie

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)