Example #1
0
func (t *tracker) CLGNames(CLG systemspec.CLG, networkPayload objectspec.NetworkPayload) error {
	destinationName := CLG.GetName()
	sourceIDs := networkPayload.GetSources()

	errors := make(chan error, len(sourceIDs))
	wg := sync.WaitGroup{}

	for _, s := range sourceIDs {
		wg.Add(1)
		go func(s string) {
			behaviourNameKey := key.NewNetworkKey("behaviour-id:%s:behaviour-name", s)
			name, err := t.Storage().General().Get(behaviourNameKey)
			if err != nil {
				errors <- maskAny(err)
			} else {
				// The errors channel is capable of buffering one error for each source
				// ID. The else clause is necessary to queue only one possible error for
				// each source ID. So in case the name lookup was successful, we are
				// able to actually persist the single CLG name connection.
				behaviourNameKey := key.NewNetworkKey("behaviour-name:%s:o:tracker:behaviour-names", name)
				err := t.Storage().General().PushToSet(behaviourNameKey, destinationName)
				if err != nil {
					errors <- maskAny(err)
				}
			}

			wg.Done()
		}(string(s))
	}

	wg.Wait()

	select {
	case err := <-errors:
		if err != nil {
			return maskAny(err)
		}
	default:
		// Nothing do here. No error occurred. All good.
	}

	return nil
}
Example #2
0
func (n *network) Calculate(CLG systemspec.CLG, networkPayload objectspec.NetworkPayload) (objectspec.NetworkPayload, error) {
	n.Log.WithTags(systemspec.Tags{C: nil, L: "D", O: n, V: 13}, "call Calculate")

	outputs, err := filterError(reflect.ValueOf(CLG.GetCalculate()).Call(networkPayload.GetCLGInput()))
	if err != nil {
		return nil, maskAny(err)
	}

	newNetworkPayloadConfig := networkpayload.DefaultConfig()
	newNetworkPayloadConfig.Args = outputs
	newNetworkPayloadConfig.Context = networkPayload.GetContext()
	newNetworkPayloadConfig.Destination = networkPayload.GetDestination()
	newNetworkPayloadConfig.Sources = networkPayload.GetSources()
	newNetworkPayload, err := networkpayload.New(newNetworkPayloadConfig)
	if err != nil {
		return nil, maskAny(err)
	}

	return newNetworkPayload, nil
}
Example #3
0
func (a *activator) Activate(CLG systemspec.CLG, networkPayload objectspec.NetworkPayload) (objectspec.NetworkPayload, error) {
	a.Log.WithTags(systemspec.Tags{C: nil, L: "D", O: a, V: 13}, "call Activate")

	// Fetch the queued network payloads. queue is a string of comma separated
	// JSON objects representing a specific network payload.
	behaviourID, ok := networkPayload.GetContext().GetBehaviourID()
	if !ok {
		return nil, maskAnyf(invalidBehaviourIDError, "must not be empty")
	}
	queueKey := key.NewNetworkKey("activate:queue:behaviour-id:%s:network-payload", behaviourID)
	s, err := a.Storage().General().Get(queueKey)
	if err != nil {
		return nil, maskAny(err)
	}
	queue, err := stringToQueue(s)
	if err != nil {
		return nil, maskAny(err)
	}

	// Merge the given network payload with the queue that we just fetched from
	// storage. We store the extended queue directly after merging it with the
	// given network payload to definitely track the received network payload,
	// even if something goes wrong and we need to return an error on the code
	// below. In case the current queue exeeds a certain amount of payloads, it is
	// unlikely that the queue is going to be helpful when growing any further.
	// Thus we cut the queue at some point beyond the interface capabilities of
	// the requested CLG. Note that it is possible to have multiple network
	// payloads sent by the same CLG. That might happen in case a specific CLG
	// wants to fulfil the interface of the requested CLG on its own, even it is
	// not able to do so with the output of a single calculation.
	queue = append(queue, networkPayload)
	queueBuffer := len(getInputTypes(CLG.GetCalculate())) + 1
	if len(queue) > queueBuffer {
		queue = queue[1:]
	}
	err = a.persistQueue(queueKey, queue)
	if err != nil {
		return nil, maskAny(err)
	}

	// This is the list of lookup functions which is executed seuqentially.
	lookups := []func(CLG systemspec.CLG, queue []objectspec.NetworkPayload) (objectspec.NetworkPayload, error){
		a.GetNetworkPayload,
		a.New,
	}

	// Execute one lookup after another. As soon as we find a network payload, we
	// return it.
	var newNetworkPayload objectspec.NetworkPayload
	for _, lookup := range lookups {
		newNetworkPayload, err = lookup(CLG, queue)
		if IsNetworkPayloadNotFound(err) {
			// There could no network payload be found by this lookup. Go on and try
			// the next one.
			continue
		} else if err != nil {
			return nil, maskAny(err)
		}

		// The current lookup was successful. We do not need to execute any further
		// lookup, but can go on with the network payload found.
		break
	}

	// Filter all network payloads from the queue that are merged into the new
	// network payload.
	var newQueue []objectspec.NetworkPayload
	for _, s := range newNetworkPayload.GetSources() {
		for _, np := range queue {
			// At this point there is only one source given. That is the CLG that
			// forwarded the current network payload to here. If this is not the case,
			// we return an error.
			sources := np.GetSources()
			if len(sources) != 1 {
				return nil, maskAnyf(invalidSourcesError, "there must be one source")
			}
			if s == sources[0] {
				// The current network payload is part of the merged network payload.
				// Thus we do not add it to the new queue.
				continue
			}
			newQueue = append(newQueue, np)
		}
	}

	// Update the modified queue in the underlying storage.
	err = a.persistQueue(queueKey, newQueue)
	if err != nil {
		return nil, maskAny(err)
	}

	// The current lookup was able to find a network payload. Thus we simply
	// return it.
	return newNetworkPayload, nil
}
Example #4
0
func (a *activator) New(CLG systemspec.CLG, queue []objectspec.NetworkPayload) (objectspec.NetworkPayload, error) {
	// Track the input types of the requested CLG as string slice to have
	// something that is easily comparable and efficient. By convention the first
	// input argument of each CLG is a context. We remove the first argument here,
	// because we want to match output interfaces against input interfaces. By
	// convention output interfaces of CLGs must not have a context as first
	// return value. Therefore we align the input and output values to make them
	// comparable.
	clgTypes := typesToStrings(getInputTypes(CLG.GetCalculate()))[1:]

	// Prepare the permutation list to find out which combination of payloads
	// satisfies the requested CLG's interface.
	newPermutationListConfig := permutation.DefaultListConfig()
	newPermutationListConfig.MaxGrowth = len(clgTypes)
	newPermutationListConfig.RawValues = queueToValues(queue)
	newPermutationList, err := permutation.NewList(newPermutationListConfig)
	if err != nil {
		return nil, maskAny(err)
	}

	// Permute the permutation list of the queued network payloads until we found
	// all the matching combinations.
	var possibleMatches [][]objectspec.NetworkPayload
	for {
		// Check if the current combination of network payloads already satisfies
		// the interface of the requested CLG. This is done in the first place to
		// also handle the very first combination of the permutation list.  In case
		// there does a combination of network payloads match the interface of the
		// requested CLG, we capture the found combination and try to find more
		// combinations in the upcoming loops.
		permutedValues := newPermutationList.GetPermutedValues()
		valueTypes := typesToStrings(valuesToTypes(permutedValues))
		if equalStrings(clgTypes, valueTypes) {
			possibleMatches = append(possibleMatches, valuesToQueue(permutedValues))
		}

		// Permute the list of the queued network payloads by one further
		// permutation step within the current iteration. As soon as the permutation
		// list cannot be permuted anymore, we stop the permutation loop to choose
		// one random combination of the tracked list in the next step below.
		err = a.Service().Permutation().PermuteBy(newPermutationList, 1)
		if permutation.IsMaxGrowthReached(err) {
			break
		} else if err != nil {
			return nil, maskAny(err)
		}
	}

	// We fetched all possible combinations if network payloads that match the
	// interface of the requested CLG. Now we need to select one random
	// combination to cover all possible combinations across all possible CLG
	// trees being created over time. This prevents us from choosing always only
	// the first matching combination, which would lack discoveries of all
	// potential combinations being created.
	matchIndex, err := a.Service().Random().CreateMax(len(possibleMatches))
	if err != nil {
		return nil, maskAny(err)
	}
	matches := possibleMatches[matchIndex]

	// The queued network payloads are able to satisfy the interface of the
	// requested CLG. We merge the matching network payloads together and return
	// the result after storing the created configuration of the requested CLG.
	newNetworkPayload, err := mergeNetworkPayloads(matches)
	if err != nil {
		return nil, maskAny(err)
	}

	// Persists the combination of permuted network payloads as configuration for
	// the requested CLG. This configuration is stored using references of the
	// behaviour IDs associated with CLGs that forwarded signals to this requested
	// CLG. Note that the order of behaviour IDs must be preserved, because it
	// represents the input interface of the requested CLG.
	behaviourID, ok := newNetworkPayload.GetContext().GetBehaviourID()
	if !ok {
		return nil, maskAnyf(invalidBehaviourIDError, "must not be empty")
	}
	behaviourIDsKey := key.NewNetworkKey("activate:configuration:behaviour-id:%s:behaviour-ids", behaviourID)
	var behaviourIDs []string
	for _, behaviourID := range newNetworkPayload.GetSources() {
		behaviourIDs = append(behaviourIDs, string(behaviourID))
	}
	err = a.Storage().General().Set(behaviourIDsKey, strings.Join(behaviourIDs, ","))
	if err != nil {
		return nil, maskAny(err)
	}

	return newNetworkPayload, nil
}
Example #5
0
func (n *network) InputHandler(CLG systemspec.CLG, textInput objectspec.TextInput) error {
	// In case the text request defines the echo flag, we overwrite the given CLG
	// directly to the output CLG. This will cause the created network payload to
	// be forwarded to the output CLG without indirection. Note that this should
	// only be used for testing purposes to bypass more complex neural network
	// activities to directly respond with the received input.
	if textInput.GetEcho() {
		var ok bool
		CLG, ok = n.CLGs["output"]
		if !ok {
			return maskAnyf(clgNotFoundError, "name: %s", "output")
		}
	}

	// Create new IDs for the new CLG tree and the input CLG.
	clgTreeID, err := n.Service().ID().New()
	if err != nil {
		return maskAny(err)
	}
	behaviourID, err := n.Service().ID().New()
	if err != nil {
		return maskAny(err)
	}

	// Create a new context and adapt it using the information of the current scope.
	ctx := context.MustNew()
	ctx.SetBehaviourID(string(behaviourID))
	ctx.SetCLGName(CLG.GetName())
	ctx.SetCLGTreeID(string(clgTreeID))
	ctx.SetExpectation(textInput.GetExpectation())
	ctx.SetSessionID(textInput.GetSessionID())

	// We transform the received text request to a network payload to have a
	// conventional data structure within the neural network.
	newNetworkPayloadConfig := networkpayload.DefaultConfig()
	newNetworkPayloadConfig.Args = []reflect.Value{reflect.ValueOf(textInput.GetInput())}
	newNetworkPayloadConfig.Context = ctx
	newNetworkPayloadConfig.Destination = behaviourID
	newNetworkPayloadConfig.Sources = []string{n.GetID()}
	newNetworkPayload, err := networkpayload.New(newNetworkPayloadConfig)
	if err != nil {
		return maskAny(err)
	}

	// Write the new CLG tree ID to reference the input CLG ID and add the CLG
	// tree ID to the new context.
	firstBehaviourIDKey := key.NewNetworkKey("clg-tree-id:%s:first-behaviour-id", clgTreeID)
	err = n.Storage().General().Set(firstBehaviourIDKey, string(behaviourID))
	if err != nil {
		return maskAny(err)
	}

	// Write the transformed network payload to the queue.
	eventKey := key.NewNetworkKey("event:network-payload")
	b, err := json.Marshal(newNetworkPayload)
	if err != nil {
		return maskAny(err)
	}
	err = n.Storage().General().PushToList(eventKey, string(b))
	if err != nil {
		return maskAny(err)
	}

	return nil
}