// calculate fetches the information ID associated with the given information // sequence. In case the information sequence is not found within the underlying // storage, a new information ID is generated and used to store the given // information sequence. In any case the information ID is added to the given // context. func (c *clg) calculate(ctx spec.Context, informationSequence string) error { informationIDKey := key.NewNetworkKey("information-sequence:%s:information-id", informationSequence) informationID, err := c.Storage().General().Get(informationIDKey) if storage.IsNotFound(err) { // The given information sequence was never seen before. Thus we register it // now with its own very unique information ID. newID, err := c.Service().ID().New() if err != nil { return maskAny(err) } informationID = string(newID) err = c.Storage().General().Set(informationIDKey, informationID) if err != nil { return maskAny(err) } informationSequenceKey := key.NewNetworkKey("information-id:%s:information-sequence", informationID) err = c.Storage().General().Set(informationSequenceKey, informationSequence) if err != nil { return maskAny(err) } } else if err != nil { return maskAny(err) } ctx.SetInformationID(informationID) return nil }
func (c *clg) calculate(ctx spec.Context) (string, error) { behaviourID, ok := ctx.GetBehaviourID() if !ok { return "", maskAnyf(invalidBehaviourIDError, "must not be empty") } behaviourIDKey := key.NewNetworkKey("behaviour-id:%s:separator", behaviourID) separator, err := c.Storage().General().Get(behaviourIDKey) if storage.IsNotFound(err) { randomKey, err := c.Storage().Feature().GetRandom() if err != nil { return "", maskAny(err) } // Make sure the fetched feature is valid based on its key structure. The // key itself already contains the feature. Knowing the key structure makes // it possible to simply parse the feature from the key fetched from the // feature storage. These features are stored by the split-features CLG. // Changes in the key structure there must be aligned with implementation // details here. The current key structure looks as follows. // // feature:%s:positions // if len(randomKey) != 22 { return "", maskAnyf(invalidFeatureKeyError, "key '%s' must have length '22'", randomKey) } if !strings.HasPrefix(randomKey, "feature:") { return "", maskAnyf(invalidFeatureKeyError, "key '%s' prefix must be 'feature:'", randomKey) } if !strings.HasSuffix(randomKey, ":positions") { return "", maskAnyf(invalidFeatureKeyError, "key '%s' suffix must be ':positions'", randomKey) } // Create a new separator from the fetched random feature. Note that a // feature is considered 4 characters long and the random factory takes a // max parameter, which is exlusive. feature := randomKey[8:12] featureIndex, err := c.Service().Random().CreateMax(5) if err != nil { return "", maskAny(err) } separator = string(feature[featureIndex]) // Store the newly created separator using the CLGs own behaviour ID. In case // this CLG is asked again to return its separator, it will lookup its // separator in the general storage. err = c.Storage().General().Set(behaviourIDKey, separator) if err != nil { return "", maskAny(err) } } else if err != nil { return "", maskAny(err) } return separator, nil }
// calculate fetches the information sequence stored under a specific // information ID. The information ID is provided by the given context. func (c *clg) calculate(ctx spec.Context) (string, error) { informationID, ok := ctx.GetInformationID() if !ok { return "", maskAnyf(invalidInformationIDError, "must not be empty") } informationSequenceKey := key.NewNetworkKey("information-id:%s:information-sequence", informationID) informationSequence, err := c.Storage().General().Get(informationSequenceKey) if err != nil { return "", maskAny(err) } return informationSequence, nil }
func (c *clg) calculate(ctx spec.Context, informationSequence string) error { // Check the calculated output against the provided expectation, if any. In // case there is no expectation provided, we simply go with what we // calculated. This then means we are probably not in a training situation. expectation, ok := ctx.GetExpectation() if !ok { err := c.sendTextResponse(ctx, informationSequence) if err != nil { return maskAny(err) } return nil } // There is an expectation provided. Thus we are going to check the calculated // output against it. In case the provided expectation does match the // calculated result, we simply return it. calculatedOutput := expectation.GetOutput() if informationSequence == calculatedOutput { err := c.sendTextResponse(ctx, informationSequence) if err != nil { return maskAny(err) } } // The calculated output did not match the given expectation. That means we // need to calculate some new output to match the given expectation. To do so // we create a new network payload and assign the input CLG of the current CLG // tree to it by queueing the new network payload in the underlying storage. err := c.forwardNetworkPayload(ctx, informationSequence) if err != nil { return maskAny(err) } // The calculated output did not match the given expectation. We return an // error to let the neural network know about it. return maskAnyf(expectationNotMetError, "'%s' != '%s'", informationSequence, calculatedOutput) }
func (c *clg) forwardNetworkPayload(ctx spec.Context, informationSequence string) error { // Find the original information sequence using the information ID from the // context. informationID, ok := ctx.GetInformationID() if !ok { return maskAnyf(invalidInformationIDError, "must not be empty") } informationSequenceKey := key.NewNetworkKey("information-id:%s:information-sequence", informationID) informationSequence, err := c.Storage().General().Get(informationSequenceKey) if err != nil { return maskAny(err) } // Find the first behaviour ID using the CLG tree ID from the context. The // behaviour ID we are looking up here is the ID of the initial input CLG. clgTreeID, ok := ctx.GetCLGTreeID() if !ok { return maskAnyf(invalidCLGTreeIDError, "must not be empty") } firstBehaviourIDKey := key.NewNetworkKey("clg-tree-id:%s:first-behaviour-id", clgTreeID) inputBehaviourID, err := c.Storage().General().Get(firstBehaviourIDKey) if err != nil { return maskAny(err) } // Lookup the behaviour ID of the current output CLG. Below we are using this // to set the source of the new network payload accordingly. outputBehaviourID, ok := ctx.GetBehaviourID() if !ok { return maskAnyf(invalidBehaviourIDError, "must not be empty") } // Create a new contect using the given context and adapt the new context with // the information of the current scope. newCtx := ctx.Clone() newCtx.SetBehaviourID(inputBehaviourID) newCtx.SetCLGName("input") newCtx.SetCLGTreeID(clgTreeID) // We do not need to set the expectation because it never changes. // We do not need to set the session ID because it never changes. // Create a new network payload. newNetworkPayloadConfig := networkpayload.DefaultConfig() newNetworkPayloadConfig.Args = []reflect.Value{reflect.ValueOf(informationSequence)} newNetworkPayloadConfig.Context = newCtx newNetworkPayloadConfig.Destination = string(inputBehaviourID) newNetworkPayloadConfig.Sources = []string{string(outputBehaviourID)} newNetworkPayload, err := networkpayload.New(newNetworkPayloadConfig) if err != nil { return maskAny(err) } // Write the transformed network payload to the queue. networkPayloadKey := key.NewNetworkKey("events:network-payload") b, err := json.Marshal(newNetworkPayload) if err != nil { return maskAny(err) } err = c.Storage().General().PushToList(networkPayloadKey, string(b)) if err != nil { return maskAny(err) } return nil }