def generateSwarmParams(stats): """ Generate parameters for creating a model :param stats: dict with "min", "max" and optional "minResolution"; values must be integer, float or None. :returns: if either minVal or maxVal is None, returns None; otherwise returns swarmParams object that is suitable for passing to startMonitoring and startModel """ minVal = stats.get("min") maxVal = stats.get("max") minResolution = stats.get("minResolution") if minVal is None or maxVal is None: return None # Create possible swarm parameters based on metric data possibleModels = getScalarMetricWithTimeOfDayParams( metricData=[0], minVal=minVal, maxVal=maxVal, minResolution=minResolution) swarmParams = possibleModels[0] swarmParams["inputRecordSchema"] = ( fieldmeta.FieldMetaInfo("c0", fieldmeta.FieldMetaType.datetime, fieldmeta.FieldMetaSpecial.timestamp), fieldmeta.FieldMetaInfo("c1", fieldmeta.FieldMetaType.float, fieldmeta.FieldMetaSpecial.none), ) return swarmParams
def generateSwarmParams(stats): """ Generate parameters for creating a model :param stats: dict with "min", "max" and optional "minResolution"; values must be integer, float or None. :returns: if either minVal or maxVal is None, returns None; otherwise returns swarmParams object that is suitable for passing to startMonitoring and startModel """ minVal = stats.get("min") maxVal = stats.get("max") minResolution = stats.get("minResolution") if minVal is None or maxVal is None: return None # Create possible swarm parameters based on metric data possibleModels = getScalarMetricWithTimeOfDayParams( metricData=[0], minVal=minVal, maxVal=maxVal, minResolution=minResolution) swarmParams = possibleModels[0] swarmParams["inputRecordSchema"] = ( fieldmeta.FieldMetaInfo("c0", fieldmeta.FieldMetaType.datetime, fieldmeta.FieldMetaSpecial.timestamp), fieldmeta.FieldMetaInfo("c1", fieldmeta.FieldMetaType.float, fieldmeta.FieldMetaSpecial.none), ) return swarmParams
def _createModel(cls, stats): """Instantiate and configure an OPF model :param dict stats: Metric data stats per stats_schema.json in the unicorn_backend package. :returns: OPF Model instance """ # TODO remove the "DummyModel" code path once the ILLEGAL INSTRUCTION issue # in nupic is resolved; # Create a dummy model instead of a real one temporarily, while we're # having trouble with the latest nupic builds on the Mac OS Yosemite that # result in ILLEGAL INSTRUCTION in nupic.bindings. This is good enough for # now to enable FrontEnd development. if False: class DummyModel(object): class Result(object): def __init__(self, inferences): self.inferences = inferences def run(self, inputRecord): inputRecord = inputRecord return self.Result(dict(anomalyScore=0.9999)) return DummyModel() else: # THIS IS THE CORRECT PRODUCTION CODE that is failing with ILLEGAL # INSTRUCTION in ModelFactory.create on my Mac OS Yosemite laptop. # Generate swarm params possibleModels = getScalarMetricWithTimeOfDayParams( metricData=[0], minVal=stats["min"], maxVal=stats["max"], minResolution=stats.get("minResolution")) swarmParams = possibleModels[0] model = ModelFactory.create(modelConfig=swarmParams["modelConfig"]) model.enableLearning() model.enableInference(swarmParams["inferenceArgs"]) return model
def _createModel(cls, stats): """Instantiate and configure an OPF model :param dict stats: Metric data stats per stats_schema.json in the unicorn_backend package. :returns: OPF Model instance """ # Generate swarm params possibleModels = getScalarMetricWithTimeOfDayParams( metricData=[0], minVal=stats["min"], maxVal=stats["max"], minResolution=stats.get("minResolution")) swarmParams = possibleModels[0] model = ModelFactory.create(modelConfig=swarmParams["modelConfig"]) model.enableLearning() model.enableInference(swarmParams["inferenceArgs"])
def _createModel(cls, stats): """Instantiate and configure an OPF model :param dict stats: Metric data stats per stats_schema.json in the unicorn_backend package. :returns: OPF Model instance """ # Generate swarm params possibleModels = getScalarMetricWithTimeOfDayParams( metricData=[0], minVal=stats["min"], maxVal=stats["max"], minResolution=stats.get("minResolution")) swarmParams = possibleModels[0] model = ModelFactory.create(modelConfig=swarmParams["modelConfig"]) model.enableLearning() model.enableInference(swarmParams["inferenceArgs"]) return model
def testModelParams(self): """ Test that clusterParams loads returns a valid dict that can be instantiated as a CLAModel. """ params = getScalarMetricWithTimeOfDayParams([0], minVal=23.42, maxVal=23.420001)[0] encodersDict= ( params['modelConfig']['modelParams']['sensorParams']['encoders'] ) model = ModelFactory.create(modelConfig=params['modelConfig']) self.assertIsInstance(model, CLAModel, "JSON returned cannot be used to create a model") # Ensure we have a time of day field self.assertIsNotNone(encodersDict['c0_timeOfDay']) # Ensure resolution doesn't get too low if (encodersDict['c1']['type'] == 'RandomDistributedScalarEncoder'): self.assertGreaterEqual(encodersDict['c1']['resolution'], 0.001, "Resolution is too low")
def testModelSwapper(self): """Simple end-to-end test of the model swapper system.""" modelSchedulerSubprocess = self._startModelSchedulerSubprocess() self.addCleanup(lambda: modelSchedulerSubprocess.kill() if modelSchedulerSubprocess.returncode is None else None) modelID = "foobar" resultBatches = [] with ModelSwapperInterface() as swapperAPI: possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0], minVal=0, maxVal=1000) # Submit requests including a model creation command and two data rows. args = possibleModels[0] args["inputRecordSchema"] = ( FieldMetaInfo("c0", FieldMetaType.datetime, FieldMetaSpecial.timestamp), FieldMetaInfo("c1", FieldMetaType.float, FieldMetaSpecial.none), ) # Define the model _LOGGER.info("Defining the model") swapperAPI.defineModel(modelID=modelID, args=args, commandID="defineModelCmd1") # Attempt to define the same model again _LOGGER.info("Defining the model again") swapperAPI.defineModel(modelID=modelID, args=args, commandID="defineModelCmd2") # Send input rows to the model inputRows = [ ModelInputRow( rowID="rowfoo", data=[datetime.datetime(2013, 5, 23, 8, 13, 00), 5.3]), ModelInputRow( rowID="rowbar", data=[datetime.datetime(2013, 5, 23, 8, 13, 15), 2.4]), ] _LOGGER.info("Submitting batch of %d input rows...", len(inputRows)) swapperAPI.submitRequests(modelID=modelID, requests=inputRows) _LOGGER.info("These models have pending input: %s", swapperAPI.getModelsWithInputPending()) # Retrieve all results. # NOTE: We collect results via background thread to avoid # deadlocking the test runner in the event consuming blocks unexpectedly _LOGGER.info("Reading all batches of results...") numBatchesExpected = 3 resultBatches.extend( self._consumeResults(numBatchesExpected, timeout=20)) self.assertEqual(len(resultBatches), numBatchesExpected) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # Delete the model _LOGGER.info("Deleting the model") swapperAPI.deleteModel(modelID=modelID, commandID="deleteModelCmd1") _LOGGER.info("Waiting for model deletion result") resultBatches.extend(self._consumeResults(1, timeout=20)) self.assertEqual(len(resultBatches), 4) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # The model input queue should be deleted now self.assertFalse( bus.isMessageQeueuePresent( swapperAPI._getModelInputQName(modelID=modelID))) # Try deleting the model again, to make sure there are no exceptions _LOGGER.info("Attempting to delete the model again") swapperAPI.deleteModel(modelID=modelID, commandID="deleteModelCmd1") # Verify results # First result batch should be the first defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "defineModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "defineModelCmd1") # The second result batch should for the second defineModel result for the # same model batch = resultBatches[1] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "defineModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "defineModelCmd2") # The third batch should be for the two input rows batch = resultBatches[2] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows)) for inputRow, result in zip(inputRows, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) # The fourth batch should be for the "deleteModel" batch = resultBatches[3] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "deleteModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "deleteModelCmd1") # Signal Model Scheduler Service subprocess to shut down and wait for it waitResult = dict() def runWaiterThread(): try: waitResult["returnCode"] = modelSchedulerSubprocess.wait() except: _LOGGER.exception( "Waiting for modelSchedulerSubprocess failed") waitResult["exceptionInfo"] = traceback.format_exc() raise return modelSchedulerSubprocess.terminate() waiterThread = threading.Thread(target=runWaiterThread) waiterThread.setDaemon(True) waiterThread.start() waiterThread.join(timeout=30) self.assertFalse(waiterThread.isAlive()) self.assertEqual(waitResult["returnCode"], 0, msg=repr(waitResult))
def testRunModelWithFullThenIncrementalCheckpoints(self): # Have model_runner create a full checkpoint, then incremental checkpoint modelID = "foobar" checkpointMgr = model_checkpoint_mgr.ModelCheckpointMgr() with ModelSwapperInterface() as swapperAPI: possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0], minVal=0, maxVal=1000) # Submit requests including a model creation command and two data rows. args = possibleModels[0] args["inputRecordSchema"] = ( FieldMetaInfo("c0", FieldMetaType.datetime, FieldMetaSpecial.timestamp), FieldMetaInfo("c1", FieldMetaType.float, FieldMetaSpecial.none), ) # Define the model _LOGGER.info("Defining the model") swapperAPI.defineModel(modelID=modelID, args=args, commandID="defineModelCmd1") # Send input rows to the model inputRows = [ ModelInputRow(rowID="rowfoo", data=[datetime.datetime(2014, 5, 23, 8, 13, 00), 5.3]), ModelInputRow(rowID="rowbar", data=[datetime.datetime(2014, 5, 23, 8, 13, 15), 2.4]), ] _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows), inputRows[0].rowID, inputRows[-1].rowID) swapperAPI.submitRequests(modelID=modelID, requests=inputRows) # Run model_runner and collect results with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=2, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) self.assertEqual(len(resultBatches), 2, repr(resultBatches)) # First result batch should be the first defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "defineModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "defineModelCmd1") # The second result batch should be for the two input rows batch = resultBatches[1] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows)) for inputRow, result in zip(inputRows, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) # Verify model checkpoint model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertEqual( len(attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME]), 2, msg=repr(attrs)) self.assertNotIn( model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) # Now, check incremental checkpointing inputRows2 = [ ModelInputRow(rowID=2, data=[datetime.datetime(2014, 5, 23, 8, 13, 20), 2.7]), ModelInputRow(rowID=3, data=[datetime.datetime(2014, 5, 23, 8, 13, 25), 3.9]), ] _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows2), inputRows2[0].rowID, inputRows2[-1].rowID) inputBatchID = swapperAPI.submitRequests(modelID=modelID, requests=inputRows2) with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows2)) for inputRow, result in zip(inputRows2, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME], [inputBatchID], msg=repr(attrs)) self.assertIn( model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( model_runner._ModelArchiver._decodeDataSamples( attrs[model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]), [row.data for row in inputRows2], msg=repr(attrs)) # Final run with incremental checkpointing inputRows3 = [ ModelInputRow(rowID=4, data=[datetime.datetime(2014, 5, 23, 8, 13, 30), 4.7]), ModelInputRow(rowID=5, data=[datetime.datetime(2014, 5, 23, 8, 13, 35), 5.9]), ] _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows3), inputRows3[0].rowID, inputRows3[-1].rowID) inputBatchID = swapperAPI.submitRequests(modelID=modelID, requests=inputRows3) with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows3)) for inputRow, result in zip(inputRows3, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME], [inputBatchID], msg=repr(attrs)) self.assertIn( model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( model_runner._ModelArchiver._decodeDataSamples( attrs[model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]), [row.data for row in itertools.chain(inputRows2, inputRows3)], msg=repr(attrs)) # Delete the model _LOGGER.info("Deleting the model=%s", modelID) swapperAPI.deleteModel(modelID=modelID, commandID="deleteModelCmd1") with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) self.assertEqual(len(resultBatches), 1, repr(resultBatches)) # First result batch should be the first defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "deleteModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "deleteModelCmd1") with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # The model input queue should be deleted now self.assertFalse( bus.isMessageQeueuePresent( swapperAPI._getModelInputQName(modelID=modelID))) # The model checkpoint should be gone too with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.load(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.loadModelDefinition(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.loadCheckpointAttributes(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.remove(modelID)
def testCloneModel(self): modelSchedulerSubprocess = self._startModelSchedulerSubprocess() self.addCleanup(lambda: modelSchedulerSubprocess.kill() if modelSchedulerSubprocess.returncode is None else None) modelID = "abc" destModelID = "def" resultBatches = [] with ModelSwapperInterface() as swapperAPI: possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0], minVal=0, maxVal=1000) # Submit requests including a model creation command and two data rows. args = possibleModels[0] args["inputRecordSchema"] = ( FieldMetaInfo("c0", FieldMetaType.datetime, FieldMetaSpecial.timestamp), FieldMetaInfo("c1", FieldMetaType.float, FieldMetaSpecial.none), ) # Define the model _LOGGER.info("Defining the model") swapperAPI.defineModel(modelID=modelID, args=args, commandID="defineModelCmd1") resultBatches.extend(self._consumeResults(1, timeout=20)) self.assertEqual(len(resultBatches), 1) # Clone the just-defined model _LOGGER.info("Cloning model") swapperAPI.cloneModel(modelID, destModelID, commandID="cloneModelCmd1") resultBatches.extend(self._consumeResults(1, timeout=20)) self.assertEqual(len(resultBatches), 2) # Send input rows to the clone inputRows = [ ModelInputRow(rowID="rowfoo", data=[datetime.datetime(2013, 5, 23, 8, 13, 00), 5.3]), ModelInputRow(rowID="rowbar", data=[datetime.datetime(2013, 5, 23, 8, 13, 15), 2.4]), ] _LOGGER.info("Submitting batch of %d input rows...", len(inputRows)) swapperAPI.submitRequests(modelID=destModelID, requests=inputRows) _LOGGER.info("These models have pending input: %s", swapperAPI.getModelsWithInputPending()) resultBatches.extend(self._consumeResults(1, timeout=20)) self.assertEqual(len(resultBatches), 3) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # Delete the model _LOGGER.info("Deleting the model") swapperAPI.deleteModel(modelID=destModelID, commandID="deleteModelCmd1") _LOGGER.info("Waiting for model deletion result") resultBatches.extend(self._consumeResults(1, timeout=20)) self.assertEqual(len(resultBatches), 4) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # The model input queue should be deleted now self.assertFalse( bus.isMessageQeueuePresent( swapperAPI._getModelInputQName(modelID=destModelID))) # Verify results # First result batch should be the defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "defineModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "defineModelCmd1") # The second result batch should for the cloneModel result batch = resultBatches[1] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "cloneModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "cloneModelCmd1") # The third batch should be for the two input rows batch = resultBatches[2] self.assertEqual(batch.modelID, destModelID) self.assertEqual(len(batch.objects), len(inputRows)) for inputRow, result in zip(inputRows, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) # The fourth batch should be for the "deleteModel" batch = resultBatches[3] self.assertEqual(batch.modelID, destModelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "deleteModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "deleteModelCmd1") # Signal Model Scheduler Service subprocess to shut down and wait for it waitResult = dict() def runWaiterThread(): try: waitResult["returnCode"] = modelSchedulerSubprocess.wait() except: _LOGGER.exception("Waiting for modelSchedulerSubprocess failed") waitResult["exceptionInfo"] = traceback.format_exc() raise return modelSchedulerSubprocess.terminate() waiterThread = threading.Thread(target=runWaiterThread) waiterThread.setDaemon(True) waiterThread.start() waiterThread.join(timeout=30) self.assertFalse(waiterThread.isAlive()) self.assertEqual(waitResult["returnCode"], 0, msg=repr(waitResult))
def testRunModelWithFullThenIncrementalCheckpoints(self): # Have model_runner create a full checkpoint, then incremental checkpoint modelID = "foobar" checkpointMgr = model_checkpoint_mgr.ModelCheckpointMgr() with ModelSwapperInterface() as swapperAPI: possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0], minVal=0, maxVal=1000) # Submit requests including a model creation command and two data rows. args = possibleModels[0] args["inputRecordSchema"] = ( FieldMetaInfo("c0", FieldMetaType.datetime, FieldMetaSpecial.timestamp), FieldMetaInfo("c1", FieldMetaType.float, FieldMetaSpecial.none), ) # Define the model _LOGGER.info("Defining the model") swapperAPI.defineModel(modelID=modelID, args=args, commandID="defineModelCmd1") # Send input rows to the model inputRows = [ ModelInputRow( rowID="rowfoo", data=[datetime.datetime(2014, 5, 23, 8, 13, 00), 5.3]), ModelInputRow( rowID="rowbar", data=[datetime.datetime(2014, 5, 23, 8, 13, 15), 2.4]), ] _LOGGER.info( "Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows), inputRows[0].rowID, inputRows[-1].rowID) swapperAPI.submitRequests(modelID=modelID, requests=inputRows) # Run model_runner and collect results with self._startModelRunnerSubprocess( modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=2, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: # The results message queue should be empty now self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) self.assertEqual(len(resultBatches), 2, repr(resultBatches)) # First result batch should be the first defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "defineModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "defineModelCmd1") # The second result batch should be for the two input rows batch = resultBatches[1] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows)) for inputRow, result in zip(inputRows, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) # Verify model checkpoint model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn( model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertEqual(len(attrs[ model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME]), 2, msg=repr(attrs)) self.assertNotIn(model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) # Now, check incremental checkpointing inputRows2 = [ ModelInputRow( rowID=2, data=[datetime.datetime(2014, 5, 23, 8, 13, 20), 2.7]), ModelInputRow( rowID=3, data=[datetime.datetime(2014, 5, 23, 8, 13, 25), 3.9]), ] _LOGGER.info( "Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows2), inputRows2[0].rowID, inputRows2[-1].rowID) inputBatchID = swapperAPI.submitRequests(modelID=modelID, requests=inputRows2) with self._startModelRunnerSubprocess( modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows2)) for inputRow, result in zip(inputRows2, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn( model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual(attrs[ model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME], [inputBatchID], msg=repr(attrs)) self.assertIn(model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( model_runner._ModelArchiver._decodeDataSamples( attrs[model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]), [row.data for row in inputRows2], msg=repr(attrs)) # Final run with incremental checkpointing inputRows3 = [ ModelInputRow( rowID=4, data=[datetime.datetime(2014, 5, 23, 8, 13, 30), 4.7]), ModelInputRow( rowID=5, data=[datetime.datetime(2014, 5, 23, 8, 13, 35), 5.9]), ] _LOGGER.info( "Submitting batch of %d input rows with ids=[%s..%s]...", len(inputRows3), inputRows3[0].rowID, inputRows3[-1].rowID) inputBatchID = swapperAPI.submitRequests(modelID=modelID, requests=inputRows3) with self._startModelRunnerSubprocess( modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), len(inputRows3)) for inputRow, result in zip(inputRows3, batch.objects): self.assertIsInstance(result, ModelInferenceResult) self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.rowID, inputRow.rowID) self.assertIsInstance(result.anomalyScore, float) model = checkpointMgr.load(modelID) del model attrs = checkpointMgr.loadCheckpointAttributes(modelID) self.assertIn( model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual(attrs[ model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME], [inputBatchID], msg=repr(attrs)) self.assertIn(model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME, attrs, msg=repr(attrs)) self.assertSequenceEqual( model_runner._ModelArchiver._decodeDataSamples( attrs[model_runner._ModelArchiver. _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]), [row.data for row in itertools.chain(inputRows2, inputRows3)], msg=repr(attrs)) # Delete the model _LOGGER.info("Deleting the model=%s", modelID) swapperAPI.deleteModel(modelID=modelID, commandID="deleteModelCmd1") with self._startModelRunnerSubprocess( modelID) as modelRunnerProcess: resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15) self._waitForProcessToStopAndCheck(modelRunnerProcess) self.assertEqual(len(resultBatches), 1, repr(resultBatches)) # First result batch should be the first defineModel result batch = resultBatches[0] self.assertEqual(batch.modelID, modelID) self.assertEqual(len(batch.objects), 1) result = batch.objects[0] self.assertIsInstance(result, ModelCommandResult) self.assertEqual(result.method, "deleteModel") self.assertEqual(result.status, htmengineerrno.SUCCESS) self.assertEqual(result.commandID, "deleteModelCmd1") with MessageBusConnector() as bus: self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName)) # The model input queue should be deleted now self.assertFalse( bus.isMessageQeueuePresent( swapperAPI._getModelInputQName(modelID=modelID))) # The model checkpoint should be gone too with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.load(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.loadModelDefinition(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.loadCheckpointAttributes(modelID) with self.assertRaises(model_checkpoint_mgr.ModelNotFound): checkpointMgr.remove(modelID)