import { TemplateResult, html } from 'lit' import { customElement, property } from 'lit/decorators.js' import type { DataSet } from '@/types/data_set' import type { InputLayerConf } from '@/types/input_layer_conf' import type { Position } from '@/types/position' import type { Activation } from '@/types/activation' import { CLayer } from '@/components/network/c_layer' import { NetworkUtils } from '@/utils/network_utils' import { AlertUtils } from '@/utils/alert_utils' import * as tf from '@tensorflow/tfjs' import { CNeuron } from './neuron' import { msg } from '@lit/localize' // an input layer is a special type of a neuron layer. We do not allow // activation functions and provide methods to assign features from the // dataSet to this input layer. We do not allow manual editing of the neurons // and other layers can not connect to an input layer. export class InputLayer extends CLayer { static scopedElements = { "c-neuron": CNeuron } @property() // @ts-ignore accessor conf: InputLayerConf // LIFECYCLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - updated(changedProperties: Map) { super.updated(changedProperties) if (changedProperties.has('conf')) { // check the updated data set keys if (!this.conf.featureKeys.length) { // if no key was assigned, notify the network that this layer wants to // be deleted this.delete() AlertUtils.spawn({ message: `${msg('Layer')} ${this.getCyId()} ${msg('was deleted because no data could be assigned to it!')}`, variant: 'warning', icon: 'x-circle', }) } } if ( changedProperties.has('dataSet') && changedProperties.get('dataSet') && (changedProperties.get('dataSet')).name != this.dataSet.name ) { this.conf.featureKeys = this.dataSet.featureDescs.map( (featureDesc) => featureDesc.key ) this.dispatchEvent( new Event('update-layer-confs', { bubbles: true, composed: true, }) ) } } // METHODS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // -> INFO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // overwrite getName function because activation function is always 'None' for // input layer getName(): string { return `${this.conf.layerId} - ${this.conf.LAYER_NAME}` } // get description getDescription(): string { return msg('An input layer is a layer that just takes data provided from outside the network and passes it on to the next layer(s)') } // -> CREATING - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // creates a new input layer with the optionally specified properties static create({ activation = NetworkUtils.actNone, featureKeys = undefined, pos = undefined, }: { activation?: Activation featureKeys?: string[] pos?: Position } = {}): InputLayerConf { // create a new dense layer configuration with the specified properties const inputLayerConf: InputLayerConf = { HTML_TAG: 'input-layer', LAYER_TYPE: 'Input', LAYER_NAME: 'Input layer', activation: activation, pos: pos, firstSpawn: true, // layer id and data set keys will be added by the layer layerId: undefined, featureKeys: featureKeys, } // emit an layer-conf-created event - the network listens to them, so it can // add a unique layer id to the layer conf and add it to the network array dispatchEvent( new CustomEvent('layer-conf-created', { detail: inputLayerConf, bubbles: true, composed: true, }) ) return inputLayerConf } // duplicate this layer duplicate(): void { const newPos = { ...this.conf.pos } newPos.y -= this.canvas.getHeight(this.getCyId()) + this.canvas.LAYER_DISTANCE InputLayer.create({ activation: this.conf.activation, featureKeys: this.conf.featureKeys, pos: newPos, }) } // -> DATASET - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // assigning inputs getAssignedInputs(): string[] { return Array.from(this._neurons).map((neuron) => neuron.key) } // -> BUILD - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - build(inputs: tf.SymbolicTensor[]): tf.SymbolicTensor { <[]>inputs // create our input tensor const tensor = tf.input({ shape: [this.conf.featureKeys.length], name: this.getTensorName(), }) tensor['layer_id'] = this.conf.layerId this.tensor = tensor return tensor } // RENDER - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - render(): TemplateResult<1> { return html` ${super.render()} ${this.conf.pos ? html`${this.conf.featureKeys.map( (dataSetKey, i) => html` ` )}` : html``} ` } }