UNPKG

@tensorflow/tfjs-converter

Version:

Tensorflow model converter for javascript

629 lines 97.2 kB
/** * @license * Copyright 2018 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ import { env, keep, tidy, util } from '@tensorflow/tfjs-core'; import { getNodeNameAndIndex, getParamValue, getTensor, getTensorsForCurrentContext, parseNodeName } from '../operations/executors/utils'; import { executeOp } from '../operations/operation_executor'; import { ExecutionContext } from './execution_context'; import { getExecutionSubgraph, getNodeLiveUntilMap, getNodesInTopologicalOrder, isControlFlow } from './model_analysis'; export class GraphExecutor { get weightIds() { return this.parent ? this.parent.weightIds : this._weightIds; } get functionExecutorMap() { return this.parent ? this.parent.functionExecutorMap : this._functionExecutorMap; } get weightMap() { return this.parent ? this.parent.weightMap : this._weightMap; } set weightMap(weightMap) { const weightIds = Object.keys(weightMap).map(key => weightMap[key].map(tensor => tensor.id)); this._weightIds = [].concat(...weightIds); this._weightMap = weightMap; } /** * Set `ResourceManager` shared by executors of a model. * @param resourceManager: `ResourceManager` of the `GraphModel`. */ set resourceManager(resourceManager) { this._resourceManager = resourceManager; } get inputs() { return this._inputs.map(node => { return { name: node.name, shape: node.attrParams['shape'] ? node.attrParams['shape'].value : undefined, dtype: node.attrParams['dtype'] ? node.attrParams['dtype'].value : undefined }; }); } get outputs() { return this._outputs.map(node => { return { name: node.name, shape: node.attrParams['shape'] ? node.attrParams['shape'].value : undefined, dtype: node.attrParams['dtype'] ? node.attrParams['dtype'].value : undefined }; }); } get inputNodes() { return this._inputs.map(node => node.signatureKey || node.name); } get outputNodes() { return this._outputs.map((node) => { const name = node.signatureKey || node.name; return node.defaultOutput ? (`${name}:${node.defaultOutput}`) : name; }); } get functions() { return Object.keys(this._functions).reduce((map, key) => { map[key] = this._functions[key].signature; return map; }, {}); } /** * * @param graph Graph the model or function graph to be executed. * @param parent When building function exector you need to set the parent * executor. Since the weights and function executor maps are set at parant * level, that function executor can access the function maps and weight maps * through the parent. */ constructor(graph, parent) { this.graph = graph; this.parent = parent; this.compiledMap = new Map(); this.parseNodeNameCache = new Map(); this._weightMap = {}; this.SEPARATOR = ','; this._functions = {}; this._functionExecutorMap = {}; this.keepIntermediateTensors = false; this._outputs = graph.outputs; this._inputs = graph.inputs; this._initNodes = graph.initNodes; this._signature = graph.signature; this._functions = graph.functions; // create sub-graph executors if (graph.functions != null) { Object.keys(graph.functions).forEach(name => { this._functionExecutorMap[name] = new GraphExecutor(graph.functions[name], this); }); } } getCompilationKey(inputs, outputs) { const sortedInputs = inputs.map(node => node.name).sort(); const sortedOutputs = outputs.map(node => node.name).sort(); return sortedInputs.join(this.SEPARATOR) + '--' + sortedOutputs.join(this.SEPARATOR); } /** * Compiles the inference graph and returns the minimal set of nodes that are * required for execution, in the correct execution order. * @returns {Object} compilation The compile result. * @returns {Node[]} compilation.orderedNodes Nodes in the correct execution * order. * @returns {Map<string, Node[]>} compilation.nodeLiveUntilMap A map from node * to disposable nodes after its execution. That is, for a node `x`, * `nodeLiveUntilMap[x]` indicates all nodes whose intermediate * tensors should be disposed after `x` is executed. */ compile(inputs, outputs) { const executionInfo = getExecutionSubgraph(inputs, outputs, this.weightMap, this._initNodes); const { missingInputs, dynamicNode, syncInputs } = executionInfo; if (dynamicNode != null) { throw new Error(`This execution contains the node '${dynamicNode.name}', which has ` + `the dynamic op '${dynamicNode.op}'. Please use ` + `model.executeAsync() instead. Alternatively, to avoid the ` + `dynamic ops, specify the inputs [${syncInputs}]`); } if (missingInputs.length > 0) { const outNames = outputs.map(n => n.name); const inNames = Object.keys(inputs); throw new Error(`Cannot compute the outputs [${outNames}] from the provided inputs ` + `[${inNames}]. Missing the following inputs: [${missingInputs}]`); } const orderedNodes = getNodesInTopologicalOrder(this.graph, executionInfo); const nodeLiveUntilMap = getNodeLiveUntilMap(orderedNodes); return { orderedNodes, nodeLiveUntilMap }; } cloneAndKeepTensor(tensor) { if (tensor == null) { return null; } const clone = tensor.clone(); // Keep the clone because`model.execute()` may be called within // a `tidy()`, but the user may inspect these tensors after the // tidy. keep(clone); return clone; } cloneTensorList(tensors) { if (!tensors) { return null; } const clonedTensor = tensors.map(tensor => { return this.cloneAndKeepTensor(tensor); }); return clonedTensor; } cloneTensorMap(tensorsMap) { return Object.fromEntries(Object.entries(tensorsMap).map(([name, tensorsList]) => { return [name, this.cloneTensorList(tensorsList)]; })); } /** * Executes the inference for given input tensors. * @param inputs Tensor map for the model inputs, keyed by the input node * names. * @param outputs Optional. output node name from the Tensorflow model, if * no outputs are specified, the default outputs of the model would be used. * You can inspect intermediate nodes of the model by adding them to the * outputs array. */ execute(inputs, outputs) { // Dispose any tensors from a prior run to avoid leaking them. this.disposeIntermediateTensors(); inputs = this.mapInputs(inputs); const names = Object.keys(inputs).sort(); this.checkInputs(inputs); this.checkInputShapeAndType(inputs); outputs = this.mapOutputs(outputs); this.checkOutputs(outputs); const inputNodes = names.map(name => this.graph.nodes[parseNodeName(name)[0]]); const outputNodeNames = outputs.map(name => parseNodeName(name)[0]); const outputNodeNameSet = new Set(outputNodeNames); let outputNodes = outputNodeNames.map(name => this.graph.nodes[name]); // If no outputs are specified, then use the default outputs of the model. if (outputNodes.length === 0) { outputNodes = this._outputs; } const compilationKey = this.getCompilationKey(inputNodes, outputNodes); // Do nothing if the compiled graph cache contains the input. let compilation = this.compiledMap.get(compilationKey); if (compilation == null) { compilation = this.compile(inputs, outputNodes); this.compiledMap.set(compilationKey, compilation); } // Keep tensors if KEEP_INTERMEDIATE_TENSORS is on. try { this.keepIntermediateTensors = env().getBool('KEEP_INTERMEDIATE_TENSORS'); } catch (e) { this.keepIntermediateTensors = false; console.warn(e.message); } const tensorArrayMap = {}; const tensorListMap = {}; return tidy(() => { const context = new ExecutionContext(this.weightMap, tensorArrayMap, tensorListMap, this.functionExecutorMap, this.parseNodeNameCache); const tensorsMap = Object.assign({}, this.weightMap); if (this.keepIntermediateTensors) { this.clonedTensorsMap = this.cloneTensorMap(this.weightMap); } Object.keys(inputs).forEach(name => { const [nodeName, index] = parseNodeName(name, context); const tensors = []; tensors[index] = inputs[name]; tensorsMap[nodeName] = tensors; if (this.keepIntermediateTensors) { this.clonedTensorsMap[nodeName] = this.cloneTensorList(tensors); } }); const tensorsToKeep = this.getFrozenTensorIds(tensorsMap); const { orderedNodes, nodeLiveUntilMap } = compilation; for (const node of orderedNodes) { if (tensorsMap[node.name]) { continue; } const tensors = executeOp(node, tensorsMap, context, this._resourceManager); if (util.isPromise(tensors)) { throw new Error(`The execution of the op '${node.op}' returned a promise. ` + `Please use model.executeAsync() instead.`); } tensorsMap[node.name] = tensors; if (this.keepIntermediateTensors) { this.clonedTensorsMap[node.name] = this.cloneTensorList(tensors); } this.checkTensorForDisposalWithNodeLiveUntilInfo(node, tensorsMap, context, tensorsToKeep, outputNodeNameSet, nodeLiveUntilMap.get(node.name)); } // dispose the context for the root executor if (this.parent == null) { context.dispose(tensorsToKeep); } return outputs.map(name => getTensor(name, tensorsMap, context)); }); } getFrozenTensorIds(tensorMap) { const ids = [].concat.apply([], Object.keys(tensorMap) .map(key => tensorMap[key]) .map(tensors => tensors.map(tensor => tensor.id))); return new Set(ids); } checkTensorForDisposal(nodeName, node, tensorMap, context, tensorsToKeep, outputNodeNameSet, intermediateTensorConsumerCount) { // Skip output nodes and any control flow nodes, since its dependency is // tricky to track correctly. if (isControlFlow(node) || outputNodeNameSet.has(nodeName)) { return; } for (const tensor of tensorMap[nodeName]) { if (tensor == null) { continue; } intermediateTensorConsumerCount[tensor.id] = (intermediateTensorConsumerCount[tensor.id] || 0) + node.children.length; } for (const input of node.inputs) { // Skip any control flow nodes, since its dependency is tricky to track // correctly. if (isControlFlow(input)) { continue; } const tensors = getTensorsForCurrentContext(input.name, tensorMap, context); if (tensors == null) { continue; } for (const tensor of tensors) { if (!tensor || tensor.kept || tensorsToKeep.has(tensor.id)) { continue; } // Only intermediate nodes' tensors have counts set, not marked as // kept, and not in `tensorsToKeep`. // Input and weight nodes' tensors should exist in `tensorsToKeep`. // Output and control flow nodes' tensors should never have count set. const count = intermediateTensorConsumerCount[tensor.id]; if (count === 1) { tensor.dispose(); delete intermediateTensorConsumerCount[tensor.id]; } else if (count != null) { intermediateTensorConsumerCount[tensor.id]--; } } } } checkTensorForDisposalWithNodeLiveUntilInfo(node, tensorMap, context, tensorsToKeep, outputNodeNameSet, liveUntilNodes) { function isNonDisposableNode(node) { // Skip output nodes and any control flow nodes, since its dependency is // tricky to track correctly. return isControlFlow(node) || outputNodeNameSet.has(node.name); } if (isControlFlow(node) || liveUntilNodes == null) { return; } for (const nodeToDispose of liveUntilNodes) { if (isNonDisposableNode(nodeToDispose)) { continue; } const tensors = getTensorsForCurrentContext(nodeToDispose.name, tensorMap, context); for (const tensor of tensors) { if (!tensor || tensor.kept || tensorsToKeep.has(tensor.id)) { continue; } tensor.dispose(); } } } /** * Executes the inference for given input tensors in Async fashion. * @param inputs Tensor map for the model inputs, keyed by the input node * names. * @param outputs output node name from the Tensorflow model, if no outputs * are specified, the default outputs of the model would be used. You can * inspect intermediate nodes of the model by adding them to the outputs * array. */ async executeAsync(inputs, outputs) { return this._executeAsync(inputs, outputs); } disposeIntermediateTensors() { if (!this.clonedTensorsMap) { return; } Object.values(this.clonedTensorsMap).forEach(tensorsList => { for (const tensor of tensorsList) { if (tensor && !tensor.isDisposed) { tensor.dispose(); } } }); this.clonedTensorsMap = null; } getIntermediateTensors() { return this.clonedTensorsMap; } /** * Executes the inference for given input tensors in Async fashion. * @param inputs Tensor map for the model inputs, keyed by the input node * names. * @param outputs Optional. output node name from the Tensorflow model, * if no outputs are specified, the default outputs of the model would be * used. You can inspect intermediate nodes of the model by adding them to * the outputs array. * @param isFunctionExecution Optional. Flag for executing a function. * @param tensorArrayMap Optional, global TensorArray map by id. Used for * function execution. * @param tensorArrayMap Optional global TensorList map by id. Used for * function execution. */ async _executeAsync(inputs, outputs, isFunctionExecution = false, tensorArrayMap = {}, tensorListMap = {}) { // Dispose any tensors from a prior run to avoid leaking them. this.disposeIntermediateTensors(); if (!isFunctionExecution) { inputs = this.mapInputs(inputs); this.checkInputs(inputs); this.checkInputShapeAndType(inputs); outputs = this.mapOutputs(outputs); this.checkOutputs(outputs); } // Keep tensors if KEEP_INTERMEDIATE_TENSORS is on. try { this.keepIntermediateTensors = env().getBool('KEEP_INTERMEDIATE_TENSORS'); } catch (e) { this.keepIntermediateTensors = false; console.warn(e.message); } const context = new ExecutionContext(this.weightMap, tensorArrayMap, tensorListMap, this.functionExecutorMap, this.parseNodeNameCache); if (this.keepIntermediateTensors) { this.clonedTensorsMap = this.cloneTensorMap(this.weightMap); } // Graph with control flow op requires runtime evaluation of the execution // order, while without control flow the execution order is pre-determined // in the compile method. const tensorsMap = await this.executeWithControlFlow(inputs, context, outputs, isFunctionExecution); const results = outputs.map(name => getTensor(name, tensorsMap, context)); // dispose all the intermediate tensors const outputIds = results.map(t => t.id); const inputIds = Object.keys(inputs).map(name => inputs[name].id); const keepIds = new Set([...outputIds, ...inputIds, ...this.weightIds]); Object.values(tensorsMap).forEach(tensorsList => { tensorsList.forEach(tensor => { if (tensor && !tensor.isDisposed && !keepIds.has(tensor.id)) { tensor.dispose(); } }); }); // dispose the context for the root executor if (this.parent == null) { context.dispose(keepIds); } return results; } async executeFunctionAsync(inputs, tensorArrayMap, tensorListMap) { const mappedInputs = inputs.reduce((map, tensor, index) => { map[this.inputs[index].name] = tensor; return map; }, {}); return this._executeAsync(mappedInputs, this.outputNodes, true, tensorArrayMap, tensorListMap); } /** * When there are control flow nodes in the graph, the graph execution use * ExecutionContext to keep track of the frames and loop iterators. * @param inputs placeholder tensors for the graph. * @param context the execution context object for current execution. * @param outputNames Optional. output node name from the Tensorflow model, * if no outputs are specified, the default outputs of the model would be * used. You can inspect intermediate nodes of the model by adding them to * the outputs array. * @param isFunctionExecution Flag for executing a function. */ async executeWithControlFlow(inputs, context, outputNames, isFunctionExecution) { const names = Object.keys(inputs); const inputNodes = names.map(name => this.graph.nodes[parseNodeName(name)[0]]); const outputNodeNames = outputNames.map(name => parseNodeName(name)[0]); const outputNodeNameSet = new Set(outputNodeNames); let outputNodes = outputNodeNames.map(name => this.graph.nodes[name]); // If no outputs are specified, then use the default outputs of the model. if (outputNodes.length === 0) { outputNodes = this._outputs; } const { usedNodes, missingInputs, dynamicNode, syncInputs } = getExecutionSubgraph(inputs, outputNodes, this.weightMap, this._initNodes); // First nodes to execute include inputNodes, weights, and initNodes. const stack = [ ...inputNodes, ...this.graph.weights, ...(this._initNodes || []) ].map(node => { return { node, contexts: context.currentContext }; }); const tensorsMap = Object.assign({}, this.weightMap); Object.keys(inputs).forEach(name => { const [nodeName, index] = parseNodeName(name); const tensors = []; tensors[index] = inputs[name]; tensorsMap[nodeName] = tensors; }); const intermediateTensorConsumerCount = {}; const tensorsToKeep = this.getFrozenTensorIds(tensorsMap); const added = {}; while (stack.length > 0) { const promises = this.processStack(inputNodes, stack, context, tensorsMap, added, tensorsToKeep, outputNodeNameSet, intermediateTensorConsumerCount, usedNodes); await Promise.all(promises); } if (dynamicNode == null && !isFunctionExecution) { console.warn(`This model execution did not contain any nodes with control flow ` + `or dynamic output shapes. You can use model.execute() instead.`); } const missingOutputs = outputNodes .filter(node => !isControlFlow(node) && !getTensor(node.name, tensorsMap, context)) .map(node => node.name); if (missingOutputs.length > 0) { let alternativeMsg = ''; if (dynamicNode != null) { alternativeMsg = `Alternatively, to avoid the dynamic ops, use model.execute() ` + `and specify the inputs [${syncInputs}]`; } throw new Error(`Cannot compute the outputs [${missingOutputs}] from the provided ` + `inputs [${names}]. Consider providing the following inputs: ` + `[${missingInputs}]. ${alternativeMsg}`); } return tensorsMap; } processStack(inputNodes, stack, context, tensorMap, added, tensorsToKeep, outputNodeNameSet, intermediateTensorConsumerCount, usedNodes) { const promises = []; while (stack.length > 0) { const item = stack.pop(); context.currentContext = item.contexts; let nodeName = ''; // The tensor of the Enter op with isConstant set should be set // in the parent scope, so it will be available as constant for the // whole loop. if (item.node.op === 'Enter' && getParamValue('isConstant', item.node, tensorMap, context)) { [nodeName] = getNodeNameAndIndex(item.node.name, context); } // only process nodes that are not in the tensorMap yet, this include // inputNodes and internal initNodes. if (tensorMap[item.node.name] == null) { const tensors = executeOp(item.node, tensorMap, context, this._resourceManager); if (!nodeName) { [nodeName] = getNodeNameAndIndex(item.node.name, context); } const currentContext = context.currentContext; if (util.isPromise(tensors)) { promises.push(tensors.then(t => { tensorMap[nodeName] = t; if (this.keepIntermediateTensors) { this.clonedTensorsMap[nodeName] = this.cloneTensorList(t); } context.currentContext = currentContext; this.checkTensorForDisposal(nodeName, item.node, tensorMap, context, tensorsToKeep, outputNodeNameSet, intermediateTensorConsumerCount); this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes); return t; })); } else { tensorMap[nodeName] = tensors; if (this.keepIntermediateTensors) { this.clonedTensorsMap[nodeName] = this.cloneTensorList(tensors); } this.checkTensorForDisposal(nodeName, item.node, tensorMap, context, tensorsToKeep, outputNodeNameSet, intermediateTensorConsumerCount); this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes); } } else { this.processChildNodes(item.node, stack, context, tensorMap, added, usedNodes); } } return promises; } processChildNodes(node, stack, context, tensorMap, added, usedNodes) { node.children.forEach((childNode) => { const [nodeName,] = getNodeNameAndIndex(childNode.name, context); if (added[nodeName] || !usedNodes.has(childNode.name)) { return; } // Merge op can be pushed if any of its inputs has value. if (childNode.op === 'Merge') { if (childNode.inputNames.some(name => { return !!getTensor(name, tensorMap, context); })) { added[nodeName] = true; stack.push({ contexts: context.currentContext, node: childNode }); } } else // Otherwise all inputs must to have value. if (childNode.inputNames.every(name => { return !!getTensor(name, tensorMap, context); })) { added[nodeName] = true; stack.push({ contexts: context.currentContext, node: childNode }); } }); } /** * Releases the memory used by the weight tensors. */ dispose() { Object.keys(this.weightMap) .forEach(key => this.weightMap[key].forEach(tensor => tensor.dispose())); } checkInputShapeAndType(inputs) { Object.keys(inputs).forEach(name => { const input = inputs[name]; const [nodeName,] = parseNodeName(name); const node = this.graph.nodes[nodeName]; if (node.attrParams['shape'] && node.attrParams['shape'].value) { const shape = node.attrParams['shape'].value; const match = shape.length === input.shape.length && input.shape.every((dim, index) => shape[index] === -1 || shape[index] === dim); util.assert(match, () => `The shape of dict['${node.name}'] provided in ` + `model.execute(dict) must be [${shape}], but was ` + `[${input.shape}]`); } if (node.attrParams['dtype'] && node.attrParams['dtype'].value) { util.assert(input.dtype === node.attrParams['dtype'].value, () => `The dtype of dict['${node.name}'] provided in ` + `model.execute(dict) must be ` + `${node.attrParams['dtype'].value}, but was ${input.dtype}`); } }); } mapInputs(inputs) { var _a, _b; const result = {}; for (const inputName in inputs) { const tensor = (_b = (_a = this._signature) === null || _a === void 0 ? void 0 : _a.inputs) === null || _b === void 0 ? void 0 : _b[inputName]; if (tensor != null) { result[tensor.name] = inputs[inputName]; } else { result[inputName] = inputs[inputName]; } } return result; } checkInputs(inputs) { const notInGraph = Object.keys(inputs).filter(name => { const [nodeName] = parseNodeName(name); return this.graph.nodes[nodeName] == null; }); if (notInGraph.length > 0) { throw new Error(`The dict provided in model.execute(dict) has ` + `keys: [${notInGraph}] that are not part of graph`); } } mapOutputs(outputs) { return outputs.map(name => { var _a, _b; const tensor = (_b = (_a = this._signature) === null || _a === void 0 ? void 0 : _a.outputs) === null || _b === void 0 ? void 0 : _b[name]; if (tensor != null) { return tensor.name; } return name; }, {}); } checkOutputs(outputs) { outputs.forEach(name => { const [normalizedName] = parseNodeName(name); if (!this.graph.nodes[normalizedName]) { throw new Error(`The output '${name}' is not found in the graph`); } }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhcGhfZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvbnZlcnRlci9zcmMvZXhlY3V0b3IvZ3JhcGhfZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFXLEdBQUcsRUFBRSxJQUFJLEVBQTBCLElBQUksRUFBRSxJQUFJLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUk5RixPQUFPLEVBQUMsbUJBQW1CLEVBQUUsYUFBYSxFQUFFLFNBQVMsRUFBRSwyQkFBMkIsRUFBRSxhQUFhLEVBQUMsTUFBTSwrQkFBK0IsQ0FBQztBQUN4SSxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sa0NBQWtDLENBQUM7QUFHM0QsT0FBTyxFQUFDLGdCQUFnQixFQUF1QixNQUFNLHFCQUFxQixDQUFDO0FBQzNFLE9BQU8sRUFBQyxvQkFBb0IsRUFBRSxtQkFBbUIsRUFBRSwwQkFBMEIsRUFBRSxhQUFhLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQVN0SCxNQUFNLE9BQU8sYUFBYTtJQWdCeEIsSUFBSSxTQUFTO1FBQ1gsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUMvRCxDQUFDO0lBRUQsSUFBSSxtQkFBbUI7UUFDckIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUM7WUFDakMsSUFBSSxDQUFDLG9CQUFvQixDQUFDO0lBQ2pELENBQUM7SUFFRCxJQUFJLFNBQVM7UUFDWCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFB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