diff --git a/server/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java similarity index 96% rename from server/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java index cf3b76c..62ceceb 100644 --- a/server/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateSerde.java @@ -1,10 +1,10 @@ package com.lightbend.modelserver.store; -import com.lightbend.model.Model; -import com.lightbend.model.ModelFactory; +import com.lightbend.model.java.Model; +import com.lightbend.model.java.ModelFactory; import com.lightbend.model.Modeldescriptor; -import com.lightbend.model.PMML.PMMLModelFactory; -import com.lightbend.model.tensorflow.TensorflowModelFactory; +import com.lightbend.model.java.PMML.PMMLModelFactory; +import com.lightbend.model.java.tensorflow.TensorflowModelFactory; import com.lightbend.queriablestate.ModelServingInfo; import org.apache.kafka.common.serialization.Deserializer; import org.apache.kafka.common.serialization.Serde; diff --git a/server/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java similarity index 99% rename from server/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java index 25954bf..fd85e97 100644 --- a/server/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStore.java @@ -1,6 +1,6 @@ package com.lightbend.modelserver.store; -import com.lightbend.model.Model; +import com.lightbend.model.java.Model; import com.lightbend.queriablestate.ModelServingInfo; import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.streams.processor.ProcessorContext; diff --git a/server/src/main/java/com/lightbend/modelserver/store/ModelStateStoreChangeLogger.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStoreChangeLogger.java similarity index 100% rename from server/src/main/java/com/lightbend/modelserver/store/ModelStateStoreChangeLogger.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStoreChangeLogger.java diff --git a/server/src/main/java/com/lightbend/modelserver/store/ModelStateStoreSupplier.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStoreSupplier.java similarity index 100% rename from server/src/main/java/com/lightbend/modelserver/store/ModelStateStoreSupplier.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ModelStateStoreSupplier.java diff --git a/server/src/main/java/com/lightbend/modelserver/store/ReadableModelStateStore.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ReadableModelStateStore.java similarity index 100% rename from server/src/main/java/com/lightbend/modelserver/store/ReadableModelStateStore.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/ReadableModelStateStore.java diff --git a/server/src/main/java/com/lightbend/modelserver/store/StoreState.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/StoreState.java similarity index 97% rename from server/src/main/java/com/lightbend/modelserver/store/StoreState.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/StoreState.java index 9b0cb32..d8d5625 100644 --- a/server/src/main/java/com/lightbend/modelserver/store/StoreState.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/store/StoreState.java @@ -1,6 +1,6 @@ package com.lightbend.modelserver.store; -import com.lightbend.model.Model; +import com.lightbend.model.java.Model; import com.lightbend.queriablestate.ModelServingInfo; /** diff --git a/server/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java similarity index 97% rename from server/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java index 535f58a..512bf71 100644 --- a/server/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/DataProcessorWithStore.java @@ -1,8 +1,8 @@ package com.lightbend.modelserver.withstore; -import com.lightbend.model.DataConverter; import com.lightbend.model.Winerecord; import com.lightbend.modelserver.store.ModelStateStore; +import com.lightbend.modelserver.support.java.DataConverter; import com.lightbend.queriablestate.ModelServingInfo; import org.apache.kafka.streams.processor.AbstractProcessor; import org.apache.kafka.streams.processor.ProcessorContext; diff --git a/server/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java similarity index 81% rename from server/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java index a8b0257..669e6e5 100644 --- a/server/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/ModelProcessorWithStore.java @@ -1,9 +1,13 @@ package com.lightbend.modelserver.withstore; -import com.lightbend.model.*; -import com.lightbend.model.PMML.PMMLModelFactory; -import com.lightbend.model.tensorflow.TensorflowModelFactory; +import com.lightbend.model.Modeldescriptor; +import com.lightbend.model.java.Model; +import com.lightbend.model.java.ModelFactory; +import com.lightbend.model.java.PMML.PMMLModelFactory; +import com.lightbend.model.java.tensorflow.TensorflowModelFactory; import com.lightbend.modelserver.store.ModelStateStore; +import com.lightbend.modelserver.support.java.DataConverter; +import com.lightbend.modelserver.support.java.ModelToServe; import com.lightbend.queriablestate.ModelServingInfo; import org.apache.kafka.streams.processor.AbstractProcessor; import org.apache.kafka.streams.processor.ProcessorContext; @@ -30,11 +34,11 @@ public class ModelProcessorWithStore extends AbstractProcessor { @Override public void process(byte[] key, byte[] value) { - Optional descriptor = DataConverter.convertModel(value); + Optional descriptor = DataConverter.convertModel(value); if(!descriptor.isPresent()){ return; // Bad record } - CurrentModelDescriptor model = descriptor.get(); + ModelToServe model = descriptor.get(); System.out.println("New scoring model " + model); if(model.getModelData() == null) { System.out.println("Location based model is not yet supported"); diff --git a/server/src/main/java/com/lightbend/modelserver/withstore/ModelServerWithStore.java b/Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/ModelServerWithStore.java similarity index 100% rename from server/src/main/java/com/lightbend/modelserver/withstore/ModelServerWithStore.java rename to Kafkastreamsserver/src/main/java/com/lightbend/modelserver/withstore/ModelServerWithStore.java diff --git a/server/src/main/java/com/lightbend/queriablestate/HostStoreInfo.java b/Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/HostStoreInfo.java similarity index 100% rename from server/src/main/java/com/lightbend/queriablestate/HostStoreInfo.java rename to Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/HostStoreInfo.java diff --git a/server/src/main/java/com/lightbend/queriablestate/MetadataService.java b/Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/MetadataService.java similarity index 100% rename from server/src/main/java/com/lightbend/queriablestate/MetadataService.java rename to Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/MetadataService.java diff --git a/server/src/main/java/com/lightbend/queriablestate/ModelServingInfo.java b/Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/ModelServingInfo.java similarity index 100% rename from server/src/main/java/com/lightbend/queriablestate/ModelServingInfo.java rename to Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/ModelServingInfo.java diff --git a/server/src/main/java/com/lightbend/queriablestate/QueriesRestService.java b/Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/QueriesRestService.java similarity index 98% rename from server/src/main/java/com/lightbend/queriablestate/QueriesRestService.java rename to Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/QueriesRestService.java index 5de45eb..ee239fc 100644 --- a/server/src/main/java/com/lightbend/queriablestate/QueriesRestService.java +++ b/Kafkastreamsserver/src/main/java/com/lightbend/queriablestate/QueriesRestService.java @@ -64,7 +64,6 @@ public List streamsMetadataForStore(@PathParam("storeName") Strin @Path("{storeName}/value") @Produces(MediaType.APPLICATION_JSON) public ModelServingInfo servingInfo(@PathParam("storeName") final String storeName) { -// return ModelState.getInstance().getCurrentServingInfo(); // Get the Store final ReadableModelStateStore store = streams.store(storeName, new ModelStateStore.ModelStateStoreType()); if (store == null) { diff --git a/README.md b/README.md index db4843e..4e5d15e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,62 @@ -# Kafka Streams Model server +# Model serving -This is a simple implementation of model serving using Kafka Streams -The basic idea behind this implementation is fairly straightforward - there are two streams: +This is an umbrella project for all things model serving that is comprised of multiple projects --**Data Stream** - Kafka stream delivering data record as protobuf buffer (example, modeldescriptor.proto) +-**akkaserver** - implementation of model scoring and statistics serving using Akka streams and Akka HTTP --**Model Stream** - Kafka stream delivering models as protobuf buffer (example, modeldescriptor.proto) + +-**flinkserver** - implementation of model scoring and queryable state using Flink. Both +key-based and partition-based approach are implemented here + +-**kafkaclient** - generic client used for testing of all implementations (except serving samples) +Reads data files, split them into records, converts to protobuf implementations and publishes them to Kafka + +-**kafkaconfiguration** - simple module containing class with Kafka definitions - server location, +topics, etc. used by all applications + +-**kafkastreamserver** - implementation of model scoring and queryable state using Kafka streams +Also includes implementation of custom Kafka streams store. + +-**model** - implementation of support classes representing model and model factories used by all applications. +Because Kafka streams is Java and the rest of implementations are Scala, there are two versions of these +classes - Java and Scala + +-**serving samples** - This module contains simple implementations of model scoring using PMML and +tensorflow model definitions. It is not using any streaming frameworks - just straight Scala code + +-**protobufs** - a module containing protobufs that are used for all streaming frameworks. +This protobufs describe model and data definition in the stream. Because Kafka streams is Java +and the rest of implementations are Scala, both Java and Scala implementations of protobufs are +generated + + +-**sparkML** - examples of using SparkML for machine learning and exporting results to PMML +using JPMML evaluator for Spark - https://github.com/jpmml/jpmml-evaluator-spark + +-**sparkserver** - implementation of model scoring using Spark + +-**utils** - a module containing some utility code. Most importantly it contains embedded Kafka implementation +which can be used for testing in the absence of kafka server. In order to use it, just add these +lines to your code: + + + // Create embedded Kafka and topics + EmbeddedSingleNodeKafkaCluster.start() // Create and start the cluster + EmbeddedSingleNodeKafkaCluster.createTopic(DATA_TOPIC) // Add topic + EmbeddedSingleNodeKafkaCluster.createTopic(MODELS_TOPIC) // Add topic + +If you are using both server and client add kafka embedded only to server and start it before the client +In addition to embedded kafka this module there are some utility classes used by all applications. +Because Kafka streams is Java and the rest of implementations are Scala, there are two versions of these +classes - Java and Scala + +-**data** - a directory of data files used as sources for all applications + +Not included in this project are: + +-**Beam implementation** - Beam Flink runner is still on Scala 2.10 so it is in its own +separate project - ??? + +-**Python/Tensorflow/Keras** - is it is a Python so it is in its own +separate project - ??? + \ No newline at end of file diff --git a/akkaserver/src/main/scala/com/lightbend/modelserver/AkkaModelServer.scala b/akkaserver/src/main/scala/com/lightbend/modelserver/AkkaModelServer.scala new file mode 100644 index 0000000..d3b2200 --- /dev/null +++ b/akkaserver/src/main/scala/com/lightbend/modelserver/AkkaModelServer.scala @@ -0,0 +1,104 @@ +package com.lightbend.modelserver + +import akka.actor.ActorSystem +import akka.http.scaladsl.server.Route +import akka.kafka.{ConsumerSettings, Subscriptions} +import akka.kafka.scaladsl.Consumer +import akka.stream.{ActorMaterializer, SourceShape} +import akka.stream.scaladsl.{GraphDSL, Sink, Source} +import akka.util.Timeout + +import scala.concurrent.duration._ +import com.lightbend.configuration.kafka.ApplicationKafkaParameters +import com.lightbend.configuration.kafka.ApplicationKafkaParameters.{DATA_GROUP, LOCAL_KAFKA_BROKER, MODELS_GROUP} +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.kafka.EmbeddedSingleNodeKafkaCluster +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.serialization.ByteArrayDeserializer +import akka.http.scaladsl.Http +import com.lightbend.modelserver.modelServer.ReadableModelStateStore +import com.lightbend.modelserver.queriablestate.QueriesAkkaHttpResource +import com.lightbend.modelserver.support.scala.{DataReader, ModelToServe} + +/** + * Created by boris on 7/21/17. + */ +object AkkaModelServer { + + implicit val system = ActorSystem("ModelServing") + implicit val materializer = ActorMaterializer() + implicit val executionContext = system.dispatcher + + val dataConsumerSettings = ConsumerSettings(system, new ByteArrayDeserializer, new ByteArrayDeserializer) + .withBootstrapServers(LOCAL_KAFKA_BROKER) + .withGroupId(DATA_GROUP) + .withProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest") + + val modelConsumerSettings = ConsumerSettings(system, new ByteArrayDeserializer, new ByteArrayDeserializer) + .withBootstrapServers(LOCAL_KAFKA_BROKER) + .withGroupId(MODELS_GROUP) + .withProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest") + + def main(args: Array[String]): Unit = { + + + import ApplicationKafkaParameters._ + + // Create embedded Kafka and topics +// EmbeddedSingleNodeKafkaCluster.start() +// EmbeddedSingleNodeKafkaCluster.createTopic(DATA_TOPIC) +// EmbeddedSingleNodeKafkaCluster.createTopic(MODELS_TOPIC) + + val modelStream: Source[ModelToServe, Consumer.Control] = + Consumer.atMostOnceSource(modelConsumerSettings, Subscriptions.topics(MODELS_TOPIC)) + .map(record => ModelToServe.fromByteArray(record.value())).filter(_.isSuccess).map(_.get) + + val dataStream: Source[WineRecord, Consumer.Control] = + Consumer.atMostOnceSource(dataConsumerSettings, Subscriptions.topics(DATA_TOPIC)) + .map(record => DataReader.fromByteArray(record.value())).filter(_.isSuccess).map(_.get) + + val model = new ModelStage() + + def keepModelMaterializedValue[M1, M2, M3](m1: M1, m2: M2, m3: M3): M3 = m3 + + val modelPredictions : Source[Option[Double], ReadableModelStateStore] = Source.fromGraph( + GraphDSL.create(dataStream, modelStream, model)(keepModelMaterializedValue) { + implicit builder => (d, m, w) => + import GraphDSL.Implicits._ + + // wire together the input streams with the model stage (2 in, 1 out) + /* + dataStream --> | | + | model | -> predictions + modelStream -> | | + */ + + d ~> w.dataRecordIn + m ~> w.modelRecordIn + SourceShape(w.scoringResultOut) + } + ) + + + val materializedReadableModelStateStore: ReadableModelStateStore = + modelPredictions + .map(println(_)) + .to(Sink.ignore) // we do not read the results directly + .run() // we run the stream, materializing the stage's StateStore + + startRest(materializedReadableModelStateStore) + } + + def startRest(service : ReadableModelStateStore) : Unit = { + + implicit val timeout = Timeout(10 seconds) + val host = "localhost" + val port = 5000 + val routes: Route = QueriesAkkaHttpResource.storeRoutes(service) + + Http().bindAndHandle(routes, host, port) map + { binding => println(s"REST interface bound to ${binding.localAddress}") } recover { case ex => + println(s"REST interface could not bind to $host:$port", ex.getMessage) + } + } +} \ No newline at end of file diff --git a/akkaserver/src/main/scala/com/lightbend/modelServer/modelServer/ModelStage.scala b/akkaserver/src/main/scala/com/lightbend/modelserver/ModelStage.scala similarity index 91% rename from akkaserver/src/main/scala/com/lightbend/modelServer/modelServer/ModelStage.scala rename to akkaserver/src/main/scala/com/lightbend/modelserver/ModelStage.scala index 639caa2..c0c18ca 100644 --- a/akkaserver/src/main/scala/com/lightbend/modelServer/modelServer/ModelStage.scala +++ b/akkaserver/src/main/scala/com/lightbend/modelserver/ModelStage.scala @@ -1,13 +1,14 @@ -package com.lightbend.modelServer.modelServer +package com.lightbend.modelserver import akka.stream._ -import akka.stream.stage.{ GraphStageLogicWithLogging, _ } +import akka.stream.stage.{GraphStageLogicWithLogging, _} import com.lightbend.model.modeldescriptor.ModelDescriptor import com.lightbend.model.winerecord.WineRecord -import com.lightbend.modelServer.model.Model -import com.lightbend.modelServer.model.PMML.PMMLModel -import com.lightbend.modelServer.model.tensorflow.TensorFlowModel -import com.lightbend.modelServer.{ ModelToServe, ModelToServeStats } +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import com.lightbend.modelserver.modelServer.ReadableModelStateStore +import com.lightbend.model.scala.Model +import com.lightbend.modelserver.support.scala.{ModelToServe, ModelToServeStats} import scala.collection.immutable diff --git a/akkaserver/src/main/scala/com/lightbend/modelserver/ReadableModelStateStore.scala b/akkaserver/src/main/scala/com/lightbend/modelserver/ReadableModelStateStore.scala new file mode 100644 index 0000000..be4ccbe --- /dev/null +++ b/akkaserver/src/main/scala/com/lightbend/modelserver/ReadableModelStateStore.scala @@ -0,0 +1,12 @@ +package com.lightbend.modelserver.modelServer + +import com.lightbend.modelserver.support.scala.ModelToServeStats + + +/** + * Created by boris on 7/21/17. + */ +trait ReadableModelStateStore { + def getCurrentServingInfo: ModelToServeStats +} + diff --git a/akkaserver/src/main/scala/com/lightbend/queriablestate/QueriesAkkaHttpResource.scala b/akkaserver/src/main/scala/com/lightbend/queriablestate/QueriesAkkaHttpResource.scala new file mode 100644 index 0000000..5be7137 --- /dev/null +++ b/akkaserver/src/main/scala/com/lightbend/queriablestate/QueriesAkkaHttpResource.scala @@ -0,0 +1,19 @@ +package com.lightbend.modelserver.queriablestate + + +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.server.Directives._ +import com.lightbend.modelserver.modelServer.ReadableModelStateStore +import com.lightbend.modelserver.support.scala.ModelToServeStats +import de.heikoseeberger.akkahttpjackson.JacksonSupport + +object QueriesAkkaHttpResource extends JacksonSupport { + + def storeRoutes(predictions: ReadableModelStateStore): Route = + get { + path("stats") { + val info: ModelToServeStats = predictions.getCurrentServingInfo + complete(info) + } + } +} diff --git a/build.sbt b/build.sbt index b2bfa5a..93a566e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ -name := "KafkaStreamsModelServer" +name := "ModelServing" version := "1.0" @@ -7,31 +7,60 @@ scalaVersion in ThisBuild := "2.11.11" lazy val protobufs = (project in file("./protobufs")) - .settings( - PB.targets in Compile := Seq( - PB.gens.java -> (sourceManaged in Compile).value, - scalapb.gen(javaConversions=true) -> (sourceManaged in Compile).value - ) + .settings( + PB.targets in Compile := Seq( + PB.gens.java -> (sourceManaged in Compile).value, + scalapb.gen(javaConversions=true) -> (sourceManaged in Compile).value ) + ) -lazy val client = (project in file("./client")) +lazy val kafkaclient = (project in file("./kafkaclient")) .settings(libraryDependencies ++= Dependencies.kafkabaseDependencies) - .dependsOn(protobufs, configuration) + .dependsOn(protobufs, kafkaconfiguration) lazy val model = (project in file("./model")) .settings(libraryDependencies ++= Dependencies.modelsDependencies) - .dependsOn(protobufs) + .dependsOn(protobufs, utils) + -lazy val server = (project in file("./server")) +lazy val kafkastreamsserver = (project in file("./Kafkastreamsserver")) .settings(libraryDependencies ++= Dependencies.kafkaDependencies ++ Dependencies.webDependencies) - .dependsOn(model, configuration) + .dependsOn(model, kafkaconfiguration, utils) + lazy val akkaServer = (project in file("./akkaserver")) .settings(libraryDependencies ++= Dependencies.kafkaDependencies ++ Dependencies.akkaServerDependencies - ++ Dependencies.modelsDependencies ++ Seq(Dependencies.curator)) - .dependsOn(protobufs, configuration) + ++ Dependencies.modelsDependencies) + .dependsOn(model, kafkaconfiguration, utils) + +lazy val flinkserver = (project in file("./flinkserver")) + .settings(libraryDependencies ++= Dependencies.flinkDependencies ++ Seq(Dependencies.joda, Dependencies.akkaslf)) + .settings(dependencyOverrides += "com.typesafe.akka" % "akka-actor-2.11" % "2.3") + .dependsOn(model, kafkaconfiguration, utils) + +lazy val sparkserver = (project in file("./sparkserver")) + .settings(libraryDependencies ++= Dependencies.sparkDependencies) + .settings(dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-core" % "2.8.9") + .settings(dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9") + .settings(dependencyOverrides += "com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % "2.8.9") + .dependsOn(model, kafkaconfiguration, utils) + +lazy val sparkML = (project in file("./sparkML")) + .settings(libraryDependencies ++= Dependencies.sparkMLDependencies) + .settings(dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-core" % "2.8.9") + .settings(dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.9") + .settings(dependencyOverrides += "com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % "2.8.9") -lazy val configuration = (project in file("./configuration")) + +lazy val servingsamples = (project in file("./servingsamples")) + .settings(libraryDependencies ++= Dependencies.modelsDependencies ++ Seq(Dependencies.tensorflowProto)) + + +lazy val kafkaconfiguration = (project in file("./kafkaconfiguration")) + +lazy val utils = (project in file("./utils")) + .settings(libraryDependencies ++= Dependencies.kafkaDependencies ++ Seq(Dependencies.curator)) + .dependsOn(protobufs) lazy val root = (project in file(".")). - aggregate(protobufs, client, model, configuration, server, akkaServer) + aggregate(protobufs, kafkaclient, model, utils, kafkaconfiguration, kafkastreamsserver, akkaServer, sparkserver) diff --git a/client/src/main/scala/com/lightbend/kafka/DataProvider_akka.scala b/client/src/main/scala/com/lightbend/kafka/DataProvider_akka.scala deleted file mode 100644 index 7b1e9f8..0000000 --- a/client/src/main/scala/com/lightbend/kafka/DataProvider_akka.scala +++ /dev/null @@ -1,99 +0,0 @@ -package com.lightbend.kafka - -import java.io.{ ByteArrayOutputStream, File } -import java.nio.file.Paths - -import akka.NotUsed -import akka.actor.ActorSystem -import akka.kafka.ProducerSettings -import akka.kafka.scaladsl.Producer -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ FileIO, Flow, Framing, Sink, Source } -import akka.util.ByteString -import com.lightbend.configuration.kafka.ApplicationKafkaParameters -import com.lightbend.model.winerecord.WineRecord -import org.apache.kafka.clients.producer.ProducerRecord -import org.apache.kafka.common.serialization.ByteArraySerializer -import org.apache.kafka.common.serialization.StringSerializer -import scala.concurrent.duration._ - -import scala.collection.immutable -import scala.concurrent.Future - -/** - * Created by boris on 5/10/17. - * - * Application publishing models from /data directory to Kafka - */ -object DataProvider_akka { - - implicit val system = ActorSystem("DataProvider") - implicit val mat = ActorMaterializer() - import system.dispatcher - - val producerSettings: ProducerSettings[Array[Byte], Array[Byte]] = - ProducerSettings(system, new ByteArraySerializer, new ByteArraySerializer) - .withBootstrapServers(ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) - - val file = "data/winequality_red.csv" - val timeInterval = 1.second - - def main(args: Array[String]) { - createTopic(ApplicationKafkaParameters.DATA_TOPIC) - - loadRecordsIntoMemory(file).map { loadedRecords => - Source.cycle(() => loadedRecords.iterator) - .statefulMapConcat(() => { - val bos = new ByteArrayOutputStream() - var lineCounter = 0 - def logEvery(n: Int) = { - lineCounter += 1 - if (lineCounter % n == 0) println(s"Processed ${lineCounter} record") - } - - wine => { - bos.reset() - wine.writeTo(bos) - new ProducerRecord[Array[Byte], Array[Byte]](ApplicationKafkaParameters.DATA_TOPIC, bos.toByteArray) :: Nil - } - }) - .via(delay) - .runWith(Producer.plainSink(producerSettings)) - } - } - - private def createTopic(topic: String): Unit = { - val sender = KafkaMessageSender(ApplicationKafkaParameters.LOCAL_KAFKA_BROKER, ApplicationKafkaParameters.LOCAL_ZOOKEEPER_HOST) - sender.createTopic(topic) - } - - /** Delays each element by con*/ - private def delay[T] : Flow[T, T, NotUsed] = { - Flow[T].mapAsync(1) { el => - akka.pattern.after(timeInterval, system.scheduler)(Future.successful(el)) - } - } - - def loadRecordsIntoMemory(file: String): Future[immutable.Seq[WineRecord]] = { - FileIO.fromPath(new File(file).toPath) - .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = Int.MaxValue)) - .map { line => - val cols = line.utf8String.split(";").map(_.trim) - new WineRecord( - fixedAcidity = cols(0).toDouble, - volatileAcidity = cols(1).toDouble, - citricAcid = cols(2).toDouble, - residualSugar = cols(3).toDouble, - chlorides = cols(4).toDouble, - freeSulfurDioxide = cols(5).toDouble, - totalSulfurDioxide = cols(6).toDouble, - density = cols(7).toDouble, - pH = cols(8).toDouble, - sulphates = cols(9).toDouble, - alcohol = cols(10).toDouble, - dataType = "wine" - ) - } - .runWith(Sink.seq) - } -} diff --git a/data/WineQuality/saved_model.pb b/data/WineQuality/saved_model.pb new file mode 100644 index 0000000..b5464b5 Binary files /dev/null and b/data/WineQuality/saved_model.pb differ diff --git a/data/WineQuality/variables/variables.data-00000-of-00001 b/data/WineQuality/variables/variables.data-00000-of-00001 new file mode 100644 index 0000000..996cd0c Binary files /dev/null and b/data/WineQuality/variables/variables.data-00000-of-00001 differ diff --git a/data/WineQuality/variables/variables.index b/data/WineQuality/variables/variables.index new file mode 100644 index 0000000..ea4e3dd Binary files /dev/null and b/data/WineQuality/variables/variables.index differ diff --git a/data/winequality_red_names.csv b/data/winequality_red_names.csv new file mode 100644 index 0000000..378016d --- /dev/null +++ b/data/winequality_red_names.csv @@ -0,0 +1,1600 @@ +"fixed acidity";"volatile acidity";"citric acid";"residual sugar";"chlorides";"free sulfur dioxide";"total sulfur dioxide";"density";"pH";"sulphates";"alcohol";"quality" +7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4;5 +7.8;0.88;0;2.6;0.098;25;67;0.9968;3.2;0.68;9.8;5 +7.8;0.76;0.04;2.3;0.092;15;54;0.997;3.26;0.65;9.8;5 +11.2;0.28;0.56;1.9;0.075;17;60;0.998;3.16;0.58;9.8;6 +7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4;5 +7.4;0.66;0;1.8;0.075;13;40;0.9978;3.51;0.56;9.4;5 +7.9;0.6;0.06;1.6;0.069;15;59;0.9964;3.3;0.46;9.4;5 +7.3;0.65;0;1.2;0.065;15;21;0.9946;3.39;0.47;10;7 +7.8;0.58;0.02;2;0.073;9;18;0.9968;3.36;0.57;9.5;7 +7.5;0.5;0.36;6.1;0.071;17;102;0.9978;3.35;0.8;10.5;5 +6.7;0.58;0.08;1.8;0.097;15;65;0.9959;3.28;0.54;9.2;5 +7.5;0.5;0.36;6.1;0.071;17;102;0.9978;3.35;0.8;10.5;5 +5.6;0.615;0;1.6;0.089;16;59;0.9943;3.58;0.52;9.9;5 +7.8;0.61;0.29;1.6;0.114;9;29;0.9974;3.26;1.56;9.1;5 +8.9;0.62;0.18;3.8;0.176;52;145;0.9986;3.16;0.88;9.2;5 +8.9;0.62;0.19;3.9;0.17;51;148;0.9986;3.17;0.93;9.2;5 +8.5;0.28;0.56;1.8;0.092;35;103;0.9969;3.3;0.75;10.5;7 +8.1;0.56;0.28;1.7;0.368;16;56;0.9968;3.11;1.28;9.3;5 +7.4;0.59;0.08;4.4;0.086;6;29;0.9974;3.38;0.5;9;4 +7.9;0.32;0.51;1.8;0.341;17;56;0.9969;3.04;1.08;9.2;6 +8.9;0.22;0.48;1.8;0.077;29;60;0.9968;3.39;0.53;9.4;6 +7.6;0.39;0.31;2.3;0.082;23;71;0.9982;3.52;0.65;9.7;5 +7.9;0.43;0.21;1.6;0.106;10;37;0.9966;3.17;0.91;9.5;5 +8.5;0.49;0.11;2.3;0.084;9;67;0.9968;3.17;0.53;9.4;5 +6.9;0.4;0.14;2.4;0.085;21;40;0.9968;3.43;0.63;9.7;6 +6.3;0.39;0.16;1.4;0.08;11;23;0.9955;3.34;0.56;9.3;5 +7.6;0.41;0.24;1.8;0.08;4;11;0.9962;3.28;0.59;9.5;5 +7.9;0.43;0.21;1.6;0.106;10;37;0.9966;3.17;0.91;9.5;5 +7.1;0.71;0;1.9;0.08;14;35;0.9972;3.47;0.55;9.4;5 +7.8;0.645;0;2;0.082;8;16;0.9964;3.38;0.59;9.8;6 +6.7;0.675;0.07;2.4;0.089;17;82;0.9958;3.35;0.54;10.1;5 +6.9;0.685;0;2.5;0.105;22;37;0.9966;3.46;0.57;10.6;6 +8.3;0.655;0.12;2.3;0.083;15;113;0.9966;3.17;0.66;9.8;5 +6.9;0.605;0.12;10.7;0.073;40;83;0.9993;3.45;0.52;9.4;6 +5.2;0.32;0.25;1.8;0.103;13;50;0.9957;3.38;0.55;9.2;5 +7.8;0.645;0;5.5;0.086;5;18;0.9986;3.4;0.55;9.6;6 +7.8;0.6;0.14;2.4;0.086;3;15;0.9975;3.42;0.6;10.8;6 +8.1;0.38;0.28;2.1;0.066;13;30;0.9968;3.23;0.73;9.7;7 +5.7;1.13;0.09;1.5;0.172;7;19;0.994;3.5;0.48;9.8;4 +7.3;0.45;0.36;5.9;0.074;12;87;0.9978;3.33;0.83;10.5;5 +7.3;0.45;0.36;5.9;0.074;12;87;0.9978;3.33;0.83;10.5;5 +8.8;0.61;0.3;2.8;0.088;17;46;0.9976;3.26;0.51;9.3;4 +7.5;0.49;0.2;2.6;0.332;8;14;0.9968;3.21;0.9;10.5;6 +8.1;0.66;0.22;2.2;0.069;9;23;0.9968;3.3;1.2;10.3;5 +6.8;0.67;0.02;1.8;0.05;5;11;0.9962;3.48;0.52;9.5;5 +4.6;0.52;0.15;2.1;0.054;8;65;0.9934;3.9;0.56;13.1;4 +7.7;0.935;0.43;2.2;0.114;22;114;0.997;3.25;0.73;9.2;5 +8.7;0.29;0.52;1.6;0.113;12;37;0.9969;3.25;0.58;9.5;5 +6.4;0.4;0.23;1.6;0.066;5;12;0.9958;3.34;0.56;9.2;5 +5.6;0.31;0.37;1.4;0.074;12;96;0.9954;3.32;0.58;9.2;5 +8.8;0.66;0.26;1.7;0.074;4;23;0.9971;3.15;0.74;9.2;5 +6.6;0.52;0.04;2.2;0.069;8;15;0.9956;3.4;0.63;9.4;6 +6.6;0.5;0.04;2.1;0.068;6;14;0.9955;3.39;0.64;9.4;6 +8.6;0.38;0.36;3;0.081;30;119;0.997;3.2;0.56;9.4;5 +7.6;0.51;0.15;2.8;0.11;33;73;0.9955;3.17;0.63;10.2;6 +7.7;0.62;0.04;3.8;0.084;25;45;0.9978;3.34;0.53;9.5;5 +10.2;0.42;0.57;3.4;0.07;4;10;0.9971;3.04;0.63;9.6;5 +7.5;0.63;0.12;5.1;0.111;50;110;0.9983;3.26;0.77;9.4;5 +7.8;0.59;0.18;2.3;0.076;17;54;0.9975;3.43;0.59;10;5 +7.3;0.39;0.31;2.4;0.074;9;46;0.9962;3.41;0.54;9.4;6 +8.8;0.4;0.4;2.2;0.079;19;52;0.998;3.44;0.64;9.2;5 +7.7;0.69;0.49;1.8;0.115;20;112;0.9968;3.21;0.71;9.3;5 +7.5;0.52;0.16;1.9;0.085;12;35;0.9968;3.38;0.62;9.5;7 +7;0.735;0.05;2;0.081;13;54;0.9966;3.39;0.57;9.8;5 +7.2;0.725;0.05;4.65;0.086;4;11;0.9962;3.41;0.39;10.9;5 +7.2;0.725;0.05;4.65;0.086;4;11;0.9962;3.41;0.39;10.9;5 +7.5;0.52;0.11;1.5;0.079;11;39;0.9968;3.42;0.58;9.6;5 +6.6;0.705;0.07;1.6;0.076;6;15;0.9962;3.44;0.58;10.7;5 +9.3;0.32;0.57;2;0.074;27;65;0.9969;3.28;0.79;10.7;5 +8;0.705;0.05;1.9;0.074;8;19;0.9962;3.34;0.95;10.5;6 +7.7;0.63;0.08;1.9;0.076;15;27;0.9967;3.32;0.54;9.5;6 +7.7;0.67;0.23;2.1;0.088;17;96;0.9962;3.32;0.48;9.5;5 +7.7;0.69;0.22;1.9;0.084;18;94;0.9961;3.31;0.48;9.5;5 +8.3;0.675;0.26;2.1;0.084;11;43;0.9976;3.31;0.53;9.2;4 +9.7;0.32;0.54;2.5;0.094;28;83;0.9984;3.28;0.82;9.6;5 +8.8;0.41;0.64;2.2;0.093;9;42;0.9986;3.54;0.66;10.5;5 +8.8;0.41;0.64;2.2;0.093;9;42;0.9986;3.54;0.66;10.5;5 +6.8;0.785;0;2.4;0.104;14;30;0.9966;3.52;0.55;10.7;6 +6.7;0.75;0.12;2;0.086;12;80;0.9958;3.38;0.52;10.1;5 +8.3;0.625;0.2;1.5;0.08;27;119;0.9972;3.16;1.12;9.1;4 +6.2;0.45;0.2;1.6;0.069;3;15;0.9958;3.41;0.56;9.2;5 +7.8;0.43;0.7;1.9;0.464;22;67;0.9974;3.13;1.28;9.4;5 +7.4;0.5;0.47;2;0.086;21;73;0.997;3.36;0.57;9.1;5 +7.3;0.67;0.26;1.8;0.401;16;51;0.9969;3.16;1.14;9.4;5 +6.3;0.3;0.48;1.8;0.069;18;61;0.9959;3.44;0.78;10.3;6 +6.9;0.55;0.15;2.2;0.076;19;40;0.9961;3.41;0.59;10.1;5 +8.6;0.49;0.28;1.9;0.11;20;136;0.9972;2.93;1.95;9.9;6 +7.7;0.49;0.26;1.9;0.062;9;31;0.9966;3.39;0.64;9.6;5 +9.3;0.39;0.44;2.1;0.107;34;125;0.9978;3.14;1.22;9.5;5 +7;0.62;0.08;1.8;0.076;8;24;0.9978;3.48;0.53;9;5 +7.9;0.52;0.26;1.9;0.079;42;140;0.9964;3.23;0.54;9.5;5 +8.6;0.49;0.28;1.9;0.11;20;136;0.9972;2.93;1.95;9.9;6 +8.6;0.49;0.29;2;0.11;19;133;0.9972;2.93;1.98;9.8;5 +7.7;0.49;0.26;1.9;0.062;9;31;0.9966;3.39;0.64;9.6;5 +5;1.02;0.04;1.4;0.045;41;85;0.9938;3.75;0.48;10.5;4 +4.7;0.6;0.17;2.3;0.058;17;106;0.9932;3.85;0.6;12.9;6 +6.8;0.775;0;3;0.102;8;23;0.9965;3.45;0.56;10.7;5 +7;0.5;0.25;2;0.07;3;22;0.9963;3.25;0.63;9.2;5 +7.6;0.9;0.06;2.5;0.079;5;10;0.9967;3.39;0.56;9.8;5 +8.1;0.545;0.18;1.9;0.08;13;35;0.9972;3.3;0.59;9;6 +8.3;0.61;0.3;2.1;0.084;11;50;0.9972;3.4;0.61;10.2;6 +7.8;0.5;0.3;1.9;0.075;8;22;0.9959;3.31;0.56;10.4;6 +8.1;0.545;0.18;1.9;0.08;13;35;0.9972;3.3;0.59;9;6 +8.1;0.575;0.22;2.1;0.077;12;65;0.9967;3.29;0.51;9.2;5 +7.2;0.49;0.24;2.2;0.07;5;36;0.996;3.33;0.48;9.4;5 +8.1;0.575;0.22;2.1;0.077;12;65;0.9967;3.29;0.51;9.2;5 +7.8;0.41;0.68;1.7;0.467;18;69;0.9973;3.08;1.31;9.3;5 +6.2;0.63;0.31;1.7;0.088;15;64;0.9969;3.46;0.79;9.3;5 +8;0.33;0.53;2.5;0.091;18;80;0.9976;3.37;0.8;9.6;6 +8.1;0.785;0.52;2;0.122;37;153;0.9969;3.21;0.69;9.3;5 +7.8;0.56;0.19;1.8;0.104;12;47;0.9964;3.19;0.93;9.5;5 +8.4;0.62;0.09;2.2;0.084;11;108;0.9964;3.15;0.66;9.8;5 +8.4;0.6;0.1;2.2;0.085;14;111;0.9964;3.15;0.66;9.8;5 +10.1;0.31;0.44;2.3;0.08;22;46;0.9988;3.32;0.67;9.7;6 +7.8;0.56;0.19;1.8;0.104;12;47;0.9964;3.19;0.93;9.5;5 +9.4;0.4;0.31;2.2;0.09;13;62;0.9966;3.07;0.63;10.5;6 +8.3;0.54;0.28;1.9;0.077;11;40;0.9978;3.39;0.61;10;6 +7.8;0.56;0.12;2;0.082;7;28;0.997;3.37;0.5;9.4;6 +8.8;0.55;0.04;2.2;0.119;14;56;0.9962;3.21;0.6;10.9;6 +7;0.69;0.08;1.8;0.097;22;89;0.9959;3.34;0.54;9.2;6 +7.3;1.07;0.09;1.7;0.178;10;89;0.9962;3.3;0.57;9;5 +8.8;0.55;0.04;2.2;0.119;14;56;0.9962;3.21;0.6;10.9;6 +7.3;0.695;0;2.5;0.075;3;13;0.998;3.49;0.52;9.2;5 +8;0.71;0;2.6;0.08;11;34;0.9976;3.44;0.53;9.5;5 +7.8;0.5;0.17;1.6;0.082;21;102;0.996;3.39;0.48;9.5;5 +9;0.62;0.04;1.9;0.146;27;90;0.9984;3.16;0.7;9.4;5 +8.2;1.33;0;1.7;0.081;3;12;0.9964;3.53;0.49;10.9;5 +8.1;1.33;0;1.8;0.082;3;12;0.9964;3.54;0.48;10.9;5 +8;0.59;0.16;1.8;0.065;3;16;0.9962;3.42;0.92;10.5;7 +6.1;0.38;0.15;1.8;0.072;6;19;0.9955;3.42;0.57;9.4;5 +8;0.745;0.56;2;0.118;30;134;0.9968;3.24;0.66;9.4;5 +5.6;0.5;0.09;2.3;0.049;17;99;0.9937;3.63;0.63;13;5 +5.6;0.5;0.09;2.3;0.049;17;99;0.9937;3.63;0.63;13;5 +6.6;0.5;0.01;1.5;0.06;17;26;0.9952;3.4;0.58;9.8;6 +7.9;1.04;0.05;2.2;0.084;13;29;0.9959;3.22;0.55;9.9;6 +8.4;0.745;0.11;1.9;0.09;16;63;0.9965;3.19;0.82;9.6;5 +8.3;0.715;0.15;1.8;0.089;10;52;0.9968;3.23;0.77;9.5;5 +7.2;0.415;0.36;2;0.081;13;45;0.9972;3.48;0.64;9.2;5 +7.8;0.56;0.19;2.1;0.081;15;105;0.9962;3.33;0.54;9.5;5 +7.8;0.56;0.19;2;0.081;17;108;0.9962;3.32;0.54;9.5;5 +8.4;0.745;0.11;1.9;0.09;16;63;0.9965;3.19;0.82;9.6;5 +8.3;0.715;0.15;1.8;0.089;10;52;0.9968;3.23;0.77;9.5;5 +5.2;0.34;0;1.8;0.05;27;63;0.9916;3.68;0.79;14;6 +6.3;0.39;0.08;1.7;0.066;3;20;0.9954;3.34;0.58;9.4;5 +5.2;0.34;0;1.8;0.05;27;63;0.9916;3.68;0.79;14;6 +8.1;0.67;0.55;1.8;0.117;32;141;0.9968;3.17;0.62;9.4;5 +5.8;0.68;0.02;1.8;0.087;21;94;0.9944;3.54;0.52;10;5 +7.6;0.49;0.26;1.6;0.236;10;88;0.9968;3.11;0.8;9.3;5 +6.9;0.49;0.1;2.3;0.074;12;30;0.9959;3.42;0.58;10.2;6 +8.2;0.4;0.44;2.8;0.089;11;43;0.9975;3.53;0.61;10.5;6 +7.3;0.33;0.47;2.1;0.077;5;11;0.9958;3.33;0.53;10.3;6 +9.2;0.52;1;3.4;0.61;32;69;0.9996;2.74;2.0;9.4;4 +7.5;0.6;0.03;1.8;0.095;25;99;0.995;3.35;0.54;10.1;5 +7.5;0.6;0.03;1.8;0.095;25;99;0.995;3.35;0.54;10.1;5 +7.1;0.43;0.42;5.5;0.07;29;129;0.9973;3.42;0.72;10.5;5 +7.1;0.43;0.42;5.5;0.071;28;128;0.9973;3.42;0.71;10.5;5 +7.1;0.43;0.42;5.5;0.07;29;129;0.9973;3.42;0.72;10.5;5 +7.1;0.43;0.42;5.5;0.071;28;128;0.9973;3.42;0.71;10.5;5 +7.1;0.68;0;2.2;0.073;12;22;0.9969;3.48;0.5;9.3;5 +6.8;0.6;0.18;1.9;0.079;18;86;0.9968;3.59;0.57;9.3;6 +7.6;0.95;0.03;2;0.09;7;20;0.9959;3.2;0.56;9.6;5 +7.6;0.68;0.02;1.3;0.072;9;20;0.9965;3.17;1.08;9.2;4 +7.8;0.53;0.04;1.7;0.076;17;31;0.9964;3.33;0.56;10;6 +7.4;0.6;0.26;7.3;0.07;36;121;0.9982;3.37;0.49;9.4;5 +7.3;0.59;0.26;7.2;0.07;35;121;0.9981;3.37;0.49;9.4;5 +7.8;0.63;0.48;1.7;0.1;14;96;0.9961;3.19;0.62;9.5;5 +6.8;0.64;0.1;2.1;0.085;18;101;0.9956;3.34;0.52;10.2;5 +7.3;0.55;0.03;1.6;0.072;17;42;0.9956;3.37;0.48;9;4 +6.8;0.63;0.07;2.1;0.089;11;44;0.9953;3.47;0.55;10.4;6 +7.5;0.705;0.24;1.8;0.36;15;63;0.9964;3;1.59;9.5;5 +7.9;0.885;0.03;1.8;0.058;4;8;0.9972;3.36;0.33;9.1;4 +8;0.42;0.17;2;0.073;6;18;0.9972;3.29;0.61;9.2;6 +8;0.42;0.17;2;0.073;6;18;0.9972;3.29;0.61;9.2;6 +7.4;0.62;0.05;1.9;0.068;24;42;0.9961;3.42;0.57;11.5;6 +7.3;0.38;0.21;2;0.08;7;35;0.9961;3.33;0.47;9.5;5 +6.9;0.5;0.04;1.5;0.085;19;49;0.9958;3.35;0.78;9.5;5 +7.3;0.38;0.21;2;0.08;7;35;0.9961;3.33;0.47;9.5;5 +7.5;0.52;0.42;2.3;0.087;8;38;0.9972;3.58;0.61;10.5;6 +7;0.805;0;2.5;0.068;7;20;0.9969;3.48;0.56;9.6;5 +8.8;0.61;0.14;2.4;0.067;10;42;0.9969;3.19;0.59;9.5;5 +8.8;0.61;0.14;2.4;0.067;10;42;0.9969;3.19;0.59;9.5;5 +8.9;0.61;0.49;2;0.27;23;110;0.9972;3.12;1.02;9.3;5 +7.2;0.73;0.02;2.5;0.076;16;42;0.9972;3.44;0.52;9.3;5 +6.8;0.61;0.2;1.8;0.077;11;65;0.9971;3.54;0.58;9.3;5 +6.7;0.62;0.21;1.9;0.079;8;62;0.997;3.52;0.58;9.3;6 +8.9;0.31;0.57;2;0.111;26;85;0.9971;3.26;0.53;9.7;5 +7.4;0.39;0.48;2;0.082;14;67;0.9972;3.34;0.55;9.2;5 +7.7;0.705;0.1;2.6;0.084;9;26;0.9976;3.39;0.49;9.7;5 +7.9;0.5;0.33;2;0.084;15;143;0.9968;3.2;0.55;9.5;5 +7.9;0.49;0.32;1.9;0.082;17;144;0.9968;3.2;0.55;9.5;5 +8.2;0.5;0.35;2.9;0.077;21;127;0.9976;3.23;0.62;9.4;5 +6.4;0.37;0.25;1.9;0.074;21;49;0.9974;3.57;0.62;9.8;6 +6.8;0.63;0.12;3.8;0.099;16;126;0.9969;3.28;0.61;9.5;5 +7.6;0.55;0.21;2.2;0.071;7;28;0.9964;3.28;0.55;9.7;5 +7.6;0.55;0.21;2.2;0.071;7;28;0.9964;3.28;0.55;9.7;5 +7.8;0.59;0.33;2;0.074;24;120;0.9968;3.25;0.54;9.4;5 +7.3;0.58;0.3;2.4;0.074;15;55;0.9968;3.46;0.59;10.2;5 +11.5;0.3;0.6;2;0.067;12;27;0.9981;3.11;0.97;10.1;6 +5.4;0.835;0.08;1.2;0.046;13;93;0.9924;3.57;0.85;13;7 +6.9;1.09;0.06;2.1;0.061;12;31;0.9948;3.51;0.43;11.4;4 +9.6;0.32;0.47;1.4;0.056;9;24;0.99695;3.22;0.82;10.3;7 +8.8;0.37;0.48;2.1;0.097;39;145;0.9975;3.04;1.03;9.3;5 +6.8;0.5;0.11;1.5;0.075;16;49;0.99545;3.36;0.79;9.5;5 +7;0.42;0.35;1.6;0.088;16;39;0.9961;3.34;0.55;9.2;5 +7;0.43;0.36;1.6;0.089;14;37;0.99615;3.34;0.56;9.2;6 +12.8;0.3;0.74;2.6;0.095;9;28;0.9994;3.2;0.77;10.8;7 +12.8;0.3;0.74;2.6;0.095;9;28;0.9994;3.2;0.77;10.8;7 +7.8;0.57;0.31;1.8;0.069;26;120;0.99625;3.29;0.53;9.3;5 +7.8;0.44;0.28;2.7;0.1;18;95;0.9966;3.22;0.67;9.4;5 +11;0.3;0.58;2.1;0.054;7;19;0.998;3.31;0.88;10.5;7 +9.7;0.53;0.6;2;0.039;5;19;0.99585;3.3;0.86;12.4;6 +8;0.725;0.24;2.8;0.083;10;62;0.99685;3.35;0.56;10;6 +11.6;0.44;0.64;2.1;0.059;5;15;0.998;3.21;0.67;10.2;6 +8.2;0.57;0.26;2.2;0.06;28;65;0.9959;3.3;0.43;10.1;5 +7.8;0.735;0.08;2.4;0.092;10;41;0.9974;3.24;0.71;9.8;6 +7;0.49;0.49;5.6;0.06;26;121;0.9974;3.34;0.76;10.5;5 +8.7;0.625;0.16;2;0.101;13;49;0.9962;3.14;0.57;11;5 +8.1;0.725;0.22;2.2;0.072;11;41;0.9967;3.36;0.55;9.1;5 +7.5;0.49;0.19;1.9;0.076;10;44;0.9957;3.39;0.54;9.7;5 +7.8;0.53;0.33;2.4;0.08;24;144;0.99655;3.3;0.6;9.5;5 +7.8;0.34;0.37;2;0.082;24;58;0.9964;3.34;0.59;9.4;6 +7.4;0.53;0.26;2;0.101;16;72;0.9957;3.15;0.57;9.4;5 +6.8;0.61;0.04;1.5;0.057;5;10;0.99525;3.42;0.6;9.5;5 +8.6;0.645;0.25;2;0.083;8;28;0.99815;3.28;0.6;10;6 +8.4;0.635;0.36;2;0.089;15;55;0.99745;3.31;0.57;10.4;4 +7.7;0.43;0.25;2.6;0.073;29;63;0.99615;3.37;0.58;10.5;6 +8.9;0.59;0.5;2;0.337;27;81;0.9964;3.04;1.61;9.5;6 +9;0.82;0.14;2.6;0.089;9;23;0.9984;3.39;0.63;9.8;5 +7.7;0.43;0.25;2.6;0.073;29;63;0.99615;3.37;0.58;10.5;6 +6.9;0.52;0.25;2.6;0.081;10;37;0.99685;3.46;0.5;11;5 +5.2;0.48;0.04;1.6;0.054;19;106;0.9927;3.54;0.62;12.2;7 +8;0.38;0.06;1.8;0.078;12;49;0.99625;3.37;0.52;9.9;6 +8.5;0.37;0.2;2.8;0.09;18;58;0.998;3.34;0.7;9.6;6 +6.9;0.52;0.25;2.6;0.081;10;37;0.99685;3.46;0.5;11;5 +8.2;1;0.09;2.3;0.065;7;37;0.99685;3.32;0.55;9;6 +7.2;0.63;0;1.9;0.097;14;38;0.99675;3.37;0.58;9;6 +7.2;0.63;0;1.9;0.097;14;38;0.99675;3.37;0.58;9;6 +7.2;0.645;0;1.9;0.097;15;39;0.99675;3.37;0.58;9.2;6 +7.2;0.63;0;1.9;0.097;14;38;0.99675;3.37;0.58;9;6 +8.2;1;0.09;2.3;0.065;7;37;0.99685;3.32;0.55;9;6 +8.9;0.635;0.37;1.7;0.263;5;62;0.9971;3;1.09;9.3;5 +12;0.38;0.56;2.1;0.093;6;24;0.99925;3.14;0.71;10.9;6 +7.7;0.58;0.1;1.8;0.102;28;109;0.99565;3.08;0.49;9.8;6 +15;0.21;0.44;2.2;0.075;10;24;1.00005;3.07;0.84;9.2;7 +15;0.21;0.44;2.2;0.075;10;24;1.00005;3.07;0.84;9.2;7 +7.3;0.66;0;2;0.084;6;23;0.9983;3.61;0.96;9.9;6 +7.1;0.68;0.07;1.9;0.075;16;51;0.99685;3.38;0.52;9.5;5 +8.2;0.6;0.17;2.3;0.072;11;73;0.9963;3.2;0.45;9.3;5 +7.7;0.53;0.06;1.7;0.074;9;39;0.99615;3.35;0.48;9.8;6 +7.3;0.66;0;2;0.084;6;23;0.9983;3.61;0.96;9.9;6 +10.8;0.32;0.44;1.6;0.063;16;37;0.9985;3.22;0.78;10;6 +7.1;0.6;0;1.8;0.074;16;34;0.9972;3.47;0.7;9.9;6 +11.1;0.35;0.48;3.1;0.09;5;21;0.9986;3.17;0.53;10.5;5 +7.7;0.775;0.42;1.9;0.092;8;86;0.9959;3.23;0.59;9.5;5 +7.1;0.6;0;1.8;0.074;16;34;0.9972;3.47;0.7;9.9;6 +8;0.57;0.23;3.2;0.073;17;119;0.99675;3.26;0.57;9.3;5 +9.4;0.34;0.37;2.2;0.075;5;13;0.998;3.22;0.62;9.2;5 +6.6;0.695;0;2.1;0.075;12;56;0.9968;3.49;0.67;9.2;5 +7.7;0.41;0.76;1.8;0.611;8;45;0.9968;3.06;1.26;9.4;5 +10;0.31;0.47;2.6;0.085;14;33;0.99965;3.36;0.8;10.5;7 +7.9;0.33;0.23;1.7;0.077;18;45;0.99625;3.29;0.65;9.3;5 +7;0.975;0.04;2;0.087;12;67;0.99565;3.35;0.6;9.4;4 +8;0.52;0.03;1.7;0.07;10;35;0.99575;3.34;0.57;10;5 +7.9;0.37;0.23;1.8;0.077;23;49;0.9963;3.28;0.67;9.3;5 +12.5;0.56;0.49;2.4;0.064;5;27;0.9999;3.08;0.87;10.9;5 +11.8;0.26;0.52;1.8;0.071;6;10;0.9968;3.2;0.72;10.2;7 +8.1;0.87;0;3.3;0.096;26;61;1.00025;3.6;0.72;9.8;4 +7.9;0.35;0.46;3.6;0.078;15;37;0.9973;3.35;0.86;12.8;8 +6.9;0.54;0.04;3;0.077;7;27;0.9987;3.69;0.91;9.4;6 +11.5;0.18;0.51;4;0.104;4;23;0.9996;3.28;0.97;10.1;6 +7.9;0.545;0.06;4;0.087;27;61;0.9965;3.36;0.67;10.7;6 +11.5;0.18;0.51;4;0.104;4;23;0.9996;3.28;0.97;10.1;6 +10.9;0.37;0.58;4;0.071;17;65;0.99935;3.22;0.78;10.1;5 +8.4;0.715;0.2;2.4;0.076;10;38;0.99735;3.31;0.64;9.4;5 +7.5;0.65;0.18;7;0.088;27;94;0.99915;3.38;0.77;9.4;5 +7.9;0.545;0.06;4;0.087;27;61;0.9965;3.36;0.67;10.7;6 +6.9;0.54;0.04;3;0.077;7;27;0.9987;3.69;0.91;9.4;6 +11.5;0.18;0.51;4;0.104;4;23;0.9996;3.28;0.97;10.1;6 +10.3;0.32;0.45;6.4;0.073;5;13;0.9976;3.23;0.82;12.6;8 +8.9;0.4;0.32;5.6;0.087;10;47;0.9991;3.38;0.77;10.5;7 +11.4;0.26;0.44;3.6;0.071;6;19;0.9986;3.12;0.82;9.3;6 +7.7;0.27;0.68;3.5;0.358;5;10;0.9972;3.25;1.08;9.9;7 +7.6;0.52;0.12;3;0.067;12;53;0.9971;3.36;0.57;9.1;5 +8.9;0.4;0.32;5.6;0.087;10;47;0.9991;3.38;0.77;10.5;7 +9.9;0.59;0.07;3.4;0.102;32;71;1.00015;3.31;0.71;9.8;5 +9.9;0.59;0.07;3.4;0.102;32;71;1.00015;3.31;0.71;9.8;5 +12;0.45;0.55;2;0.073;25;49;0.9997;3.1;0.76;10.3;6 +7.5;0.4;0.12;3;0.092;29;53;0.9967;3.37;0.7;10.3;6 +8.7;0.52;0.09;2.5;0.091;20;49;0.9976;3.34;0.86;10.6;7 +11.6;0.42;0.53;3.3;0.105;33;98;1.001;3.2;0.95;9.2;5 +8.7;0.52;0.09;2.5;0.091;20;49;0.9976;3.34;0.86;10.6;7 +11;0.2;0.48;2;0.343;6;18;0.9979;3.3;0.71;10.5;5 +10.4;0.55;0.23;2.7;0.091;18;48;0.9994;3.22;0.64;10.3;6 +6.9;0.36;0.25;2.4;0.098;5;16;0.9964;3.41;0.6;10.1;6 +13.3;0.34;0.52;3.2;0.094;17;53;1.0014;3.05;0.81;9.5;6 +10.8;0.5;0.46;2.5;0.073;5;27;1.0001;3.05;0.64;9.5;5 +10.6;0.83;0.37;2.6;0.086;26;70;0.9981;3.16;0.52;9.9;5 +7.1;0.63;0.06;2;0.083;8;29;0.99855;3.67;0.73;9.6;5 +7.2;0.65;0.02;2.3;0.094;5;31;0.9993;3.67;0.8;9.7;5 +6.9;0.67;0.06;2.1;0.08;8;33;0.99845;3.68;0.71;9.6;5 +7.5;0.53;0.06;2.6;0.086;20;44;0.9965;3.38;0.59;10.7;6 +11.1;0.18;0.48;1.5;0.068;7;15;0.9973;3.22;0.64;10.1;6 +8.3;0.705;0.12;2.6;0.092;12;28;0.9994;3.51;0.72;10;5 +7.4;0.67;0.12;1.6;0.186;5;21;0.996;3.39;0.54;9.5;5 +8.4;0.65;0.6;2.1;0.112;12;90;0.9973;3.2;0.52;9.2;5 +10.3;0.53;0.48;2.5;0.063;6;25;0.9998;3.12;0.59;9.3;6 +7.6;0.62;0.32;2.2;0.082;7;54;0.9966;3.36;0.52;9.4;5 +10.3;0.41;0.42;2.4;0.213;6;14;0.9994;3.19;0.62;9.5;6 +10.3;0.43;0.44;2.4;0.214;5;12;0.9994;3.19;0.63;9.5;6 +7.4;0.29;0.38;1.7;0.062;9;30;0.9968;3.41;0.53;9.5;6 +10.3;0.53;0.48;2.5;0.063;6;25;0.9998;3.12;0.59;9.3;6 +7.9;0.53;0.24;2;0.072;15;105;0.996;3.27;0.54;9.4;6 +9;0.46;0.31;2.8;0.093;19;98;0.99815;3.32;0.63;9.5;6 +8.6;0.47;0.3;3;0.076;30;135;0.9976;3.3;0.53;9.4;5 +7.4;0.36;0.29;2.6;0.087;26;72;0.99645;3.39;0.68;11;5 +7.1;0.35;0.29;2.5;0.096;20;53;0.9962;3.42;0.65;11;6 +9.6;0.56;0.23;3.4;0.102;37;92;0.9996;3.3;0.65;10.1;5 +9.6;0.77;0.12;2.9;0.082;30;74;0.99865;3.3;0.64;10.4;6 +9.8;0.66;0.39;3.2;0.083;21;59;0.9989;3.37;0.71;11.5;7 +9.6;0.77;0.12;2.9;0.082;30;74;0.99865;3.3;0.64;10.4;6 +9.8;0.66;0.39;3.2;0.083;21;59;0.9989;3.37;0.71;11.5;7 +9.3;0.61;0.26;3.4;0.09;25;87;0.99975;3.24;0.62;9.7;5 +7.8;0.62;0.05;2.3;0.079;6;18;0.99735;3.29;0.63;9.3;5 +10.3;0.59;0.42;2.8;0.09;35;73;0.999;3.28;0.7;9.5;6 +10;0.49;0.2;11;0.071;13;50;1.0015;3.16;0.69;9.2;6 +10;0.49;0.2;11;0.071;13;50;1.0015;3.16;0.69;9.2;6 +11.6;0.53;0.66;3.65;0.121;6;14;0.9978;3.05;0.74;11.5;7 +10.3;0.44;0.5;4.5;0.107;5;13;0.998;3.28;0.83;11.5;5 +13.4;0.27;0.62;2.6;0.082;6;21;1.0002;3.16;0.67;9.7;6 +10.7;0.46;0.39;2;0.061;7;15;0.9981;3.18;0.62;9.5;5 +10.2;0.36;0.64;2.9;0.122;10;41;0.998;3.23;0.66;12.5;6 +10.2;0.36;0.64;2.9;0.122;10;41;0.998;3.23;0.66;12.5;6 +8;0.58;0.28;3.2;0.066;21;114;0.9973;3.22;0.54;9.4;6 +8.4;0.56;0.08;2.1;0.105;16;44;0.9958;3.13;0.52;11;5 +7.9;0.65;0.01;2.5;0.078;17;38;0.9963;3.34;0.74;11.7;7 +11.9;0.695;0.53;3.4;0.128;7;21;0.9992;3.17;0.84;12.2;7 +8.9;0.43;0.45;1.9;0.052;6;16;0.9948;3.35;0.7;12.5;6 +7.8;0.43;0.32;2.8;0.08;29;58;0.9974;3.31;0.64;10.3;5 +12.4;0.49;0.58;3;0.103;28;99;1.0008;3.16;1;11.5;6 +12.5;0.28;0.54;2.3;0.082;12;29;0.9997;3.11;1.36;9.8;7 +12.2;0.34;0.5;2.4;0.066;10;21;1;3.12;1.18;9.2;6 +10.6;0.42;0.48;2.7;0.065;5;18;0.9972;3.21;0.87;11.3;6 +10.9;0.39;0.47;1.8;0.118;6;14;0.9982;3.3;0.75;9.8;6 +10.9;0.39;0.47;1.8;0.118;6;14;0.9982;3.3;0.75;9.8;6 +11.9;0.57;0.5;2.6;0.082;6;32;1.0006;3.12;0.78;10.7;6 +7;0.685;0;1.9;0.067;40;63;0.9979;3.6;0.81;9.9;5 +6.6;0.815;0.02;2.7;0.072;17;34;0.9955;3.58;0.89;12.3;7 +13.8;0.49;0.67;3;0.093;6;15;0.9986;3.02;0.93;12;6 +9.6;0.56;0.31;2.8;0.089;15;46;0.9979;3.11;0.92;10;6 +9.1;0.785;0;2.6;0.093;11;28;0.9994;3.36;0.86;9.4;6 +10.7;0.67;0.22;2.7;0.107;17;34;1.0004;3.28;0.98;9.9;6 +9.1;0.795;0;2.6;0.096;11;26;0.9994;3.35;0.83;9.4;6 +7.7;0.665;0;2.4;0.09;8;19;0.9974;3.27;0.73;9.3;5 +13.5;0.53;0.79;4.8;0.12;23;77;1.0018;3.18;0.77;13;5 +6.1;0.21;0.4;1.4;0.066;40.5;165;0.9912;3.25;0.59;11.9;6 +6.7;0.75;0.01;2.4;0.078;17;32;0.9955;3.55;0.61;12.8;6 +11.5;0.41;0.52;3;0.08;29;55;1.0001;3.26;0.88;11;5 +10.5;0.42;0.66;2.95;0.116;12;29;0.997;3.24;0.75;11.7;7 +11.9;0.43;0.66;3.1;0.109;10;23;1;3.15;0.85;10.4;7 +12.6;0.38;0.66;2.6;0.088;10;41;1.001;3.17;0.68;9.8;6 +8.2;0.7;0.23;2;0.099;14;81;0.9973;3.19;0.7;9.4;5 +8.6;0.45;0.31;2.6;0.086;21;50;0.9982;3.37;0.91;9.9;6 +11.9;0.58;0.66;2.5;0.072;6;37;0.9992;3.05;0.56;10;5 +12.5;0.46;0.63;2;0.071;6;15;0.9988;2.99;0.87;10.2;5 +12.8;0.615;0.66;5.8;0.083;7;42;1.0022;3.07;0.73;10;7 +10;0.42;0.5;3.4;0.107;7;21;0.9979;3.26;0.93;11.8;6 +12.8;0.615;0.66;5.8;0.083;7;42;1.0022;3.07;0.73;10;7 +10.4;0.575;0.61;2.6;0.076;11;24;1;3.16;0.69;9;5 +10.3;0.34;0.52;2.8;0.159;15;75;0.9998;3.18;0.64;9.4;5 +9.4;0.27;0.53;2.4;0.074;6;18;0.9962;3.2;1.13;12;7 +6.9;0.765;0.02;2.3;0.063;35;63;0.9975;3.57;0.78;9.9;5 +7.9;0.24;0.4;1.6;0.056;11;25;0.9967;3.32;0.87;8.7;6 +9.1;0.28;0.48;1.8;0.067;26;46;0.9967;3.32;1.04;10.6;6 +7.4;0.55;0.22;2.2;0.106;12;72;0.9959;3.05;0.63;9.2;5 +14;0.41;0.63;3.8;0.089;6;47;1.0014;3.01;0.81;10.8;6 +11.5;0.54;0.71;4.4;0.124;6;15;0.9984;3.01;0.83;11.8;7 +11.5;0.45;0.5;3;0.078;19;47;1.0003;3.26;1.11;11;6 +9.4;0.27;0.53;2.4;0.074;6;18;0.9962;3.2;1.13;12;7 +11.4;0.625;0.66;6.2;0.088;6;24;0.9988;3.11;0.99;13.3;6 +8.3;0.42;0.38;2.5;0.094;24;60;0.9979;3.31;0.7;10.8;6 +8.3;0.26;0.42;2;0.08;11;27;0.9974;3.21;0.8;9.4;6 +13.7;0.415;0.68;2.9;0.085;17;43;1.0014;3.06;0.8;10;6 +8.3;0.26;0.42;2;0.08;11;27;0.9974;3.21;0.8;9.4;6 +8.3;0.26;0.42;2;0.08;11;27;0.9974;3.21;0.8;9.4;6 +7.7;0.51;0.28;2.1;0.087;23;54;0.998;3.42;0.74;9.2;5 +7.4;0.63;0.07;2.4;0.09;11;37;0.9979;3.43;0.76;9.7;6 +7.8;0.54;0.26;2;0.088;23;48;0.9981;3.41;0.74;9.2;6 +8.3;0.66;0.15;1.9;0.079;17;42;0.9972;3.31;0.54;9.6;6 +7.8;0.46;0.26;1.9;0.088;23;53;0.9981;3.43;0.74;9.2;6 +9.6;0.38;0.31;2.5;0.096;16;49;0.9982;3.19;0.7;10;7 +5.6;0.85;0.05;1.4;0.045;12;88;0.9924;3.56;0.82;12.9;8 +13.7;0.415;0.68;2.9;0.085;17;43;1.0014;3.06;0.8;10;6 +9.5;0.37;0.52;2;0.082;6;26;0.998;3.18;0.51;9.5;5 +8.4;0.665;0.61;2;0.112;13;95;0.997;3.16;0.54;9.1;5 +12.7;0.6;0.65;2.3;0.063;6;25;0.9997;3.03;0.57;9.9;5 +12;0.37;0.76;4.2;0.066;7;38;1.0004;3.22;0.6;13;7 +6.6;0.735;0.02;7.9;0.122;68;124;0.9994;3.47;0.53;9.9;5 +11.5;0.59;0.59;2.6;0.087;13;49;0.9988;3.18;0.65;11;6 +11.5;0.59;0.59;2.6;0.087;13;49;0.9988;3.18;0.65;11;6 +8.7;0.765;0.22;2.3;0.064;9;42;0.9963;3.1;0.55;9.4;5 +6.6;0.735;0.02;7.9;0.122;68;124;0.9994;3.47;0.53;9.9;5 +7.7;0.26;0.3;1.7;0.059;20;38;0.9949;3.29;0.47;10.8;6 +12.2;0.48;0.54;2.6;0.085;19;64;1;3.1;0.61;10.5;6 +11.4;0.6;0.49;2.7;0.085;10;41;0.9994;3.15;0.63;10.5;6 +7.7;0.69;0.05;2.7;0.075;15;27;0.9974;3.26;0.61;9.1;5 +8.7;0.31;0.46;1.4;0.059;11;25;0.9966;3.36;0.76;10.1;6 +9.8;0.44;0.47;2.5;0.063;9;28;0.9981;3.24;0.65;10.8;6 +12;0.39;0.66;3;0.093;12;30;0.9996;3.18;0.63;10.8;7 +10.4;0.34;0.58;3.7;0.174;6;16;0.997;3.19;0.7;11.3;6 +12.5;0.46;0.49;4.5;0.07;26;49;0.9981;3.05;0.57;9.6;4 +9;0.43;0.34;2.5;0.08;26;86;0.9987;3.38;0.62;9.5;6 +9.1;0.45;0.35;2.4;0.08;23;78;0.9987;3.38;0.62;9.5;5 +7.1;0.735;0.16;1.9;0.1;15;77;0.9966;3.27;0.64;9.3;5 +9.9;0.4;0.53;6.7;0.097;6;19;0.9986;3.27;0.82;11.7;7 +8.8;0.52;0.34;2.7;0.087;24;122;0.9982;3.26;0.61;9.5;5 +8.6;0.725;0.24;6.6;0.117;31;134;1.0014;3.32;1.07;9.3;5 +10.6;0.48;0.64;2.2;0.111;6;20;0.997;3.26;0.66;11.7;6 +7;0.58;0.12;1.9;0.091;34;124;0.9956;3.44;0.48;10.5;5 +11.9;0.38;0.51;2;0.121;7;20;0.9996;3.24;0.76;10.4;6 +6.8;0.77;0;1.8;0.066;34;52;0.9976;3.62;0.68;9.9;5 +9.5;0.56;0.33;2.4;0.089;35;67;0.9972;3.28;0.73;11.8;7 +6.6;0.84;0.03;2.3;0.059;32;48;0.9952;3.52;0.56;12.3;7 +7.7;0.96;0.2;2;0.047;15;60;0.9955;3.36;0.44;10.9;5 +10.5;0.24;0.47;2.1;0.066;6;24;0.9978;3.15;0.9;11;7 +7.7;0.96;0.2;2;0.047;15;60;0.9955;3.36;0.44;10.9;5 +6.6;0.84;0.03;2.3;0.059;32;48;0.9952;3.52;0.56;12.3;7 +6.4;0.67;0.08;2.1;0.045;19;48;0.9949;3.49;0.49;11.4;6 +9.5;0.78;0.22;1.9;0.077;6;32;0.9988;3.26;0.56;10.6;6 +9.1;0.52;0.33;1.3;0.07;9;30;0.9978;3.24;0.6;9.3;5 +12.8;0.84;0.63;2.4;0.088;13;35;0.9997;3.1;0.6;10.4;6 +10.5;0.24;0.47;2.1;0.066;6;24;0.9978;3.15;0.9;11;7 +7.8;0.55;0.35;2.2;0.074;21;66;0.9974;3.25;0.56;9.2;5 +11.9;0.37;0.69;2.3;0.078;12;24;0.9958;3;0.65;12.8;6 +12.3;0.39;0.63;2.3;0.091;6;18;1.0004;3.16;0.49;9.5;5 +10.4;0.41;0.55;3.2;0.076;22;54;0.9996;3.15;0.89;9.9;6 +12.3;0.39;0.63;2.3;0.091;6;18;1.0004;3.16;0.49;9.5;5 +8;0.67;0.3;2;0.06;38;62;0.9958;3.26;0.56;10.2;6 +11.1;0.45;0.73;3.2;0.066;6;22;0.9986;3.17;0.66;11.2;6 +10.4;0.41;0.55;3.2;0.076;22;54;0.9996;3.15;0.89;9.9;6 +7;0.62;0.18;1.5;0.062;7;50;0.9951;3.08;0.6;9.3;5 +12.6;0.31;0.72;2.2;0.072;6;29;0.9987;2.88;0.82;9.8;8 +11.9;0.4;0.65;2.15;0.068;7;27;0.9988;3.06;0.68;11.3;6 +15.6;0.685;0.76;3.7;0.1;6;43;1.0032;2.95;0.68;11.2;7 +10;0.44;0.49;2.7;0.077;11;19;0.9963;3.23;0.63;11.6;7 +5.3;0.57;0.01;1.7;0.054;5;27;0.9934;3.57;0.84;12.5;7 +9.5;0.735;0.1;2.1;0.079;6;31;0.9986;3.23;0.56;10.1;6 +12.5;0.38;0.6;2.6;0.081;31;72;0.9996;3.1;0.73;10.5;5 +9.3;0.48;0.29;2.1;0.127;6;16;0.9968;3.22;0.72;11.2;5 +8.6;0.53;0.22;2;0.1;7;27;0.9967;3.2;0.56;10.2;6 +11.9;0.39;0.69;2.8;0.095;17;35;0.9994;3.1;0.61;10.8;6 +11.9;0.39;0.69;2.8;0.095;17;35;0.9994;3.1;0.61;10.8;6 +8.4;0.37;0.53;1.8;0.413;9;26;0.9979;3.06;1.06;9.1;6 +6.8;0.56;0.03;1.7;0.084;18;35;0.9968;3.44;0.63;10;6 +10.4;0.33;0.63;2.8;0.084;5;22;0.9998;3.26;0.74;11.2;7 +7;0.23;0.4;1.6;0.063;21;67;0.9952;3.5;0.63;11.1;5 +11.3;0.62;0.67;5.2;0.086;6;19;0.9988;3.22;0.69;13.4;8 +8.9;0.59;0.39;2.3;0.095;5;22;0.9986;3.37;0.58;10.3;5 +9.2;0.63;0.21;2.7;0.097;29;65;0.9988;3.28;0.58;9.6;5 +10.4;0.33;0.63;2.8;0.084;5;22;0.9998;3.26;0.74;11.2;7 +11.6;0.58;0.66;2.2;0.074;10;47;1.0008;3.25;0.57;9;3 +9.2;0.43;0.52;2.3;0.083;14;23;0.9976;3.35;0.61;11.3;6 +8.3;0.615;0.22;2.6;0.087;6;19;0.9982;3.26;0.61;9.3;5 +11;0.26;0.68;2.55;0.085;10;25;0.997;3.18;0.61;11.8;5 +8.1;0.66;0.7;2.2;0.098;25;129;0.9972;3.08;0.53;9;5 +11.5;0.315;0.54;2.1;0.084;5;15;0.9987;2.98;0.7;9.2;6 +10;0.29;0.4;2.9;0.098;10;26;1.0006;3.48;0.91;9.7;5 +10.3;0.5;0.42;2;0.069;21;51;0.9982;3.16;0.72;11.5;6 +8.8;0.46;0.45;2.6;0.065;7;18;0.9947;3.32;0.79;14;6 +11.4;0.36;0.69;2.1;0.09;6;21;1;3.17;0.62;9.2;6 +8.7;0.82;0.02;1.2;0.07;36;48;0.9952;3.2;0.58;9.8;5 +13;0.32;0.65;2.6;0.093;15;47;0.9996;3.05;0.61;10.6;5 +9.6;0.54;0.42;2.4;0.081;25;52;0.997;3.2;0.71;11.4;6 +12.5;0.37;0.55;2.6;0.083;25;68;0.9995;3.15;0.82;10.4;6 +9.9;0.35;0.55;2.1;0.062;5;14;0.9971;3.26;0.79;10.6;5 +10.5;0.28;0.51;1.7;0.08;10;24;0.9982;3.2;0.89;9.4;6 +9.6;0.68;0.24;2.2;0.087;5;28;0.9988;3.14;0.6;10.2;5 +9.3;0.27;0.41;2;0.091;6;16;0.998;3.28;0.7;9.7;5 +10.4;0.24;0.49;1.8;0.075;6;20;0.9977;3.18;1.06;11;6 +9.6;0.68;0.24;2.2;0.087;5;28;0.9988;3.14;0.6;10.2;5 +9.4;0.685;0.11;2.7;0.077;6;31;0.9984;3.19;0.7;10.1;6 +10.6;0.28;0.39;15.5;0.069;6;23;1.0026;3.12;0.66;9.2;5 +9.4;0.3;0.56;2.8;0.08;6;17;0.9964;3.15;0.92;11.7;8 +10.6;0.36;0.59;2.2;0.152;6;18;0.9986;3.04;1.05;9.4;5 +10.6;0.36;0.6;2.2;0.152;7;18;0.9986;3.04;1.06;9.4;5 +10.6;0.44;0.68;4.1;0.114;6;24;0.997;3.06;0.66;13.4;6 +10.2;0.67;0.39;1.9;0.054;6;17;0.9976;3.17;0.47;10;5 +10.2;0.67;0.39;1.9;0.054;6;17;0.9976;3.17;0.47;10;5 +10.2;0.645;0.36;1.8;0.053;5;14;0.9982;3.17;0.42;10;6 +11.6;0.32;0.55;2.8;0.081;35;67;1.0002;3.32;0.92;10.8;7 +9.3;0.39;0.4;2.6;0.073;10;26;0.9984;3.34;0.75;10.2;6 +9.3;0.775;0.27;2.8;0.078;24;56;0.9984;3.31;0.67;10.6;6 +9.2;0.41;0.5;2.5;0.055;12;25;0.9952;3.34;0.79;13.3;7 +8.9;0.4;0.51;2.6;0.052;13;27;0.995;3.32;0.9;13.4;7 +8.7;0.69;0.31;3;0.086;23;81;1.0002;3.48;0.74;11.6;6 +6.5;0.39;0.23;8.3;0.051;28;91;0.9952;3.44;0.55;12.1;6 +10.7;0.35;0.53;2.6;0.07;5;16;0.9972;3.15;0.65;11;8 +7.8;0.52;0.25;1.9;0.081;14;38;0.9984;3.43;0.65;9;6 +7.2;0.34;0.32;2.5;0.09;43;113;0.9966;3.32;0.79;11.1;5 +10.7;0.35;0.53;2.6;0.07;5;16;0.9972;3.15;0.65;11;8 +8.7;0.69;0.31;3;0.086;23;81;1.0002;3.48;0.74;11.6;6 +7.8;0.52;0.25;1.9;0.081;14;38;0.9984;3.43;0.65;9;6 +10.4;0.44;0.73;6.55;0.074;38;76;0.999;3.17;0.85;12;7 +10.4;0.44;0.73;6.55;0.074;38;76;0.999;3.17;0.85;12;7 +10.5;0.26;0.47;1.9;0.078;6;24;0.9976;3.18;1.04;10.9;7 +10.5;0.24;0.42;1.8;0.077;6;22;0.9976;3.21;1.05;10.8;7 +10.2;0.49;0.63;2.9;0.072;10;26;0.9968;3.16;0.78;12.5;7 +10.4;0.24;0.46;1.8;0.075;6;21;0.9976;3.25;1.02;10.8;7 +11.2;0.67;0.55;2.3;0.084;6;13;1;3.17;0.71;9.5;6 +10;0.59;0.31;2.2;0.09;26;62;0.9994;3.18;0.63;10.2;6 +13.3;0.29;0.75;2.8;0.084;23;43;0.9986;3.04;0.68;11.4;7 +12.4;0.42;0.49;4.6;0.073;19;43;0.9978;3.02;0.61;9.5;5 +10;0.59;0.31;2.2;0.09;26;62;0.9994;3.18;0.63;10.2;6 +10.7;0.4;0.48;2.1;0.125;15;49;0.998;3.03;0.81;9.7;6 +10.5;0.51;0.64;2.4;0.107;6;15;0.9973;3.09;0.66;11.8;7 +10.5;0.51;0.64;2.4;0.107;6;15;0.9973;3.09;0.66;11.8;7 +8.5;0.655;0.49;6.1;0.122;34;151;1.001;3.31;1.14;9.3;5 +12.5;0.6;0.49;4.3;0.1;5;14;1.001;3.25;0.74;11.9;6 +10.4;0.61;0.49;2.1;0.2;5;16;0.9994;3.16;0.63;8.4;3 +10.9;0.21;0.49;2.8;0.088;11;32;0.9972;3.22;0.68;11.7;6 +7.3;0.365;0.49;2.5;0.088;39;106;0.9966;3.36;0.78;11;5 +9.8;0.25;0.49;2.7;0.088;15;33;0.9982;3.42;0.9;10;6 +7.6;0.41;0.49;2;0.088;16;43;0.998;3.48;0.64;9.1;5 +8.2;0.39;0.49;2.3;0.099;47;133;0.9979;3.38;0.99;9.8;5 +9.3;0.4;0.49;2.5;0.085;38;142;0.9978;3.22;0.55;9.4;5 +9.2;0.43;0.49;2.4;0.086;23;116;0.9976;3.23;0.64;9.5;5 +10.4;0.64;0.24;2.8;0.105;29;53;0.9998;3.24;0.67;9.9;5 +7.3;0.365;0.49;2.5;0.088;39;106;0.9966;3.36;0.78;11;5 +7;0.38;0.49;2.5;0.097;33;85;0.9962;3.39;0.77;11.4;6 +8.2;0.42;0.49;2.6;0.084;32;55;0.9988;3.34;0.75;8.7;6 +9.9;0.63;0.24;2.4;0.077;6;33;0.9974;3.09;0.57;9.4;5 +9.1;0.22;0.24;2.1;0.078;1;28;0.999;3.41;0.87;10.3;6 +11.9;0.38;0.49;2.7;0.098;12;42;1.0004;3.16;0.61;10.3;5 +11.9;0.38;0.49;2.7;0.098;12;42;1.0004;3.16;0.61;10.3;5 +10.3;0.27;0.24;2.1;0.072;15;33;0.9956;3.22;0.66;12.8;6 +10;0.48;0.24;2.7;0.102;13;32;1;3.28;0.56;10;6 +9.1;0.22;0.24;2.1;0.078;1;28;0.999;3.41;0.87;10.3;6 +9.9;0.63;0.24;2.4;0.077;6;33;0.9974;3.09;0.57;9.4;5 +8.1;0.825;0.24;2.1;0.084;5;13;0.9972;3.37;0.77;10.7;6 +12.9;0.35;0.49;5.8;0.066;5;35;1.0014;3.2;0.66;12;7 +11.2;0.5;0.74;5.15;0.1;5;17;0.9996;3.22;0.62;11.2;5 +9.2;0.59;0.24;3.3;0.101;20;47;0.9988;3.26;0.67;9.6;5 +9.5;0.46;0.49;6.3;0.064;5;17;0.9988;3.21;0.73;11;6 +9.3;0.715;0.24;2.1;0.07;5;20;0.9966;3.12;0.59;9.9;5 +11.2;0.66;0.24;2.5;0.085;16;53;0.9993;3.06;0.72;11;6 +14.3;0.31;0.74;1.8;0.075;6;15;1.0008;2.86;0.79;8.4;6 +9.1;0.47;0.49;2.6;0.094;38;106;0.9982;3.08;0.59;9.1;5 +7.5;0.55;0.24;2;0.078;10;28;0.9983;3.45;0.78;9.5;6 +10.6;0.31;0.49;2.5;0.067;6;21;0.9987;3.26;0.86;10.7;6 +12.4;0.35;0.49;2.6;0.079;27;69;0.9994;3.12;0.75;10.4;6 +9;0.53;0.49;1.9;0.171;6;25;0.9975;3.27;0.61;9.4;6 +6.8;0.51;0.01;2.1;0.074;9;25;0.9958;3.33;0.56;9.5;6 +9.4;0.43;0.24;2.8;0.092;14;45;0.998;3.19;0.73;10;6 +9.5;0.46;0.24;2.7;0.092;14;44;0.998;3.12;0.74;10;6 +5;1.04;0.24;1.6;0.05;32;96;0.9934;3.74;0.62;11.5;5 +15.5;0.645;0.49;4.2;0.095;10;23;1.00315;2.92;0.74;11.1;5 +15.5;0.645;0.49;4.2;0.095;10;23;1.00315;2.92;0.74;11.1;5 +10.9;0.53;0.49;4.6;0.118;10;17;1.0002;3.07;0.56;11.7;6 +15.6;0.645;0.49;4.2;0.095;10;23;1.00315;2.92;0.74;11.1;5 +10.9;0.53;0.49;4.6;0.118;10;17;1.0002;3.07;0.56;11.7;6 +13;0.47;0.49;4.3;0.085;6;47;1.0021;3.3;0.68;12.7;6 +12.7;0.6;0.49;2.8;0.075;5;19;0.9994;3.14;0.57;11.4;5 +9;0.44;0.49;2.4;0.078;26;121;0.9978;3.23;0.58;9.2;5 +9;0.54;0.49;2.9;0.094;41;110;0.9982;3.08;0.61;9.2;5 +7.6;0.29;0.49;2.7;0.092;25;60;0.9971;3.31;0.61;10.1;6 +13;0.47;0.49;4.3;0.085;6;47;1.0021;3.3;0.68;12.7;6 +12.7;0.6;0.49;2.8;0.075;5;19;0.9994;3.14;0.57;11.4;5 +8.7;0.7;0.24;2.5;0.226;5;15;0.9991;3.32;0.6;9;6 +8.7;0.7;0.24;2.5;0.226;5;15;0.9991;3.32;0.6;9;6 +9.8;0.5;0.49;2.6;0.25;5;20;0.999;3.31;0.79;10.7;6 +6.2;0.36;0.24;2.2;0.095;19;42;0.9946;3.57;0.57;11.7;6 +11.5;0.35;0.49;3.3;0.07;10;37;1.0003;3.32;0.91;11;6 +6.2;0.36;0.24;2.2;0.095;19;42;0.9946;3.57;0.57;11.7;6 +10.2;0.24;0.49;2.4;0.075;10;28;0.9978;3.14;0.61;10.4;5 +10.5;0.59;0.49;2.1;0.07;14;47;0.9991;3.3;0.56;9.6;4 +10.6;0.34;0.49;3.2;0.078;20;78;0.9992;3.19;0.7;10;6 +12.3;0.27;0.49;3.1;0.079;28;46;0.9993;3.2;0.8;10.2;6 +9.9;0.5;0.24;2.3;0.103;6;14;0.9978;3.34;0.52;10;4 +8.8;0.44;0.49;2.8;0.083;18;111;0.9982;3.3;0.6;9.5;5 +8.8;0.47;0.49;2.9;0.085;17;110;0.9982;3.29;0.6;9.8;5 +10.6;0.31;0.49;2.2;0.063;18;40;0.9976;3.14;0.51;9.8;6 +12.3;0.5;0.49;2.2;0.089;5;14;1.0002;3.19;0.44;9.6;5 +12.3;0.5;0.49;2.2;0.089;5;14;1.0002;3.19;0.44;9.6;5 +11.7;0.49;0.49;2.2;0.083;5;15;1;3.19;0.43;9.2;5 +12;0.28;0.49;1.9;0.074;10;21;0.9976;2.98;0.66;9.9;7 +11.8;0.33;0.49;3.4;0.093;54;80;1.0002;3.3;0.76;10.7;7 +7.6;0.51;0.24;2.4;0.091;8;38;0.998;3.47;0.66;9.6;6 +11.1;0.31;0.49;2.7;0.094;16;47;0.9986;3.12;1.02;10.6;7 +7.3;0.73;0.24;1.9;0.108;18;102;0.9967;3.26;0.59;9.3;5 +5;0.42;0.24;2;0.06;19;50;0.9917;3.72;0.74;14;8 +10.2;0.29;0.49;2.6;0.059;5;13;0.9976;3.05;0.74;10.5;7 +9;0.45;0.49;2.6;0.084;21;75;0.9987;3.35;0.57;9.7;5 +6.6;0.39;0.49;1.7;0.07;23;149;0.9922;3.12;0.5;11.5;6 +9;0.45;0.49;2.6;0.084;21;75;0.9987;3.35;0.57;9.7;5 +9.9;0.49;0.58;3.5;0.094;9;43;1.0004;3.29;0.58;9;5 +7.9;0.72;0.17;2.6;0.096;20;38;0.9978;3.4;0.53;9.5;5 +8.9;0.595;0.41;7.9;0.086;30;109;0.9998;3.27;0.57;9.3;5 +12.4;0.4;0.51;2;0.059;6;24;0.9994;3.04;0.6;9.3;6 +11.9;0.58;0.58;1.9;0.071;5;18;0.998;3.09;0.63;10;6 +8.5;0.585;0.18;2.1;0.078;5;30;0.9967;3.2;0.48;9.8;6 +12.7;0.59;0.45;2.3;0.082;11;22;1;3;0.7;9.3;6 +8.2;0.915;0.27;2.1;0.088;7;23;0.9962;3.26;0.47;10;4 +13.2;0.46;0.52;2.2;0.071;12;35;1.0006;3.1;0.56;9;6 +7.7;0.835;0;2.6;0.081;6;14;0.9975;3.3;0.52;9.3;5 +13.2;0.46;0.52;2.2;0.071;12;35;1.0006;3.1;0.56;9;6 +8.3;0.58;0.13;2.9;0.096;14;63;0.9984;3.17;0.62;9.1;6 +8.3;0.6;0.13;2.6;0.085;6;24;0.9984;3.31;0.59;9.2;6 +9.4;0.41;0.48;4.6;0.072;10;20;0.9973;3.34;0.79;12.2;7 +8.8;0.48;0.41;3.3;0.092;26;52;0.9982;3.31;0.53;10.5;6 +10.1;0.65;0.37;5.1;0.11;11;65;1.0026;3.32;0.64;10.4;6 +6.3;0.36;0.19;3.2;0.075;15;39;0.9956;3.56;0.52;12.7;6 +8.8;0.24;0.54;2.5;0.083;25;57;0.9983;3.39;0.54;9.2;5 +13.2;0.38;0.55;2.7;0.081;5;16;1.0006;2.98;0.54;9.4;5 +7.5;0.64;0;2.4;0.077;18;29;0.9965;3.32;0.6;10;6 +8.2;0.39;0.38;1.5;0.058;10;29;0.9962;3.26;0.74;9.8;5 +9.2;0.755;0.18;2.2;0.148;10;103;0.9969;2.87;1.36;10.2;6 +9.6;0.6;0.5;2.3;0.079;28;71;0.9997;3.5;0.57;9.7;5 +9.6;0.6;0.5;2.3;0.079;28;71;0.9997;3.5;0.57;9.7;5 +11.5;0.31;0.51;2.2;0.079;14;28;0.9982;3.03;0.93;9.8;6 +11.4;0.46;0.5;2.7;0.122;4;17;1.0006;3.13;0.7;10.2;5 +11.3;0.37;0.41;2.3;0.088;6;16;0.9988;3.09;0.8;9.3;5 +8.3;0.54;0.24;3.4;0.076;16;112;0.9976;3.27;0.61;9.4;5 +8.2;0.56;0.23;3.4;0.078;14;104;0.9976;3.28;0.62;9.4;5 +10;0.58;0.22;1.9;0.08;9;32;0.9974;3.13;0.55;9.5;5 +7.9;0.51;0.25;2.9;0.077;21;45;0.9974;3.49;0.96;12.1;6 +6.8;0.69;0;5.6;0.124;21;58;0.9997;3.46;0.72;10.2;5 +6.8;0.69;0;5.6;0.124;21;58;0.9997;3.46;0.72;10.2;5 +8.8;0.6;0.29;2.2;0.098;5;15;0.9988;3.36;0.49;9.1;5 +8.8;0.6;0.29;2.2;0.098;5;15;0.9988;3.36;0.49;9.1;5 +8.7;0.54;0.26;2.5;0.097;7;31;0.9976;3.27;0.6;9.3;6 +7.6;0.685;0.23;2.3;0.111;20;84;0.9964;3.21;0.61;9.3;5 +8.7;0.54;0.26;2.5;0.097;7;31;0.9976;3.27;0.6;9.3;6 +10.4;0.28;0.54;2.7;0.105;5;19;0.9988;3.25;0.63;9.5;5 +7.6;0.41;0.14;3;0.087;21;43;0.9964;3.32;0.57;10.5;6 +10.1;0.935;0.22;3.4;0.105;11;86;1.001;3.43;0.64;11.3;4 +7.9;0.35;0.21;1.9;0.073;46;102;0.9964;3.27;0.58;9.5;5 +8.7;0.84;0;1.4;0.065;24;33;0.9954;3.27;0.55;9.7;5 +9.6;0.88;0.28;2.4;0.086;30;147;0.9979;3.24;0.53;9.4;5 +9.5;0.885;0.27;2.3;0.084;31;145;0.9978;3.24;0.53;9.4;5 +7.7;0.915;0.12;2.2;0.143;7;23;0.9964;3.35;0.65;10.2;7 +8.9;0.29;0.35;1.9;0.067;25;57;0.997;3.18;1.36;10.3;6 +9.9;0.54;0.45;2.3;0.071;16;40;0.9991;3.39;0.62;9.4;5 +9.5;0.59;0.44;2.3;0.071;21;68;0.9992;3.46;0.63;9.5;5 +9.9;0.54;0.45;2.3;0.071;16;40;0.9991;3.39;0.62;9.4;5 +9.5;0.59;0.44;2.3;0.071;21;68;0.9992;3.46;0.63;9.5;5 +9.9;0.54;0.45;2.3;0.071;16;40;0.9991;3.39;0.62;9.4;5 +7.8;0.64;0.1;6;0.115;5;11;0.9984;3.37;0.69;10.1;7 +7.3;0.67;0.05;3.6;0.107;6;20;0.9972;3.4;0.63;10.1;5 +8.3;0.845;0.01;2.2;0.07;5;14;0.9967;3.32;0.58;11;4 +8.7;0.48;0.3;2.8;0.066;10;28;0.9964;3.33;0.67;11.2;7 +6.7;0.42;0.27;8.6;0.068;24;148;0.9948;3.16;0.57;11.3;6 +10.7;0.43;0.39;2.2;0.106;8;32;0.9986;2.89;0.5;9.6;5 +9.8;0.88;0.25;2.5;0.104;35;155;1.001;3.41;0.67;11.2;5 +15.9;0.36;0.65;7.5;0.096;22;71;0.9976;2.98;0.84;14.9;5 +9.4;0.33;0.59;2.8;0.079;9;30;0.9976;3.12;0.54;12;6 +8.6;0.47;0.47;2.4;0.074;7;29;0.9979;3.08;0.46;9.5;5 +9.7;0.55;0.17;2.9;0.087;20;53;1.0004;3.14;0.61;9.4;5 +10.7;0.43;0.39;2.2;0.106;8;32;0.9986;2.89;0.5;9.6;5 +12;0.5;0.59;1.4;0.073;23;42;0.998;2.92;0.68;10.5;7 +7.2;0.52;0.07;1.4;0.074;5;20;0.9973;3.32;0.81;9.6;6 +7.1;0.84;0.02;4.4;0.096;5;13;0.997;3.41;0.57;11;4 +7.2;0.52;0.07;1.4;0.074;5;20;0.9973;3.32;0.81;9.6;6 +7.5;0.42;0.31;1.6;0.08;15;42;0.9978;3.31;0.64;9;5 +7.2;0.57;0.06;1.6;0.076;9;27;0.9972;3.36;0.7;9.6;6 +10.1;0.28;0.46;1.8;0.05;5;13;0.9974;3.04;0.79;10.2;6 +12.1;0.4;0.52;2;0.092;15;54;1;3.03;0.66;10.2;5 +9.4;0.59;0.14;2;0.084;25;48;0.9981;3.14;0.56;9.7;5 +8.3;0.49;0.36;1.8;0.222;6;16;0.998;3.18;0.6;9.5;6 +11.3;0.34;0.45;2;0.082;6;15;0.9988;2.94;0.66;9.2;6 +10;0.73;0.43;2.3;0.059;15;31;0.9966;3.15;0.57;11;5 +11.3;0.34;0.45;2;0.082;6;15;0.9988;2.94;0.66;9.2;6 +6.9;0.4;0.24;2.5;0.083;30;45;0.9959;3.26;0.58;10;5 +8.2;0.73;0.21;1.7;0.074;5;13;0.9968;3.2;0.52;9.5;5 +9.8;1.24;0.34;2;0.079;32;151;0.998;3.15;0.53;9.5;5 +8.2;0.73;0.21;1.7;0.074;5;13;0.9968;3.2;0.52;9.5;5 +10.8;0.4;0.41;2.2;0.084;7;17;0.9984;3.08;0.67;9.3;6 +9.3;0.41;0.39;2.2;0.064;12;31;0.9984;3.26;0.65;10.2;5 +10.8;0.4;0.41;2.2;0.084;7;17;0.9984;3.08;0.67;9.3;6 +8.6;0.8;0.11;2.3;0.084;12;31;0.9979;3.4;0.48;9.9;5 +8.3;0.78;0.1;2.6;0.081;45;87;0.9983;3.48;0.53;10;5 +10.8;0.26;0.45;3.3;0.06;20;49;0.9972;3.13;0.54;9.6;5 +13.3;0.43;0.58;1.9;0.07;15;40;1.0004;3.06;0.49;9;5 +8;0.45;0.23;2.2;0.094;16;29;0.9962;3.21;0.49;10.2;6 +8.5;0.46;0.31;2.25;0.078;32;58;0.998;3.33;0.54;9.8;5 +8.1;0.78;0.23;2.6;0.059;5;15;0.997;3.37;0.56;11.3;5 +9.8;0.98;0.32;2.3;0.078;35;152;0.998;3.25;0.48;9.4;5 +8.1;0.78;0.23;2.6;0.059;5;15;0.997;3.37;0.56;11.3;5 +7.1;0.65;0.18;1.8;0.07;13;40;0.997;3.44;0.6;9.1;5 +9.1;0.64;0.23;3.1;0.095;13;38;0.9998;3.28;0.59;9.7;5 +7.7;0.66;0.04;1.6;0.039;4;9;0.9962;3.4;0.47;9.4;5 +8.1;0.38;0.48;1.8;0.157;5;17;0.9976;3.3;1.05;9.4;5 +7.4;1.185;0;4.25;0.097;5;14;0.9966;3.63;0.54;10.7;3 +9.2;0.92;0.24;2.6;0.087;12;93;0.9998;3.48;0.54;9.8;5 +8.6;0.49;0.51;2;0.422;16;62;0.9979;3.03;1.17;9;5 +9;0.48;0.32;2.8;0.084;21;122;0.9984;3.32;0.62;9.4;5 +9;0.47;0.31;2.7;0.084;24;125;0.9984;3.31;0.61;9.4;5 +5.1;0.47;0.02;1.3;0.034;18;44;0.9921;3.9;0.62;12.8;6 +7;0.65;0.02;2.1;0.066;8;25;0.9972;3.47;0.67;9.5;6 +7;0.65;0.02;2.1;0.066;8;25;0.9972;3.47;0.67;9.5;6 +9.4;0.615;0.28;3.2;0.087;18;72;1.0001;3.31;0.53;9.7;5 +11.8;0.38;0.55;2.1;0.071;5;19;0.9986;3.11;0.62;10.8;6 +10.6;1.02;0.43;2.9;0.076;26;88;0.9984;3.08;0.57;10.1;6 +7;0.65;0.02;2.1;0.066;8;25;0.9972;3.47;0.67;9.5;6 +7;0.64;0.02;2.1;0.067;9;23;0.997;3.47;0.67;9.4;6 +7.5;0.38;0.48;2.6;0.073;22;84;0.9972;3.32;0.7;9.6;4 +9.1;0.765;0.04;1.6;0.078;4;14;0.998;3.29;0.54;9.7;4 +8.4;1.035;0.15;6;0.073;11;54;0.999;3.37;0.49;9.9;5 +7;0.78;0.08;2;0.093;10;19;0.9956;3.4;0.47;10;5 +7.4;0.49;0.19;3;0.077;16;37;0.9966;3.37;0.51;10.5;5 +7.8;0.545;0.12;2.5;0.068;11;35;0.996;3.34;0.61;11.6;6 +9.7;0.31;0.47;1.6;0.062;13;33;0.9983;3.27;0.66;10;6 +10.6;1.025;0.43;2.8;0.08;21;84;0.9985;3.06;0.57;10.1;5 +8.9;0.565;0.34;3;0.093;16;112;0.9998;3.38;0.61;9.5;5 +8.7;0.69;0;3.2;0.084;13;33;0.9992;3.36;0.45;9.4;5 +8;0.43;0.36;2.3;0.075;10;48;0.9976;3.34;0.46;9.4;5 +9.9;0.74;0.28;2.6;0.078;21;77;0.998;3.28;0.51;9.8;5 +7.2;0.49;0.18;2.7;0.069;13;34;0.9967;3.29;0.48;9.2;6 +8;0.43;0.36;2.3;0.075;10;48;0.9976;3.34;0.46;9.4;5 +7.6;0.46;0.11;2.6;0.079;12;49;0.9968;3.21;0.57;10;5 +8.4;0.56;0.04;2;0.082;10;22;0.9976;3.22;0.44;9.6;5 +7.1;0.66;0;3.9;0.086;17;45;0.9976;3.46;0.54;9.5;5 +8.4;0.56;0.04;2;0.082;10;22;0.9976;3.22;0.44;9.6;5 +8.9;0.48;0.24;2.85;0.094;35;106;0.9982;3.1;0.53;9.2;5 +7.6;0.42;0.08;2.7;0.084;15;48;0.9968;3.21;0.59;10;5 +7.1;0.31;0.3;2.2;0.053;36;127;0.9965;2.94;1.62;9.5;5 +7.5;1.115;0.1;3.1;0.086;5;12;0.9958;3.54;0.6;11.2;4 +9;0.66;0.17;3;0.077;5;13;0.9976;3.29;0.55;10.4;5 +8.1;0.72;0.09;2.8;0.084;18;49;0.9994;3.43;0.72;11.1;6 +6.4;0.57;0.02;1.8;0.067;4;11;0.997;3.46;0.68;9.5;5 +6.4;0.57;0.02;1.8;0.067;4;11;0.997;3.46;0.68;9.5;5 +6.4;0.865;0.03;3.2;0.071;27;58;0.995;3.61;0.49;12.7;6 +9.5;0.55;0.66;2.3;0.387;12;37;0.9982;3.17;0.67;9.6;5 +8.9;0.875;0.13;3.45;0.088;4;14;0.9994;3.44;0.52;11.5;5 +7.3;0.835;0.03;2.1;0.092;10;19;0.9966;3.39;0.47;9.6;5 +7;0.45;0.34;2.7;0.082;16;72;0.998;3.55;0.6;9.5;5 +7.7;0.56;0.2;2;0.075;9;39;0.9987;3.48;0.62;9.3;5 +7.7;0.965;0.1;2.1;0.112;11;22;0.9963;3.26;0.5;9.5;5 +7.7;0.965;0.1;2.1;0.112;11;22;0.9963;3.26;0.5;9.5;5 +8.2;0.59;0;2.5;0.093;19;58;1.0002;3.5;0.65;9.3;6 +9;0.46;0.23;2.8;0.092;28;104;0.9983;3.1;0.56;9.2;5 +9;0.69;0;2.4;0.088;19;38;0.999;3.35;0.6;9.3;5 +8.3;0.76;0.29;4.2;0.075;12;16;0.9965;3.45;0.68;11.5;6 +9.2;0.53;0.24;2.6;0.078;28;139;0.99788;3.21;0.57;9.5;5 +6.5;0.615;0;1.9;0.065;9;18;0.9972;3.46;0.65;9.2;5 +11.6;0.41;0.58;2.8;0.096;25;101;1.00024;3.13;0.53;10;5 +11.1;0.39;0.54;2.7;0.095;21;101;1.0001;3.13;0.51;9.5;5 +7.3;0.51;0.18;2.1;0.07;12;28;0.99768;3.52;0.73;9.5;6 +8.2;0.34;0.38;2.5;0.08;12;57;0.9978;3.3;0.47;9;6 +8.6;0.33;0.4;2.6;0.083;16;68;0.99782;3.3;0.48;9.4;5 +7.2;0.5;0.18;2.1;0.071;12;31;0.99761;3.52;0.72;9.6;6 +7.3;0.51;0.18;2.1;0.07;12;28;0.99768;3.52;0.73;9.5;6 +8.3;0.65;0.1;2.9;0.089;17;40;0.99803;3.29;0.55;9.5;5 +8.3;0.65;0.1;2.9;0.089;17;40;0.99803;3.29;0.55;9.5;5 +7.6;0.54;0.13;2.5;0.097;24;66;0.99785;3.39;0.61;9.4;5 +8.3;0.65;0.1;2.9;0.089;17;40;0.99803;3.29;0.55;9.5;5 +7.8;0.48;0.68;1.7;0.415;14;32;0.99656;3.09;1.06;9.1;6 +7.8;0.91;0.07;1.9;0.058;22;47;0.99525;3.51;0.43;10.7;6 +6.3;0.98;0.01;2;0.057;15;33;0.99488;3.6;0.46;11.2;6 +8.1;0.87;0;2.2;0.084;10;31;0.99656;3.25;0.5;9.8;5 +8.1;0.87;0;2.2;0.084;10;31;0.99656;3.25;0.5;9.8;5 +8.8;0.42;0.21;2.5;0.092;33;88;0.99823;3.19;0.52;9.2;5 +9;0.58;0.25;2.8;0.075;9;104;0.99779;3.23;0.57;9.7;5 +9.3;0.655;0.26;2;0.096;5;35;0.99738;3.25;0.42;9.6;5 +8.8;0.7;0;1.7;0.069;8;19;0.99701;3.31;0.53;10;6 +9.3;0.655;0.26;2;0.096;5;35;0.99738;3.25;0.42;9.6;5 +9.1;0.68;0.11;2.8;0.093;11;44;0.99888;3.31;0.55;9.5;6 +9.2;0.67;0.1;3;0.091;12;48;0.99888;3.31;0.54;9.5;6 +8.8;0.59;0.18;2.9;0.089;12;74;0.99738;3.14;0.54;9.4;5 +7.5;0.6;0.32;2.7;0.103;13;98;0.99938;3.45;0.62;9.5;5 +7.1;0.59;0.02;2.3;0.082;24;94;0.99744;3.55;0.53;9.7;6 +7.9;0.72;0.01;1.9;0.076;7;32;0.99668;3.39;0.54;9.6;5 +7.1;0.59;0.02;2.3;0.082;24;94;0.99744;3.55;0.53;9.7;6 +9.4;0.685;0.26;2.4;0.082;23;143;0.9978;3.28;0.55;9.4;5 +9.5;0.57;0.27;2.3;0.082;23;144;0.99782;3.27;0.55;9.4;5 +7.9;0.4;0.29;1.8;0.157;1;44;0.9973;3.3;0.92;9.5;6 +7.9;0.4;0.3;1.8;0.157;2;45;0.99727;3.31;0.91;9.5;6 +7.2;1;0;3;0.102;7;16;0.99586;3.43;0.46;10;5 +6.9;0.765;0.18;2.4;0.243;5.5;48;0.99612;3.4;0.6;10.3;6 +6.9;0.635;0.17;2.4;0.241;6;18;0.9961;3.4;0.59;10.3;6 +8.3;0.43;0.3;3.4;0.079;7;34;0.99788;3.36;0.61;10.5;5 +7.1;0.52;0.03;2.6;0.076;21;92;0.99745;3.5;0.6;9.8;5 +7;0.57;0;2;0.19;12;45;0.99676;3.31;0.6;9.4;6 +6.5;0.46;0.14;2.4;0.114;9;37;0.99732;3.66;0.65;9.8;5 +9;0.82;0.05;2.4;0.081;26;96;0.99814;3.36;0.53;10;5 +6.5;0.46;0.14;2.4;0.114;9;37;0.99732;3.66;0.65;9.8;5 +7.1;0.59;0.01;2.5;0.077;20;85;0.99746;3.55;0.59;9.8;5 +9.9;0.35;0.41;2.3;0.083;11;61;0.9982;3.21;0.5;9.5;5 +9.9;0.35;0.41;2.3;0.083;11;61;0.9982;3.21;0.5;9.5;5 +10;0.56;0.24;2.2;0.079;19;58;0.9991;3.18;0.56;10.1;6 +10;0.56;0.24;2.2;0.079;19;58;0.9991;3.18;0.56;10.1;6 +8.6;0.63;0.17;2.9;0.099;21;119;0.998;3.09;0.52;9.3;5 +7.4;0.37;0.43;2.6;0.082;18;82;0.99708;3.33;0.68;9.7;6 +8.8;0.64;0.17;2.9;0.084;25;130;0.99818;3.23;0.54;9.6;5 +7.1;0.61;0.02;2.5;0.081;17;87;0.99745;3.48;0.6;9.7;6 +7.7;0.6;0;2.6;0.055;7;13;0.99639;3.38;0.56;10.8;5 +10.1;0.27;0.54;2.3;0.065;7;26;0.99531;3.17;0.53;12.5;6 +10.8;0.89;0.3;2.6;0.132;7;60;0.99786;2.99;1.18;10.2;5 +8.7;0.46;0.31;2.5;0.126;24;64;0.99746;3.1;0.74;9.6;5 +9.3;0.37;0.44;1.6;0.038;21;42;0.99526;3.24;0.81;10.8;7 +9.4;0.5;0.34;3.6;0.082;5;14;0.9987;3.29;0.52;10.7;6 +9.4;0.5;0.34;3.6;0.082;5;14;0.9987;3.29;0.52;10.7;6 +7.2;0.61;0.08;4;0.082;26;108;0.99641;3.25;0.51;9.4;5 +8.6;0.55;0.09;3.3;0.068;8;17;0.99735;3.23;0.44;10;5 +5.1;0.585;0;1.7;0.044;14;86;0.99264;3.56;0.94;12.9;7 +7.7;0.56;0.08;2.5;0.114;14;46;0.9971;3.24;0.66;9.6;6 +8.4;0.52;0.22;2.7;0.084;4;18;0.99682;3.26;0.57;9.9;6 +8.2;0.28;0.4;2.4;0.052;4;10;0.99356;3.33;0.7;12.8;7 +8.4;0.25;0.39;2;0.041;4;10;0.99386;3.27;0.71;12.5;7 +8.2;0.28;0.4;2.4;0.052;4;10;0.99356;3.33;0.7;12.8;7 +7.4;0.53;0.12;1.9;0.165;4;12;0.99702;3.26;0.86;9.2;5 +7.6;0.48;0.31;2.8;0.07;4;15;0.99693;3.22;0.55;10.3;6 +7.3;0.49;0.1;2.6;0.068;4;14;0.99562;3.3;0.47;10.5;5 +12.9;0.5;0.55;2.8;0.072;7;24;1.00012;3.09;0.68;10.9;6 +10.8;0.45;0.33;2.5;0.099;20;38;0.99818;3.24;0.71;10.8;5 +6.9;0.39;0.24;2.1;0.102;4;7;0.99462;3.44;0.58;11.4;4 +12.6;0.41;0.54;2.8;0.103;19;41;0.99939;3.21;0.76;11.3;6 +10.8;0.45;0.33;2.5;0.099;20;38;0.99818;3.24;0.71;10.8;5 +9.8;0.51;0.19;3.2;0.081;8;30;0.9984;3.23;0.58;10.5;6 +10.8;0.29;0.42;1.6;0.084;19;27;0.99545;3.28;0.73;11.9;6 +7.1;0.715;0;2.35;0.071;21;47;0.99632;3.29;0.45;9.4;5 +9.1;0.66;0.15;3.2;0.097;9;59;0.99976;3.28;0.54;9.6;5 +7;0.685;0;1.9;0.099;9;22;0.99606;3.34;0.6;9.7;5 +4.9;0.42;0;2.1;0.048;16;42;0.99154;3.71;0.74;14;7 +6.7;0.54;0.13;2;0.076;15;36;0.9973;3.61;0.64;9.8;5 +6.7;0.54;0.13;2;0.076;15;36;0.9973;3.61;0.64;9.8;5 +7.1;0.48;0.28;2.8;0.068;6;16;0.99682;3.24;0.53;10.3;5 +7.1;0.46;0.14;2.8;0.076;15;37;0.99624;3.36;0.49;10.7;5 +7.5;0.27;0.34;2.3;0.05;4;8;0.9951;3.4;0.64;11;7 +7.1;0.46;0.14;2.8;0.076;15;37;0.99624;3.36;0.49;10.7;5 +7.8;0.57;0.09;2.3;0.065;34;45;0.99417;3.46;0.74;12.7;8 +5.9;0.61;0.08;2.1;0.071;16;24;0.99376;3.56;0.77;11.1;6 +7.5;0.685;0.07;2.5;0.058;5;9;0.99632;3.38;0.55;10.9;4 +5.9;0.61;0.08;2.1;0.071;16;24;0.99376;3.56;0.77;11.1;6 +10.4;0.44;0.42;1.5;0.145;34;48;0.99832;3.38;0.86;9.9;3 +11.6;0.47;0.44;1.6;0.147;36;51;0.99836;3.38;0.86;9.9;4 +8.8;0.685;0.26;1.6;0.088;16;23;0.99694;3.32;0.47;9.4;5 +7.6;0.665;0.1;1.5;0.066;27;55;0.99655;3.39;0.51;9.3;5 +6.7;0.28;0.28;2.4;0.012;36;100;0.99064;3.26;0.39;11.7;7 +6.7;0.28;0.28;2.4;0.012;36;100;0.99064;3.26;0.39;11.7;7 +10.1;0.31;0.35;1.6;0.075;9;28;0.99672;3.24;0.83;11.2;7 +6;0.5;0.04;2.2;0.092;13;26;0.99647;3.46;0.47;10;5 +11.1;0.42;0.47;2.65;0.085;9;34;0.99736;3.24;0.77;12.1;7 +6.6;0.66;0;3;0.115;21;31;0.99629;3.45;0.63;10.3;5 +10.6;0.5;0.45;2.6;0.119;34;68;0.99708;3.23;0.72;10.9;6 +7.1;0.685;0.35;2;0.088;9;92;0.9963;3.28;0.62;9.4;5 +9.9;0.25;0.46;1.7;0.062;26;42;0.9959;3.18;0.83;10.6;6 +6.4;0.64;0.21;1.8;0.081;14;31;0.99689;3.59;0.66;9.8;5 +6.4;0.64;0.21;1.8;0.081;14;31;0.99689;3.59;0.66;9.8;5 +7.4;0.68;0.16;1.8;0.078;12;39;0.9977;3.5;0.7;9.9;6 +6.4;0.64;0.21;1.8;0.081;14;31;0.99689;3.59;0.66;9.8;5 +6.4;0.63;0.21;1.6;0.08;12;32;0.99689;3.58;0.66;9.8;5 +9.3;0.43;0.44;1.9;0.085;9;22;0.99708;3.28;0.55;9.5;5 +9.3;0.43;0.44;1.9;0.085;9;22;0.99708;3.28;0.55;9.5;5 +8;0.42;0.32;2.5;0.08;26;122;0.99801;3.22;1.07;9.7;5 +9.3;0.36;0.39;1.5;0.08;41;55;0.99652;3.47;0.73;10.9;6 +9.3;0.36;0.39;1.5;0.08;41;55;0.99652;3.47;0.73;10.9;6 +7.6;0.735;0.02;2.5;0.071;10;14;0.99538;3.51;0.71;11.7;7 +9.3;0.36;0.39;1.5;0.08;41;55;0.99652;3.47;0.73;10.9;6 +8.2;0.26;0.34;2.5;0.073;16;47;0.99594;3.4;0.78;11.3;7 +11.7;0.28;0.47;1.7;0.054;17;32;0.99686;3.15;0.67;10.6;7 +6.8;0.56;0.22;1.8;0.074;15;24;0.99438;3.4;0.82;11.2;6 +7.2;0.62;0.06;2.7;0.077;15;85;0.99746;3.51;0.54;9.5;5 +5.8;1.01;0.66;2;0.039;15;88;0.99357;3.66;0.6;11.5;6 +7.5;0.42;0.32;2.7;0.067;7;25;0.99628;3.24;0.44;10.4;5 +7.2;0.62;0.06;2.5;0.078;17;84;0.99746;3.51;0.53;9.7;5 +7.2;0.62;0.06;2.7;0.077;15;85;0.99746;3.51;0.54;9.5;5 +7.2;0.635;0.07;2.6;0.077;16;86;0.99748;3.51;0.54;9.7;5 +6.8;0.49;0.22;2.3;0.071;13;24;0.99438;3.41;0.83;11.3;6 +6.9;0.51;0.23;2;0.072;13;22;0.99438;3.4;0.84;11.2;6 +6.8;0.56;0.22;1.8;0.074;15;24;0.99438;3.4;0.82;11.2;6 +7.6;0.63;0.03;2;0.08;27;43;0.99578;3.44;0.64;10.9;6 +7.7;0.715;0.01;2.1;0.064;31;43;0.99371;3.41;0.57;11.8;6 +6.9;0.56;0.03;1.5;0.086;36;46;0.99522;3.53;0.57;10.6;5 +7.3;0.35;0.24;2;0.067;28;48;0.99576;3.43;0.54;10;4 +9.1;0.21;0.37;1.6;0.067;6;10;0.99552;3.23;0.58;11.1;7 +10.4;0.38;0.46;2.1;0.104;6;10;0.99664;3.12;0.65;11.8;7 +8.8;0.31;0.4;2.8;0.109;7;16;0.99614;3.31;0.79;11.8;7 +7.1;0.47;0;2.2;0.067;7;14;0.99517;3.4;0.58;10.9;4 +7.7;0.715;0.01;2.1;0.064;31;43;0.99371;3.41;0.57;11.8;6 +8.8;0.61;0.19;4;0.094;30;69;0.99787;3.22;0.5;10;6 +7.2;0.6;0.04;2.5;0.076;18;88;0.99745;3.53;0.55;9.5;5 +9.2;0.56;0.18;1.6;0.078;10;21;0.99576;3.15;0.49;9.9;5 +7.6;0.715;0;2.1;0.068;30;35;0.99533;3.48;0.65;11.4;6 +8.4;0.31;0.29;3.1;0.194;14;26;0.99536;3.22;0.78;12;6 +7.2;0.6;0.04;2.5;0.076;18;88;0.99745;3.53;0.55;9.5;5 +8.8;0.61;0.19;4;0.094;30;69;0.99787;3.22;0.5;10;6 +8.9;0.75;0.14;2.5;0.086;9;30;0.99824;3.34;0.64;10.5;5 +9;0.8;0.12;2.4;0.083;8;28;0.99836;3.33;0.65;10.4;6 +10.7;0.52;0.38;2.6;0.066;29;56;0.99577;3.15;0.79;12.1;7 +6.8;0.57;0;2.5;0.072;32;64;0.99491;3.43;0.56;11.2;6 +10.7;0.9;0.34;6.6;0.112;23;99;1.00289;3.22;0.68;9.3;5 +7.2;0.34;0.24;2;0.071;30;52;0.99576;3.44;0.58;10.1;5 +7.2;0.66;0.03;2.3;0.078;16;86;0.99743;3.53;0.57;9.7;5 +10.1;0.45;0.23;1.9;0.082;10;18;0.99774;3.22;0.65;9.3;6 +7.2;0.66;0.03;2.3;0.078;16;86;0.99743;3.53;0.57;9.7;5 +7.2;0.63;0.03;2.2;0.08;17;88;0.99745;3.53;0.58;9.8;6 +7.1;0.59;0.01;2.3;0.08;27;43;0.9955;3.42;0.58;10.7;6 +8.3;0.31;0.39;2.4;0.078;17;43;0.99444;3.31;0.77;12.5;7 +7.1;0.59;0.01;2.3;0.08;27;43;0.9955;3.42;0.58;10.7;6 +8.3;0.31;0.39;2.4;0.078;17;43;0.99444;3.31;0.77;12.5;7 +8.3;1.02;0.02;3.4;0.084;6;11;0.99892;3.48;0.49;11;3 +8.9;0.31;0.36;2.6;0.056;10;39;0.99562;3.4;0.69;11.8;5 +7.4;0.635;0.1;2.4;0.08;16;33;0.99736;3.58;0.69;10.8;7 +7.4;0.635;0.1;2.4;0.08;16;33;0.99736;3.58;0.69;10.8;7 +6.8;0.59;0.06;6;0.06;11;18;0.9962;3.41;0.59;10.8;7 +6.8;0.59;0.06;6;0.06;11;18;0.9962;3.41;0.59;10.8;7 +9.2;0.58;0.2;3;0.081;15;115;0.998;3.23;0.59;9.5;5 +7.2;0.54;0.27;2.6;0.084;12;78;0.9964;3.39;0.71;11;5 +6.1;0.56;0;2.2;0.079;6;9;0.9948;3.59;0.54;11.5;6 +7.4;0.52;0.13;2.4;0.078;34;61;0.99528;3.43;0.59;10.8;6 +7.3;0.305;0.39;1.2;0.059;7;11;0.99331;3.29;0.52;11.5;6 +9.3;0.38;0.48;3.8;0.132;3;11;0.99577;3.23;0.57;13.2;6 +9.1;0.28;0.46;9;0.114;3;9;0.99901;3.18;0.6;10.9;6 +10;0.46;0.44;2.9;0.065;4;8;0.99674;3.33;0.62;12.2;6 +9.4;0.395;0.46;4.6;0.094;3;10;0.99639;3.27;0.64;12.2;7 +7.3;0.305;0.39;1.2;0.059;7;11;0.99331;3.29;0.52;11.5;6 +8.6;0.315;0.4;2.2;0.079;3;6;0.99512;3.27;0.67;11.9;6 +5.3;0.715;0.19;1.5;0.161;7;62;0.99395;3.62;0.61;11;5 +6.8;0.41;0.31;8.8;0.084;26;45;0.99824;3.38;0.64;10.1;6 +8.4;0.36;0.32;2.2;0.081;32;79;0.9964;3.3;0.72;11;6 +8.4;0.62;0.12;1.8;0.072;38;46;0.99504;3.38;0.89;11.8;6 +9.6;0.41;0.37;2.3;0.091;10;23;0.99786;3.24;0.56;10.5;5 +8.4;0.36;0.32;2.2;0.081;32;79;0.9964;3.3;0.72;11;6 +8.4;0.62;0.12;1.8;0.072;38;46;0.99504;3.38;0.89;11.8;6 +6.8;0.41;0.31;8.8;0.084;26;45;0.99824;3.38;0.64;10.1;6 +8.6;0.47;0.27;2.3;0.055;14;28;0.99516;3.18;0.8;11.2;5 +8.6;0.22;0.36;1.9;0.064;53;77;0.99604;3.47;0.87;11;7 +9.4;0.24;0.33;2.3;0.061;52;73;0.99786;3.47;0.9;10.2;6 +8.4;0.67;0.19;2.2;0.093;11;75;0.99736;3.2;0.59;9.2;4 +8.6;0.47;0.27;2.3;0.055;14;28;0.99516;3.18;0.8;11.2;5 +8.7;0.33;0.38;3.3;0.063;10;19;0.99468;3.3;0.73;12;7 +6.6;0.61;0.01;1.9;0.08;8;25;0.99746;3.69;0.73;10.5;5 +7.4;0.61;0.01;2;0.074;13;38;0.99748;3.48;0.65;9.8;5 +7.6;0.4;0.29;1.9;0.078;29;66;0.9971;3.45;0.59;9.5;6 +7.4;0.61;0.01;2;0.074;13;38;0.99748;3.48;0.65;9.8;5 +6.6;0.61;0.01;1.9;0.08;8;25;0.99746;3.69;0.73;10.5;5 +8.8;0.3;0.38;2.3;0.06;19;72;0.99543;3.39;0.72;11.8;6 +8.8;0.3;0.38;2.3;0.06;19;72;0.99543;3.39;0.72;11.8;6 +12;0.63;0.5;1.4;0.071;6;26;0.99791;3.07;0.6;10.4;4 +7.2;0.38;0.38;2.8;0.068;23;42;0.99356;3.34;0.72;12.9;7 +6.2;0.46;0.17;1.6;0.073;7;11;0.99425;3.61;0.54;11.4;5 +9.6;0.33;0.52;2.2;0.074;13;25;0.99509;3.36;0.76;12.4;7 +9.9;0.27;0.49;5;0.082;9;17;0.99484;3.19;0.52;12.5;7 +10.1;0.43;0.4;2.6;0.092;13;52;0.99834;3.22;0.64;10;7 +9.8;0.5;0.34;2.3;0.094;10;45;0.99864;3.24;0.6;9.7;7 +8.3;0.3;0.49;3.8;0.09;11;24;0.99498;3.27;0.64;12.1;7 +10.2;0.44;0.42;2;0.071;7;20;0.99566;3.14;0.79;11.1;7 +10.2;0.44;0.58;4.1;0.092;11;24;0.99745;3.29;0.99;12;7 +8.3;0.28;0.48;2.1;0.093;6;12;0.99408;3.26;0.62;12.4;7 +8.9;0.12;0.45;1.8;0.075;10;21;0.99552;3.41;0.76;11.9;7 +8.9;0.12;0.45;1.8;0.075;10;21;0.99552;3.41;0.76;11.9;7 +8.9;0.12;0.45;1.8;0.075;10;21;0.99552;3.41;0.76;11.9;7 +8.3;0.28;0.48;2.1;0.093;6;12;0.99408;3.26;0.62;12.4;7 +8.2;0.31;0.4;2.2;0.058;6;10;0.99536;3.31;0.68;11.2;7 +10.2;0.34;0.48;2.1;0.052;5;9;0.99458;3.2;0.69;12.1;7 +7.6;0.43;0.4;2.7;0.082;6;11;0.99538;3.44;0.54;12.2;6 +8.5;0.21;0.52;1.9;0.09;9;23;0.99648;3.36;0.67;10.4;5 +9;0.36;0.52;2.1;0.111;5;10;0.99568;3.31;0.62;11.3;6 +9.5;0.37;0.52;2;0.088;12;51;0.99613;3.29;0.58;11.1;6 +6.4;0.57;0.12;2.3;0.12;25;36;0.99519;3.47;0.71;11.3;7 +8;0.59;0.05;2;0.089;12;32;0.99735;3.36;0.61;10;5 +8.5;0.47;0.27;1.9;0.058;18;38;0.99518;3.16;0.85;11.1;6 +7.1;0.56;0.14;1.6;0.078;7;18;0.99592;3.27;0.62;9.3;5 +6.6;0.57;0.02;2.1;0.115;6;16;0.99654;3.38;0.69;9.5;5 +8.8;0.27;0.39;2;0.1;20;27;0.99546;3.15;0.69;11.2;6 +8.5;0.47;0.27;1.9;0.058;18;38;0.99518;3.16;0.85;11.1;6 +8.3;0.34;0.4;2.4;0.065;24;48;0.99554;3.34;0.86;11;6 +9;0.38;0.41;2.4;0.103;6;10;0.99604;3.13;0.58;11.9;7 +8.5;0.66;0.2;2.1;0.097;23;113;0.99733;3.13;0.48;9.2;5 +9;0.4;0.43;2.4;0.068;29;46;0.9943;3.2;0.6;12.2;6 +6.7;0.56;0.09;2.9;0.079;7;22;0.99669;3.46;0.61;10.2;5 +10.4;0.26;0.48;1.9;0.066;6;10;0.99724;3.33;0.87;10.9;6 +10.4;0.26;0.48;1.9;0.066;6;10;0.99724;3.33;0.87;10.9;6 +10.1;0.38;0.5;2.4;0.104;6;13;0.99643;3.22;0.65;11.6;7 +8.5;0.34;0.44;1.7;0.079;6;12;0.99605;3.52;0.63;10.7;5 +8.8;0.33;0.41;5.9;0.073;7;13;0.99658;3.3;0.62;12.1;7 +7.2;0.41;0.3;2.1;0.083;35;72;0.997;3.44;0.52;9.4;5 +7.2;0.41;0.3;2.1;0.083;35;72;0.997;3.44;0.52;9.4;5 +8.4;0.59;0.29;2.6;0.109;31;119;0.99801;3.15;0.5;9.1;5 +7;0.4;0.32;3.6;0.061;9;29;0.99416;3.28;0.49;11.3;7 +12.2;0.45;0.49;1.4;0.075;3;6;0.9969;3.13;0.63;10.4;5 +9.1;0.5;0.3;1.9;0.065;8;17;0.99774;3.32;0.71;10.5;6 +9.5;0.86;0.26;1.9;0.079;13;28;0.99712;3.25;0.62;10;5 +7.3;0.52;0.32;2.1;0.07;51;70;0.99418;3.34;0.82;12.9;6 +9.1;0.5;0.3;1.9;0.065;8;17;0.99774;3.32;0.71;10.5;6 +12.2;0.45;0.49;1.4;0.075;3;6;0.9969;3.13;0.63;10.4;5 +7.4;0.58;0;2;0.064;7;11;0.99562;3.45;0.58;11.3;6 +9.8;0.34;0.39;1.4;0.066;3;7;0.9947;3.19;0.55;11.4;7 +7.1;0.36;0.3;1.6;0.08;35;70;0.99693;3.44;0.5;9.4;5 +7.7;0.39;0.12;1.7;0.097;19;27;0.99596;3.16;0.49;9.4;5 +9.7;0.295;0.4;1.5;0.073;14;21;0.99556;3.14;0.51;10.9;6 +7.7;0.39;0.12;1.7;0.097;19;27;0.99596;3.16;0.49;9.4;5 +7.1;0.34;0.28;2;0.082;31;68;0.99694;3.45;0.48;9.4;5 +6.5;0.4;0.1;2;0.076;30;47;0.99554;3.36;0.48;9.4;6 +7.1;0.34;0.28;2;0.082;31;68;0.99694;3.45;0.48;9.4;5 +10;0.35;0.45;2.5;0.092;20;88;0.99918;3.15;0.43;9.4;5 +7.7;0.6;0.06;2;0.079;19;41;0.99697;3.39;0.62;10.1;6 +5.6;0.66;0;2.2;0.087;3;11;0.99378;3.71;0.63;12.8;7 +5.6;0.66;0;2.2;0.087;3;11;0.99378;3.71;0.63;12.8;7 +8.9;0.84;0.34;1.4;0.05;4;10;0.99554;3.12;0.48;9.1;6 +6.4;0.69;0;1.65;0.055;7;12;0.99162;3.47;0.53;12.9;6 +7.5;0.43;0.3;2.2;0.062;6;12;0.99495;3.44;0.72;11.5;7 +9.9;0.35;0.38;1.5;0.058;31;47;0.99676;3.26;0.82;10.6;7 +9.1;0.29;0.33;2.05;0.063;13;27;0.99516;3.26;0.84;11.7;7 +6.8;0.36;0.32;1.8;0.067;4;8;0.9928;3.36;0.55;12.8;7 +8.2;0.43;0.29;1.6;0.081;27;45;0.99603;3.25;0.54;10.3;5 +6.8;0.36;0.32;1.8;0.067;4;8;0.9928;3.36;0.55;12.8;7 +9.1;0.29;0.33;2.05;0.063;13;27;0.99516;3.26;0.84;11.7;7 +9.1;0.3;0.34;2;0.064;12;25;0.99516;3.26;0.84;11.7;7 +8.9;0.35;0.4;3.6;0.11;12;24;0.99549;3.23;0.7;12;7 +9.6;0.5;0.36;2.8;0.116;26;55;0.99722;3.18;0.68;10.9;5 +8.9;0.28;0.45;1.7;0.067;7;12;0.99354;3.25;0.55;12.3;7 +8.9;0.32;0.31;2;0.088;12;19;0.9957;3.17;0.55;10.4;6 +7.7;1.005;0.15;2.1;0.102;11;32;0.99604;3.23;0.48;10;5 +7.5;0.71;0;1.6;0.092;22;31;0.99635;3.38;0.58;10;6 +8;0.58;0.16;2;0.12;3;7;0.99454;3.22;0.58;11.2;6 +10.5;0.39;0.46;2.2;0.075;14;27;0.99598;3.06;0.84;11.4;6 +8.9;0.38;0.4;2.2;0.068;12;28;0.99486;3.27;0.75;12.6;7 +8;0.18;0.37;0.9;0.049;36;109;0.99007;2.89;0.44;12.7;6 +8;0.18;0.37;0.9;0.049;36;109;0.99007;2.89;0.44;12.7;6 +7;0.5;0.14;1.8;0.078;10;23;0.99636;3.53;0.61;10.4;5 +11.3;0.36;0.66;2.4;0.123;3;8;0.99642;3.2;0.53;11.9;6 +11.3;0.36;0.66;2.4;0.123;3;8;0.99642;3.2;0.53;11.9;6 +7;0.51;0.09;2.1;0.062;4;9;0.99584;3.35;0.54;10.5;5 +8.2;0.32;0.42;2.3;0.098;3;9;0.99506;3.27;0.55;12.3;6 +7.7;0.58;0.01;1.8;0.088;12;18;0.99568;3.32;0.56;10.5;7 +8.6;0.83;0;2.8;0.095;17;43;0.99822;3.33;0.6;10.4;6 +7.9;0.31;0.32;1.9;0.066;14;36;0.99364;3.41;0.56;12.6;6 +6.4;0.795;0;2.2;0.065;28;52;0.99378;3.49;0.52;11.6;5 +7.2;0.34;0.21;2.5;0.075;41;68;0.99586;3.37;0.54;10.1;6 +7.7;0.58;0.01;1.8;0.088;12;18;0.99568;3.32;0.56;10.5;7 +7.1;0.59;0;2.1;0.091;9;14;0.99488;3.42;0.55;11.5;7 +7.3;0.55;0.01;1.8;0.093;9;15;0.99514;3.35;0.58;11;7 +8.1;0.82;0;4.1;0.095;5;14;0.99854;3.36;0.53;9.6;5 +7.5;0.57;0.08;2.6;0.089;14;27;0.99592;3.3;0.59;10.4;6 +8.9;0.745;0.18;2.5;0.077;15;48;0.99739;3.2;0.47;9.7;6 +10.1;0.37;0.34;2.4;0.085;5;17;0.99683;3.17;0.65;10.6;7 +7.6;0.31;0.34;2.5;0.082;26;35;0.99356;3.22;0.59;12.5;7 +7.3;0.91;0.1;1.8;0.074;20;56;0.99672;3.35;0.56;9.2;5 +8.7;0.41;0.41;6.2;0.078;25;42;0.9953;3.24;0.77;12.6;7 +8.9;0.5;0.21;2.2;0.088;21;39;0.99692;3.33;0.83;11.1;6 +7.4;0.965;0;2.2;0.088;16;32;0.99756;3.58;0.67;10.2;5 +6.9;0.49;0.19;1.7;0.079;13;26;0.99547;3.38;0.64;9.8;6 +8.9;0.5;0.21;2.2;0.088;21;39;0.99692;3.33;0.83;11.1;6 +9.5;0.39;0.41;8.9;0.069;18;39;0.99859;3.29;0.81;10.9;7 +6.4;0.39;0.33;3.3;0.046;12;53;0.99294;3.36;0.62;12.2;6 +6.9;0.44;0;1.4;0.07;32;38;0.99438;3.32;0.58;11.4;6 +7.6;0.78;0;1.7;0.076;33;45;0.99612;3.31;0.62;10.7;6 +7.1;0.43;0.17;1.8;0.082;27;51;0.99634;3.49;0.64;10.4;5 +9.3;0.49;0.36;1.7;0.081;3;14;0.99702;3.27;0.78;10.9;6 +9.3;0.5;0.36;1.8;0.084;6;17;0.99704;3.27;0.77;10.8;6 +7.1;0.43;0.17;1.8;0.082;27;51;0.99634;3.49;0.64;10.4;5 +8.5;0.46;0.59;1.4;0.414;16;45;0.99702;3.03;1.34;9.2;5 +5.6;0.605;0.05;2.4;0.073;19;25;0.99258;3.56;0.55;12.9;5 +8.3;0.33;0.42;2.3;0.07;9;20;0.99426;3.38;0.77;12.7;7 +8.2;0.64;0.27;2;0.095;5;77;0.99747;3.13;0.62;9.1;6 +8.2;0.64;0.27;2;0.095;5;77;0.99747;3.13;0.62;9.1;6 +8.9;0.48;0.53;4;0.101;3;10;0.99586;3.21;0.59;12.1;7 +7.6;0.42;0.25;3.9;0.104;28;90;0.99784;3.15;0.57;9.1;5 +9.9;0.53;0.57;2.4;0.093;30;52;0.9971;3.19;0.76;11.6;7 +8.9;0.48;0.53;4;0.101;3;10;0.99586;3.21;0.59;12.1;7 +11.6;0.23;0.57;1.8;0.074;3;8;0.9981;3.14;0.7;9.9;6 +9.1;0.4;0.5;1.8;0.071;7;16;0.99462;3.21;0.69;12.5;8 +8;0.38;0.44;1.9;0.098;6;15;0.9956;3.3;0.64;11.4;6 +10.2;0.29;0.65;2.4;0.075;6;17;0.99565;3.22;0.63;11.8;6 +8.2;0.74;0.09;2;0.067;5;10;0.99418;3.28;0.57;11.8;6 +7.7;0.61;0.18;2.4;0.083;6;20;0.9963;3.29;0.6;10.2;6 +6.6;0.52;0.08;2.4;0.07;13;26;0.99358;3.4;0.72;12.5;7 +11.1;0.31;0.53;2.2;0.06;3;10;0.99572;3.02;0.83;10.9;7 +11.1;0.31;0.53;2.2;0.06;3;10;0.99572;3.02;0.83;10.9;7 +8;0.62;0.35;2.8;0.086;28;52;0.997;3.31;0.62;10.8;5 +9.3;0.33;0.45;1.5;0.057;19;37;0.99498;3.18;0.89;11.1;7 +7.5;0.77;0.2;8.1;0.098;30;92;0.99892;3.2;0.58;9.2;5 +7.2;0.35;0.26;1.8;0.083;33;75;0.9968;3.4;0.58;9.5;6 +8;0.62;0.33;2.7;0.088;16;37;0.9972;3.31;0.58;10.7;6 +7.5;0.77;0.2;8.1;0.098;30;92;0.99892;3.2;0.58;9.2;5 +9.1;0.25;0.34;2;0.071;45;67;0.99769;3.44;0.86;10.2;7 +9.9;0.32;0.56;2;0.073;3;8;0.99534;3.15;0.73;11.4;6 +8.6;0.37;0.65;6.4;0.08;3;8;0.99817;3.27;0.58;11;5 +8.6;0.37;0.65;6.4;0.08;3;8;0.99817;3.27;0.58;11;5 +7.9;0.3;0.68;8.3;0.05;37.5;278;0.99316;3.01;0.51;12.3;7 +10.3;0.27;0.56;1.4;0.047;3;8;0.99471;3.16;0.51;11.8;6 +7.9;0.3;0.68;8.3;0.05;37.5;289;0.99316;3.01;0.51;12.3;7 +7.2;0.38;0.3;1.8;0.073;31;70;0.99685;3.42;0.59;9.5;6 +8.7;0.42;0.45;2.4;0.072;32;59;0.99617;3.33;0.77;12;6 +7.2;0.38;0.3;1.8;0.073;31;70;0.99685;3.42;0.59;9.5;6 +6.8;0.48;0.08;1.8;0.074;40;64;0.99529;3.12;0.49;9.6;5 +8.5;0.34;0.4;4.7;0.055;3;9;0.99738;3.38;0.66;11.6;7 +7.9;0.19;0.42;1.6;0.057;18;30;0.994;3.29;0.69;11.2;6 +11.6;0.41;0.54;1.5;0.095;22;41;0.99735;3.02;0.76;9.9;7 +11.6;0.41;0.54;1.5;0.095;22;41;0.99735;3.02;0.76;9.9;7 +10;0.26;0.54;1.9;0.083;42;74;0.99451;2.98;0.63;11.8;8 +7.9;0.34;0.42;2;0.086;8;19;0.99546;3.35;0.6;11.4;6 +7;0.54;0.09;2;0.081;10;16;0.99479;3.43;0.59;11.5;6 +9.2;0.31;0.36;2.2;0.079;11;31;0.99615;3.33;0.86;12;7 +6.6;0.725;0.09;5.5;0.117;9;17;0.99655;3.35;0.49;10.8;6 +9.4;0.4;0.47;2.5;0.087;6;20;0.99772;3.15;0.5;10.5;5 +6.6;0.725;0.09;5.5;0.117;9;17;0.99655;3.35;0.49;10.8;6 +8.6;0.52;0.38;1.5;0.096;5;18;0.99666;3.2;0.52;9.4;5 +8;0.31;0.45;2.1;0.216;5;16;0.99358;3.15;0.81;12.5;7 +8.6;0.52;0.38;1.5;0.096;5;18;0.99666;3.2;0.52;9.4;5 +8.4;0.34;0.42;2.1;0.072;23;36;0.99392;3.11;0.78;12.4;6 +7.4;0.49;0.27;2.1;0.071;14;25;0.99388;3.35;0.63;12;6 +6.1;0.48;0.09;1.7;0.078;18;30;0.99402;3.45;0.54;11.2;6 +7.4;0.49;0.27;2.1;0.071;14;25;0.99388;3.35;0.63;12;6 +8;0.48;0.34;2.2;0.073;16;25;0.9936;3.28;0.66;12.4;6 +6.3;0.57;0.28;2.1;0.048;13;49;0.99374;3.41;0.6;12.8;5 +8.2;0.23;0.42;1.9;0.069;9;17;0.99376;3.21;0.54;12.3;6 +9.1;0.3;0.41;2;0.068;10;24;0.99523;3.27;0.85;11.7;7 +8.1;0.78;0.1;3.3;0.09;4;13;0.99855;3.36;0.49;9.5;5 +10.8;0.47;0.43;2.1;0.171;27;66;0.9982;3.17;0.76;10.8;6 +8.3;0.53;0;1.4;0.07;6;14;0.99593;3.25;0.64;10;6 +5.4;0.42;0.27;2;0.092;23;55;0.99471;3.78;0.64;12.3;7 +7.9;0.33;0.41;1.5;0.056;6;35;0.99396;3.29;0.71;11;6 +8.9;0.24;0.39;1.6;0.074;3;10;0.99698;3.12;0.59;9.5;6 +5;0.4;0.5;4.3;0.046;29;80;0.9902;3.49;0.66;13.6;6 +7;0.69;0.07;2.5;0.091;15;21;0.99572;3.38;0.6;11.3;6 +7;0.69;0.07;2.5;0.091;15;21;0.99572;3.38;0.6;11.3;6 +7;0.69;0.07;2.5;0.091;15;21;0.99572;3.38;0.6;11.3;6 +7.1;0.39;0.12;2.1;0.065;14;24;0.99252;3.3;0.53;13.3;6 +5.6;0.66;0;2.5;0.066;7;15;0.99256;3.52;0.58;12.9;5 +7.9;0.54;0.34;2.5;0.076;8;17;0.99235;3.2;0.72;13.1;8 +6.6;0.5;0;1.8;0.062;21;28;0.99352;3.44;0.55;12.3;6 +6.3;0.47;0;1.4;0.055;27;33;0.9922;3.45;0.48;12.3;6 +10.7;0.4;0.37;1.9;0.081;17;29;0.99674;3.12;0.65;11.2;6 +6.5;0.58;0;2.2;0.096;3;13;0.99557;3.62;0.62;11.5;4 +8.8;0.24;0.35;1.7;0.055;13;27;0.99394;3.14;0.59;11.3;7 +5.8;0.29;0.26;1.7;0.063;3;11;0.9915;3.39;0.54;13.5;6 +6.3;0.76;0;2.9;0.072;26;52;0.99379;3.51;0.6;11.5;6 +10;0.43;0.33;2.7;0.095;28;89;0.9984;3.22;0.68;10;5 +10.5;0.43;0.35;3.3;0.092;24;70;0.99798;3.21;0.69;10.5;6 +9.1;0.6;0;1.9;0.058;5;10;0.9977;3.18;0.63;10.4;6 +5.9;0.19;0.21;1.7;0.045;57;135;0.99341;3.32;0.44;9.5;5 +7.4;0.36;0.34;1.8;0.075;18;38;0.9933;3.38;0.88;13.6;7 +7.2;0.48;0.07;5.5;0.089;10;18;0.99684;3.37;0.68;11.2;7 +8.5;0.28;0.35;1.7;0.061;6;15;0.99524;3.3;0.74;11.8;7 +8;0.25;0.43;1.7;0.067;22;50;0.9946;3.38;0.6;11.9;6 +10.4;0.52;0.45;2;0.08;6;13;0.99774;3.22;0.76;11.4;6 +10.4;0.52;0.45;2;0.08;6;13;0.99774;3.22;0.76;11.4;6 +7.5;0.41;0.15;3.7;0.104;29;94;0.99786;3.14;0.58;9.1;5 +8.2;0.51;0.24;2;0.079;16;86;0.99764;3.34;0.64;9.5;6 +7.3;0.4;0.3;1.7;0.08;33;79;0.9969;3.41;0.65;9.5;6 +8.2;0.38;0.32;2.5;0.08;24;71;0.99624;3.27;0.85;11;6 +6.9;0.45;0.11;2.4;0.043;6;12;0.99354;3.3;0.65;11.4;6 +7;0.22;0.3;1.8;0.065;16;20;0.99672;3.61;0.82;10;6 +7.3;0.32;0.23;2.3;0.066;35;70;0.99588;3.43;0.62;10.1;5 +8.2;0.2;0.43;2.5;0.076;31;51;0.99672;3.53;0.81;10.4;6 +7.8;0.5;0.12;1.8;0.178;6;21;0.996;3.28;0.87;9.8;6 +10;0.41;0.45;6.2;0.071;6;14;0.99702;3.21;0.49;11.8;7 +7.8;0.39;0.42;2;0.086;9;21;0.99526;3.39;0.66;11.6;6 +10;0.35;0.47;2;0.061;6;11;0.99585;3.23;0.52;12;6 +8.2;0.33;0.32;2.8;0.067;4;12;0.99473;3.3;0.76;12.8;7 +6.1;0.58;0.23;2.5;0.044;16;70;0.99352;3.46;0.65;12.5;6 +8.3;0.6;0.25;2.2;0.118;9;38;0.99616;3.15;0.53;9.8;5 +9.6;0.42;0.35;2.1;0.083;17;38;0.99622;3.23;0.66;11.1;6 +6.6;0.58;0;2.2;0.1;50;63;0.99544;3.59;0.68;11.4;6 +8.3;0.6;0.25;2.2;0.118;9;38;0.99616;3.15;0.53;9.8;5 +8.5;0.18;0.51;1.75;0.071;45;88;0.99524;3.33;0.76;11.8;7 +5.1;0.51;0.18;2.1;0.042;16;101;0.9924;3.46;0.87;12.9;7 +6.7;0.41;0.43;2.8;0.076;22;54;0.99572;3.42;1.16;10.6;6 +10.2;0.41;0.43;2.2;0.11;11;37;0.99728;3.16;0.67;10.8;5 +10.6;0.36;0.57;2.3;0.087;6;20;0.99676;3.14;0.72;11.1;7 +8.8;0.45;0.43;1.4;0.076;12;21;0.99551;3.21;0.75;10.2;6 +8.5;0.32;0.42;2.3;0.075;12;19;0.99434;3.14;0.71;11.8;7 +9;0.785;0.24;1.7;0.078;10;21;0.99692;3.29;0.67;10;5 +9;0.785;0.24;1.7;0.078;10;21;0.99692;3.29;0.67;10;5 +8.5;0.44;0.5;1.9;0.369;15;38;0.99634;3.01;1.1;9.4;5 +9.9;0.54;0.26;2;0.111;7;60;0.99709;2.94;0.98;10.2;5 +8.2;0.33;0.39;2.5;0.074;29;48;0.99528;3.32;0.88;12.4;7 +6.5;0.34;0.27;2.8;0.067;8;44;0.99384;3.21;0.56;12;6 +7.6;0.5;0.29;2.3;0.086;5;14;0.99502;3.32;0.62;11.5;6 +9.2;0.36;0.34;1.6;0.062;5;12;0.99667;3.2;0.67;10.5;6 +7.1;0.59;0;2.2;0.078;26;44;0.99522;3.42;0.68;10.8;6 +9.7;0.42;0.46;2.1;0.074;5;16;0.99649;3.27;0.74;12.3;6 +7.6;0.36;0.31;1.7;0.079;26;65;0.99716;3.46;0.62;9.5;6 +7.6;0.36;0.31;1.7;0.079;26;65;0.99716;3.46;0.62;9.5;6 +6.5;0.61;0;2.2;0.095;48;59;0.99541;3.61;0.7;11.5;6 +6.5;0.88;0.03;5.6;0.079;23;47;0.99572;3.58;0.5;11.2;4 +7.1;0.66;0;2.4;0.052;6;11;0.99318;3.35;0.66;12.7;7 +5.6;0.915;0;2.1;0.041;17;78;0.99346;3.68;0.73;11.4;5 +8.2;0.35;0.33;2.4;0.076;11;47;0.99599;3.27;0.81;11;6 +8.2;0.35;0.33;2.4;0.076;11;47;0.99599;3.27;0.81;11;6 +9.8;0.39;0.43;1.65;0.068;5;11;0.99478;3.19;0.46;11.4;5 +10.2;0.4;0.4;2.5;0.068;41;54;0.99754;3.38;0.86;10.5;6 +6.8;0.66;0.07;1.6;0.07;16;61;0.99572;3.29;0.6;9.3;5 +6.7;0.64;0.23;2.1;0.08;11;119;0.99538;3.36;0.7;10.9;5 +7;0.43;0.3;2;0.085;6;39;0.99346;3.33;0.46;11.9;6 +6.6;0.8;0.03;7.8;0.079;6;12;0.9963;3.52;0.5;12.2;5 +7;0.43;0.3;2;0.085;6;39;0.99346;3.33;0.46;11.9;6 +6.7;0.64;0.23;2.1;0.08;11;119;0.99538;3.36;0.7;10.9;5 +8.8;0.955;0.05;1.8;0.075;5;19;0.99616;3.3;0.44;9.6;4 +9.1;0.4;0.57;4.6;0.08;6;20;0.99652;3.28;0.57;12.5;6 +6.5;0.885;0;2.3;0.166;6;12;0.99551;3.56;0.51;10.8;5 +7.2;0.25;0.37;2.5;0.063;11;41;0.99439;3.52;0.8;12.4;7 +6.4;0.885;0;2.3;0.166;6;12;0.99551;3.56;0.51;10.8;5 +7;0.745;0.12;1.8;0.114;15;64;0.99588;3.22;0.59;9.5;6 +6.2;0.43;0.22;1.8;0.078;21;56;0.99633;3.52;0.6;9.5;6 +7.9;0.58;0.23;2.3;0.076;23;94;0.99686;3.21;0.58;9.5;6 +7.7;0.57;0.21;1.5;0.069;4;9;0.99458;3.16;0.54;9.8;6 +7.7;0.26;0.26;2;0.052;19;77;0.9951;3.15;0.79;10.9;6 +7.9;0.58;0.23;2.3;0.076;23;94;0.99686;3.21;0.58;9.5;6 +7.7;0.57;0.21;1.5;0.069;4;9;0.99458;3.16;0.54;9.8;6 +7.9;0.34;0.36;1.9;0.065;5;10;0.99419;3.27;0.54;11.2;7 +8.6;0.42;0.39;1.8;0.068;6;12;0.99516;3.35;0.69;11.7;8 +9.9;0.74;0.19;5.8;0.111;33;76;0.99878;3.14;0.55;9.4;5 +7.2;0.36;0.46;2.1;0.074;24;44;0.99534;3.4;0.85;11;7 +7.2;0.36;0.46;2.1;0.074;24;44;0.99534;3.4;0.85;11;7 +7.2;0.36;0.46;2.1;0.074;24;44;0.99534;3.4;0.85;11;7 +9.9;0.72;0.55;1.7;0.136;24;52;0.99752;3.35;0.94;10;5 +7.2;0.36;0.46;2.1;0.074;24;44;0.99534;3.4;0.85;11;7 +6.2;0.39;0.43;2;0.071;14;24;0.99428;3.45;0.87;11.2;7 +6.8;0.65;0.02;2.1;0.078;8;15;0.99498;3.35;0.62;10.4;6 +6.6;0.44;0.15;2.1;0.076;22;53;0.9957;3.32;0.62;9.3;5 +6.8;0.65;0.02;2.1;0.078;8;15;0.99498;3.35;0.62;10.4;6 +9.6;0.38;0.42;1.9;0.071;5;13;0.99659;3.15;0.75;10.5;6 +10.2;0.33;0.46;1.9;0.081;6;9;0.99628;3.1;0.48;10.4;6 +8.8;0.27;0.46;2.1;0.095;20;29;0.99488;3.26;0.56;11.3;6 +7.9;0.57;0.31;2;0.079;10;79;0.99677;3.29;0.69;9.5;6 +8.2;0.34;0.37;1.9;0.057;43;74;0.99408;3.23;0.81;12;6 +8.2;0.4;0.31;1.9;0.082;8;24;0.996;3.24;0.69;10.6;6 +9;0.39;0.4;1.3;0.044;25;50;0.99478;3.2;0.83;10.9;6 +10.9;0.32;0.52;1.8;0.132;17;44;0.99734;3.28;0.77;11.5;6 +10.9;0.32;0.52;1.8;0.132;17;44;0.99734;3.28;0.77;11.5;6 +8.1;0.53;0.22;2.2;0.078;33;89;0.99678;3.26;0.46;9.6;6 +10.5;0.36;0.47;2.2;0.074;9;23;0.99638;3.23;0.76;12;6 +12.6;0.39;0.49;2.5;0.08;8;20;0.9992;3.07;0.82;10.3;6 +9.2;0.46;0.23;2.6;0.091;18;77;0.99922;3.15;0.51;9.4;5 +7.5;0.58;0.03;4.1;0.08;27;46;0.99592;3.02;0.47;9.2;5 +9;0.58;0.25;2;0.104;8;21;0.99769;3.27;0.72;9.6;5 +5.1;0.42;0;1.8;0.044;18;88;0.99157;3.68;0.73;13.6;7 +7.6;0.43;0.29;2.1;0.075;19;66;0.99718;3.4;0.64;9.5;5 +7.7;0.18;0.34;2.7;0.066;15;58;0.9947;3.37;0.78;11.8;6 +7.8;0.815;0.01;2.6;0.074;48;90;0.99621;3.38;0.62;10.8;5 +7.6;0.43;0.29;2.1;0.075;19;66;0.99718;3.4;0.64;9.5;5 +10.2;0.23;0.37;2.2;0.057;14;36;0.99614;3.23;0.49;9.3;4 +7.1;0.75;0.01;2.2;0.059;11;18;0.99242;3.39;0.4;12.8;6 +6;0.33;0.32;12.9;0.054;6;113;0.99572;3.3;0.56;11.5;4 +7.8;0.55;0;1.7;0.07;7;17;0.99659;3.26;0.64;9.4;6 +7.1;0.75;0.01;2.2;0.059;11;18;0.99242;3.39;0.4;12.8;6 +8.1;0.73;0;2.5;0.081;12;24;0.99798;3.38;0.46;9.6;4 +6.5;0.67;0;4.3;0.057;11;20;0.99488;3.45;0.56;11.8;4 +7.5;0.61;0.2;1.7;0.076;36;60;0.99494;3.1;0.4;9.3;5 +9.8;0.37;0.39;2.5;0.079;28;65;0.99729;3.16;0.59;9.8;5 +9;0.4;0.41;2;0.058;15;40;0.99414;3.22;0.6;12.2;6 +8.3;0.56;0.22;2.4;0.082;10;86;0.9983;3.37;0.62;9.5;5 +5.9;0.29;0.25;13.4;0.067;72;160;0.99721;3.33;0.54;10.3;6 +7.4;0.55;0.19;1.8;0.082;15;34;0.99655;3.49;0.68;10.5;5 +7.4;0.74;0.07;1.7;0.086;15;48;0.99502;3.12;0.48;10;5 +7.4;0.55;0.19;1.8;0.082;15;34;0.99655;3.49;0.68;10.5;5 +6.9;0.41;0.33;2.2;0.081;22;36;0.9949;3.41;0.75;11.1;6 +7.1;0.6;0.01;2.3;0.079;24;37;0.99514;3.4;0.61;10.9;6 +7.1;0.6;0.01;2.3;0.079;24;37;0.99514;3.4;0.61;10.9;6 +7.5;0.58;0.14;2.2;0.077;27;60;0.9963;3.28;0.59;9.8;5 +7.1;0.72;0;1.8;0.123;6;14;0.99627;3.45;0.58;9.8;5 +7.9;0.66;0;1.4;0.096;6;13;0.99569;3.43;0.58;9.5;5 +7.8;0.7;0.06;1.9;0.079;20;35;0.99628;3.4;0.69;10.9;5 +6.1;0.64;0.02;2.4;0.069;26;46;0.99358;3.47;0.45;11;5 +7.5;0.59;0.22;1.8;0.082;43;60;0.99499;3.1;0.42;9.2;5 +7;0.58;0.28;4.8;0.085;12;69;0.99633;3.32;0.7;11;6 +6.8;0.64;0;2.7;0.123;15;33;0.99538;3.44;0.63;11.3;6 +6.8;0.64;0;2.7;0.123;15;33;0.99538;3.44;0.63;11.3;6 +8.6;0.635;0.68;1.8;0.403;19;56;0.99632;3.02;1.15;9.3;5 +6.3;1.02;0;2;0.083;17;24;0.99437;3.59;0.55;11.2;4 +9.8;0.45;0.38;2.5;0.081;34;66;0.99726;3.15;0.58;9.8;5 +8.2;0.78;0;2.2;0.089;13;26;0.9978;3.37;0.46;9.6;4 +8.5;0.37;0.32;1.8;0.066;26;51;0.99456;3.38;0.72;11.8;6 +7.2;0.57;0.05;2.3;0.081;16;36;0.99564;3.38;0.6;10.3;6 +7.2;0.57;0.05;2.3;0.081;16;36;0.99564;3.38;0.6;10.3;6 +10.4;0.43;0.5;2.3;0.068;13;19;0.996;3.1;0.87;11.4;6 +6.9;0.41;0.31;2;0.079;21;51;0.99668;3.47;0.55;9.5;6 +5.5;0.49;0.03;1.8;0.044;28;87;0.9908;3.5;0.82;14;8 +5;0.38;0.01;1.6;0.048;26;60;0.99084;3.7;0.75;14;6 +7.3;0.44;0.2;1.6;0.049;24;64;0.9935;3.38;0.57;11.7;6 +5.9;0.46;0;1.9;0.077;25;44;0.99385;3.5;0.53;11.2;5 +7.5;0.58;0.2;2;0.073;34;44;0.99494;3.1;0.43;9.3;5 +7.8;0.58;0.13;2.1;0.102;17;36;0.9944;3.24;0.53;11.2;6 +8;0.715;0.22;2.3;0.075;13;81;0.99688;3.24;0.54;9.5;6 +8.5;0.4;0.4;6.3;0.05;3;10;0.99566;3.28;0.56;12;4 +7;0.69;0;1.9;0.114;3;10;0.99636;3.35;0.6;9.7;6 +8;0.715;0.22;2.3;0.075;13;81;0.99688;3.24;0.54;9.5;6 +9.8;0.3;0.39;1.7;0.062;3;9;0.9948;3.14;0.57;11.5;7 +7.1;0.46;0.2;1.9;0.077;28;54;0.9956;3.37;0.64;10.4;6 +7.1;0.46;0.2;1.9;0.077;28;54;0.9956;3.37;0.64;10.4;6 +7.9;0.765;0;2;0.084;9;22;0.99619;3.33;0.68;10.9;6 +8.7;0.63;0.28;2.7;0.096;17;69;0.99734;3.26;0.63;10.2;6 +7;0.42;0.19;2.3;0.071;18;36;0.99476;3.39;0.56;10.9;5 +11.3;0.37;0.5;1.8;0.09;20;47;0.99734;3.15;0.57;10.5;5 +7.1;0.16;0.44;2.5;0.068;17;31;0.99328;3.35;0.54;12.4;6 +8;0.6;0.08;2.6;0.056;3;7;0.99286;3.22;0.37;13;5 +7;0.6;0.3;4.5;0.068;20;110;0.99914;3.3;1.17;10.2;5 +7;0.6;0.3;4.5;0.068;20;110;0.99914;3.3;1.17;10.2;5 +7.6;0.74;0;1.9;0.1;6;12;0.99521;3.36;0.59;11;5 +8.2;0.635;0.1;2.1;0.073;25;60;0.99638;3.29;0.75;10.9;6 +5.9;0.395;0.13;2.4;0.056;14;28;0.99362;3.62;0.67;12.4;6 +7.5;0.755;0;1.9;0.084;6;12;0.99672;3.34;0.49;9.7;4 +8.2;0.635;0.1;2.1;0.073;25;60;0.99638;3.29;0.75;10.9;6 +6.6;0.63;0;4.3;0.093;51;77.5;0.99558;3.2;0.45;9.5;5 +6.6;0.63;0;4.3;0.093;51;77.5;0.99558;3.2;0.45;9.5;5 +7.2;0.53;0.14;2.1;0.064;15;29;0.99323;3.35;0.61;12.1;6 +5.7;0.6;0;1.4;0.063;11;18;0.99191;3.45;0.56;12.2;6 +7.6;1.58;0;2.1;0.137;5;9;0.99476;3.5;0.4;10.9;3 +5.2;0.645;0;2.15;0.08;15;28;0.99444;3.78;0.61;12.5;6 +6.7;0.86;0.07;2;0.1;20;57;0.99598;3.6;0.74;11.7;6 +9.1;0.37;0.32;2.1;0.064;4;15;0.99576;3.3;0.8;11.2;6 +8;0.28;0.44;1.8;0.081;28;68;0.99501;3.36;0.66;11.2;5 +7.6;0.79;0.21;2.3;0.087;21;68;0.9955;3.12;0.44;9.2;5 +7.5;0.61;0.26;1.9;0.073;24;88;0.99612;3.3;0.53;9.8;5 +9.7;0.69;0.32;2.5;0.088;22;91;0.9979;3.29;0.62;10.1;5 +6.8;0.68;0.09;3.9;0.068;15;29;0.99524;3.41;0.52;11.1;4 +9.7;0.69;0.32;2.5;0.088;22;91;0.9979;3.29;0.62;10.1;5 +7;0.62;0.1;1.4;0.071;27;63;0.996;3.28;0.61;9.2;5 +7.5;0.61;0.26;1.9;0.073;24;88;0.99612;3.3;0.53;9.8;5 +6.5;0.51;0.15;3;0.064;12;27;0.9929;3.33;0.59;12.8;6 +8;1.18;0.21;1.9;0.083;14;41;0.99532;3.34;0.47;10.5;5 +7;0.36;0.21;2.3;0.086;20;65;0.99558;3.4;0.54;10.1;6 +7;0.36;0.21;2.4;0.086;24;69;0.99556;3.4;0.53;10.1;6 +7.5;0.63;0.27;2;0.083;17;91;0.99616;3.26;0.58;9.8;6 +5.4;0.74;0;1.2;0.041;16;46;0.99258;4.01;0.59;12.5;6 +9.9;0.44;0.46;2.2;0.091;10;41;0.99638;3.18;0.69;11.9;6 +7.5;0.63;0.27;2;0.083;17;91;0.99616;3.26;0.58;9.8;6 +9.1;0.76;0.68;1.7;0.414;18;64;0.99652;2.9;1.33;9.1;6 +9.7;0.66;0.34;2.6;0.094;12;88;0.99796;3.26;0.66;10.1;5 +5;0.74;0;1.2;0.041;16;46;0.99258;4.01;0.59;12.5;6 +9.1;0.34;0.42;1.8;0.058;9;18;0.99392;3.18;0.55;11.4;5 +9.1;0.36;0.39;1.8;0.06;21;55;0.99495;3.18;0.82;11;7 +6.7;0.46;0.24;1.7;0.077;18;34;0.9948;3.39;0.6;10.6;6 +6.7;0.46;0.24;1.7;0.077;18;34;0.9948;3.39;0.6;10.6;6 +6.7;0.46;0.24;1.7;0.077;18;34;0.9948;3.39;0.6;10.6;6 +6.7;0.46;0.24;1.7;0.077;18;34;0.9948;3.39;0.6;10.6;6 +6.5;0.52;0.11;1.8;0.073;13;38;0.9955;3.34;0.52;9.3;5 +7.4;0.6;0.26;2.1;0.083;17;91;0.99616;3.29;0.56;9.8;6 +7.4;0.6;0.26;2.1;0.083;17;91;0.99616;3.29;0.56;9.8;6 +7.8;0.87;0.26;3.8;0.107;31;67;0.99668;3.26;0.46;9.2;5 +8.4;0.39;0.1;1.7;0.075;6;25;0.99581;3.09;0.43;9.7;6 +9.1;0.775;0.22;2.2;0.079;12;48;0.9976;3.18;0.51;9.6;5 +7.2;0.835;0;2;0.166;4;11;0.99608;3.39;0.52;10;5 +6.6;0.58;0.02;2.4;0.069;19;40;0.99387;3.38;0.66;12.6;6 +6;0.5;0;1.4;0.057;15;26;0.99448;3.36;0.45;9.5;5 +6;0.5;0;1.4;0.057;15;26;0.99448;3.36;0.45;9.5;5 +6;0.5;0;1.4;0.057;15;26;0.99448;3.36;0.45;9.5;5 +7.5;0.51;0.02;1.7;0.084;13;31;0.99538;3.36;0.54;10.5;6 +7.5;0.51;0.02;1.7;0.084;13;31;0.99538;3.36;0.54;10.5;6 +7.5;0.51;0.02;1.7;0.084;13;31;0.99538;3.36;0.54;10.5;6 +7.6;0.54;0.02;1.7;0.085;17;31;0.99589;3.37;0.51;10.4;6 +7.5;0.51;0.02;1.7;0.084;13;31;0.99538;3.36;0.54;10.5;6 +11.5;0.42;0.48;2.6;0.077;8;20;0.99852;3.09;0.53;11;5 +8.2;0.44;0.24;2.3;0.063;10;28;0.99613;3.25;0.53;10.2;6 +6.1;0.59;0.01;2.1;0.056;5;13;0.99472;3.52;0.56;11.4;5 +7.2;0.655;0.03;1.8;0.078;7;12;0.99587;3.34;0.39;9.5;5 +7.2;0.655;0.03;1.8;0.078;7;12;0.99587;3.34;0.39;9.5;5 +6.9;0.57;0;2.8;0.081;21;41;0.99518;3.41;0.52;10.8;5 +9;0.6;0.29;2;0.069;32;73;0.99654;3.34;0.57;10;5 +7.2;0.62;0.01;2.3;0.065;8;46;0.99332;3.32;0.51;11.8;6 +7.6;0.645;0.03;1.9;0.086;14;57;0.9969;3.37;0.46;10.3;5 +7.6;0.645;0.03;1.9;0.086;14;57;0.9969;3.37;0.46;10.3;5 +7.2;0.58;0.03;2.3;0.077;7;28;0.99568;3.35;0.52;10;5 +6.1;0.32;0.25;1.8;0.086;5;32;0.99464;3.36;0.44;10.1;5 +6.1;0.34;0.25;1.8;0.084;4;28;0.99464;3.36;0.44;10.1;5 +7.3;0.43;0.24;2.5;0.078;27;67;0.99648;3.6;0.59;11.1;6 +7.4;0.64;0.17;5.4;0.168;52;98;0.99736;3.28;0.5;9.5;5 +11.6;0.475;0.4;1.4;0.091;6;28;0.99704;3.07;0.65;10.0333333333333;6 +9.2;0.54;0.31;2.3;0.112;11;38;0.99699;3.24;0.56;10.9;5 +8.3;0.85;0.14;2.5;0.093;13;54;0.99724;3.36;0.54;10.1;5 +11.6;0.475;0.4;1.4;0.091;6;28;0.99704;3.07;0.65;10.0333333333333;6 +8;0.83;0.27;2;0.08;11;63;0.99652;3.29;0.48;9.8;4 +7.2;0.605;0.02;1.9;0.096;10;31;0.995;3.46;0.53;11.8;6 +7.8;0.5;0.09;2.2;0.115;10;42;0.9971;3.18;0.62;9.5;5 +7.3;0.74;0.08;1.7;0.094;10;45;0.99576;3.24;0.5;9.8;5 +6.9;0.54;0.3;2.2;0.088;9;105;0.99725;3.25;1.18;10.5;6 +8;0.77;0.32;2.1;0.079;16;74;0.99656;3.27;0.5;9.8;6 +6.6;0.61;0;1.6;0.069;4;8;0.99396;3.33;0.37;10.4;4 +8.7;0.78;0.51;1.7;0.415;12;66;0.99623;3;1.17;9.2;5 +7.5;0.58;0.56;3.1;0.153;5;14;0.99476;3.21;1.03;11.6;6 +8.7;0.78;0.51;1.7;0.415;12;66;0.99623;3;1.17;9.2;5 +7.7;0.75;0.27;3.8;0.11;34;89;0.99664;3.24;0.45;9.3;5 +6.8;0.815;0;1.2;0.267;16;29;0.99471;3.32;0.51;9.8;3 +7.2;0.56;0.26;2;0.083;13;100;0.99586;3.26;0.52;9.9;5 +8.2;0.885;0.2;1.4;0.086;7;31;0.9946;3.11;0.46;10;5 +5.2;0.49;0.26;2.3;0.09;23;74;0.9953;3.71;0.62;12.2;6 +7.2;0.45;0.15;2;0.078;10;28;0.99609;3.29;0.51;9.9;6 +7.5;0.57;0.02;2.6;0.077;11;35;0.99557;3.36;0.62;10.8;6 +7.5;0.57;0.02;2.6;0.077;11;35;0.99557;3.36;0.62;10.8;6 +6.8;0.83;0.09;1.8;0.074;4;25;0.99534;3.38;0.45;9.6;5 +8;0.6;0.22;2.1;0.08;25;105;0.99613;3.3;0.49;9.9;5 +8;0.6;0.22;2.1;0.08;25;105;0.99613;3.3;0.49;9.9;5 +7.1;0.755;0.15;1.8;0.107;20;84;0.99593;3.19;0.5;9.5;5 +8;0.81;0.25;3.4;0.076;34;85;0.99668;3.19;0.42;9.2;5 +7.4;0.64;0.07;1.8;0.1;8;23;0.9961;3.3;0.58;9.6;5 +7.4;0.64;0.07;1.8;0.1;8;23;0.9961;3.3;0.58;9.6;5 +6.6;0.64;0.31;6.1;0.083;7;49;0.99718;3.35;0.68;10.3;5 +6.7;0.48;0.02;2.2;0.08;36;111;0.99524;3.1;0.53;9.7;5 +6;0.49;0;2.3;0.068;15;33;0.99292;3.58;0.59;12.5;6 +8;0.64;0.22;2.4;0.094;5;33;0.99612;3.37;0.58;11;5 +7.1;0.62;0.06;1.3;0.07;5;12;0.9942;3.17;0.48;9.8;5 +8;0.52;0.25;2;0.078;19;59;0.99612;3.3;0.48;10.2;5 +6.4;0.57;0.14;3.9;0.07;27;73;0.99669;3.32;0.48;9.2;5 +8.6;0.685;0.1;1.6;0.092;3;12;0.99745;3.31;0.65;9.55;6 +8.7;0.675;0.1;1.6;0.09;4;11;0.99745;3.31;0.65;9.55;5 +7.3;0.59;0.26;2;0.08;17;104;0.99584;3.28;0.52;9.9;5 +7;0.6;0.12;2.2;0.083;13;28;0.9966;3.52;0.62;10.2;7 +7.2;0.67;0;2.2;0.068;10;24;0.9956;3.42;0.72;11.1;6 +7.9;0.69;0.21;2.1;0.08;33;141;0.9962;3.25;0.51;9.9;5 +7.9;0.69;0.21;2.1;0.08;33;141;0.9962;3.25;0.51;9.9;5 +7.6;0.3;0.42;2;0.052;6;24;0.9963;3.44;0.82;11.9;6 +7.2;0.33;0.33;1.7;0.061;3;13;0.996;3.23;1.1;10;8 +8;0.5;0.39;2.6;0.082;12;46;0.9985;3.43;0.62;10.7;6 +7.7;0.28;0.3;2;0.062;18;34;0.9952;3.28;0.9;11.3;7 +8.2;0.24;0.34;5.1;0.062;8;22;0.9974;3.22;0.94;10.9;6 +6;0.51;0;2.1;0.064;40;54;0.995;3.54;0.93;10.7;6 +8.1;0.29;0.36;2.2;0.048;35;53;0.995;3.27;1.01;12.4;7 +6;0.51;0;2.1;0.064;40;54;0.995;3.54;0.93;10.7;6 +6.6;0.96;0;1.8;0.082;5;16;0.9936;3.5;0.44;11.9;6 +6.4;0.47;0.4;2.4;0.071;8;19;0.9963;3.56;0.73;10.6;6 +8.2;0.24;0.34;5.1;0.062;8;22;0.9974;3.22;0.94;10.9;6 +9.9;0.57;0.25;2;0.104;12;89;0.9963;3.04;0.9;10.1;5 +10;0.32;0.59;2.2;0.077;3;15;0.9994;3.2;0.78;9.6;5 +6.2;0.58;0;1.6;0.065;8;18;0.9966;3.56;0.84;9.4;5 +10;0.32;0.59;2.2;0.077;3;15;0.9994;3.2;0.78;9.6;5 +7.3;0.34;0.33;2.5;0.064;21;37;0.9952;3.35;0.77;12.1;7 +7.8;0.53;0.01;1.6;0.077;3;19;0.995;3.16;0.46;9.8;5 +7.7;0.64;0.21;2.2;0.077;32;133;0.9956;3.27;0.45;9.9;5 +7.8;0.53;0.01;1.6;0.077;3;19;0.995;3.16;0.46;9.8;5 +7.5;0.4;0.18;1.6;0.079;24;58;0.9965;3.34;0.58;9.4;5 +7;0.54;0;2.1;0.079;39;55;0.9956;3.39;0.84;11.4;6 +6.4;0.53;0.09;3.9;0.123;14;31;0.9968;3.5;0.67;11;4 +8.3;0.26;0.37;1.4;0.076;8;23;0.9974;3.26;0.7;9.6;6 +8.3;0.26;0.37;1.4;0.076;8;23;0.9974;3.26;0.7;9.6;6 +7.7;0.23;0.37;1.8;0.046;23;60;0.9971;3.41;0.71;12.1;6 +7.6;0.41;0.33;2.5;0.078;6;23;0.9957;3.3;0.58;11.2;5 +7.8;0.64;0;1.9;0.072;27;55;0.9962;3.31;0.63;11;5 +7.9;0.18;0.4;2.2;0.049;38;67;0.996;3.33;0.93;11.3;5 +7.4;0.41;0.24;1.8;0.066;18;47;0.9956;3.37;0.62;10.4;5 +7.6;0.43;0.31;2.1;0.069;13;74;0.9958;3.26;0.54;9.9;6 +5.9;0.44;0;1.6;0.042;3;11;0.9944;3.48;0.85;11.7;6 +6.1;0.4;0.16;1.8;0.069;11;25;0.9955;3.42;0.74;10.1;7 +10.2;0.54;0.37;15.4;0.214;55;95;1.00369;3.18;0.77;9;6 +10.2;0.54;0.37;15.4;0.214;55;95;1.00369;3.18;0.77;9;6 +10;0.38;0.38;1.6;0.169;27;90;0.99914;3.15;0.65;8.5;5 +6.8;0.915;0.29;4.8;0.07;15;39;0.99577;3.53;0.54;11.1;5 +7;0.59;0;1.7;0.052;3;8;0.996;3.41;0.47;10.3;5 +7.3;0.67;0.02;2.2;0.072;31;92;0.99566;3.32;0.68;11.0666666666667;6 +7.2;0.37;0.32;2;0.062;15;28;0.9947;3.23;0.73;11.3;7 +7.4;0.785;0.19;5.2;0.094;19;98;0.99713;3.16;0.52;9.56666666666667;6 +6.9;0.63;0.02;1.9;0.078;18;30;0.99712;3.4;0.75;9.8;5 +6.9;0.58;0.2;1.75;0.058;8;22;0.99322;3.38;0.49;11.7;5 +7.3;0.67;0.02;2.2;0.072;31;92;0.99566;3.32;0.68;11.1;6 +7.4;0.785;0.19;5.2;0.094;19;98;0.99713;3.16;0.52;9.6;6 +6.9;0.63;0.02;1.9;0.078;18;30;0.99712;3.4;0.75;9.8;5 +6.8;0.67;0;1.9;0.08;22;39;0.99701;3.4;0.74;9.7;5 +6.9;0.58;0.01;1.9;0.08;40;54;0.99683;3.4;0.73;9.7;5 +7.2;0.38;0.31;2;0.056;15;29;0.99472;3.23;0.76;11.3;8 +7.2;0.37;0.32;2;0.062;15;28;0.9947;3.23;0.73;11.3;7 +7.8;0.32;0.44;2.7;0.104;8;17;0.99732;3.33;0.78;11;7 +6.6;0.58;0.02;2;0.062;37;53;0.99374;3.35;0.76;11.6;7 +7.6;0.49;0.33;1.9;0.074;27;85;0.99706;3.41;0.58;9;5 +11.7;0.45;0.63;2.2;0.073;7;23;0.99974;3.21;0.69;10.9;6 +6.5;0.9;0;1.6;0.052;9;17;0.99467;3.5;0.63;10.9;6 +6;0.54;0.06;1.8;0.05;38;89;0.99236;3.3;0.5;10.55;6 +7.6;0.49;0.33;1.9;0.074;27;85;0.99706;3.41;0.58;9;5 +8.4;0.29;0.4;1.7;0.067;8;20;0.99603;3.39;0.6;10.5;5 +7.9;0.2;0.35;1.7;0.054;7;15;0.99458;3.32;0.8;11.9;7 +6.4;0.42;0.09;2.3;0.054;34;64;0.99724;3.41;0.68;10.4;6 +6.2;0.785;0;2.1;0.06;6;13;0.99664;3.59;0.61;10;4 +6.8;0.64;0.03;2.3;0.075;14;31;0.99545;3.36;0.58;10.4;6 +6.9;0.63;0.01;2.4;0.076;14;39;0.99522;3.34;0.53;10.8;6 +6.8;0.59;0.1;1.7;0.063;34;53;0.9958;3.41;0.67;9.7;5 +6.8;0.59;0.1;1.7;0.063;34;53;0.9958;3.41;0.67;9.7;5 +7.3;0.48;0.32;2.1;0.062;31;54;0.99728;3.3;0.65;10;7 +6.7;1.04;0.08;2.3;0.067;19;32;0.99648;3.52;0.57;11;4 +7.3;0.48;0.32;2.1;0.062;31;54;0.99728;3.3;0.65;10;7 +7.3;0.98;0.05;2.1;0.061;20;49;0.99705;3.31;0.55;9.7;3 +10;0.69;0.11;1.4;0.084;8;24;0.99578;2.88;0.47;9.7;5 +6.7;0.7;0.08;3.75;0.067;8;16;0.99334;3.43;0.52;12.6;5 +7.6;0.35;0.6;2.6;0.073;23;44;0.99656;3.38;0.79;11.1;6 +6.1;0.6;0.08;1.8;0.071;14;45;0.99336;3.38;0.54;11;5 +9.9;0.5;0.5;13.8;0.205;48;82;1.00242;3.16;0.75;8.8;5 +5.3;0.47;0.11;2.2;0.048;16;89;0.99182;3.54;0.88;13.5666666666667;7 +9.9;0.5;0.5;13.8;0.205;48;82;1.00242;3.16;0.75;8.8;5 +5.3;0.47;0.11;2.2;0.048;16;89;0.99182;3.54;0.88;13.6;7 +7.1;0.875;0.05;5.7;0.082;3;14;0.99808;3.4;0.52;10.2;3 +8.2;0.28;0.6;3;0.104;10;22;0.99828;3.39;0.68;10.6;5 +5.6;0.62;0.03;1.5;0.08;6;13;0.99498;3.66;0.62;10.1;4 +8.2;0.28;0.6;3;0.104;10;22;0.99828;3.39;0.68;10.6;5 +7.2;0.58;0.54;2.1;0.114;3;9;0.99719;3.33;0.57;10.3;4 +8.1;0.33;0.44;1.5;0.042;6;12;0.99542;3.35;0.61;10.7;5 +6.8;0.91;0.06;2;0.06;4;11;0.99592;3.53;0.64;10.9;4 +7;0.655;0.16;2.1;0.074;8;25;0.99606;3.37;0.55;9.7;5 +6.8;0.68;0.21;2.1;0.07;9;23;0.99546;3.38;0.6;10.3;5 +6;0.64;0.05;1.9;0.066;9;17;0.99496;3.52;0.78;10.6;5 +5.6;0.54;0.04;1.7;0.049;5;13;0.9942;3.72;0.58;11.4;5 +6.2;0.57;0.1;2.1;0.048;4;11;0.99448;3.44;0.76;10.8;6 +7.1;0.22;0.49;1.8;0.039;8;18;0.99344;3.39;0.56;12.4;6 +5.6;0.54;0.04;1.7;0.049;5;13;0.9942;3.72;0.58;11.4;5 +6.2;0.65;0.06;1.6;0.05;6;18;0.99348;3.57;0.54;11.95;5 +7.7;0.54;0.26;1.9;0.089;23;147;0.99636;3.26;0.59;9.7;5 +6.4;0.31;0.09;1.4;0.066;15;28;0.99459;3.42;0.7;10;7 +7;0.43;0.02;1.9;0.08;15;28;0.99492;3.35;0.81;10.6;6 +7.7;0.54;0.26;1.9;0.089;23;147;0.99636;3.26;0.59;9.7;5 +6.9;0.74;0.03;2.3;0.054;7;16;0.99508;3.45;0.63;11.5;6 +6.6;0.895;0.04;2.3;0.068;7;13;0.99582;3.53;0.58;10.8;6 +6.9;0.74;0.03;2.3;0.054;7;16;0.99508;3.45;0.63;11.5;6 +7.5;0.725;0.04;1.5;0.076;8;15;0.99508;3.26;0.53;9.6;5 +7.8;0.82;0.29;4.3;0.083;21;64;0.99642;3.16;0.53;9.4;5 +7.3;0.585;0.18;2.4;0.078;15;60;0.99638;3.31;0.54;9.8;5 +6.2;0.44;0.39;2.5;0.077;6;14;0.99555;3.51;0.69;11;6 +7.5;0.38;0.57;2.3;0.106;5;12;0.99605;3.36;0.55;11.4;6 +6.7;0.76;0.02;1.8;0.078;6;12;0.996;3.55;0.63;9.95;3 +6.8;0.81;0.05;2;0.07;6;14;0.99562;3.51;0.66;10.8;6 +7.5;0.38;0.57;2.3;0.106;5;12;0.99605;3.36;0.55;11.4;6 +7.1;0.27;0.6;2.1;0.074;17;25;0.99814;3.38;0.72;10.6;6 +7.9;0.18;0.4;1.8;0.062;7;20;0.9941;3.28;0.7;11.1;5 +6.4;0.36;0.21;2.2;0.047;26;48;0.99661;3.47;0.77;9.7;6 +7.1;0.69;0.04;2.1;0.068;19;27;0.99712;3.44;0.67;9.8;5 +6.4;0.79;0.04;2.2;0.061;11;17;0.99588;3.53;0.65;10.4;6 +6.4;0.56;0.15;1.8;0.078;17;65;0.99294;3.33;0.6;10.5;6 +6.9;0.84;0.21;4.1;0.074;16;65;0.99842;3.53;0.72;9.23333333333333;6 +6.9;0.84;0.21;4.1;0.074;16;65;0.99842;3.53;0.72;9.25;6 +6.1;0.32;0.25;2.3;0.071;23;58;0.99633;3.42;0.97;10.6;5 +6.5;0.53;0.06;2;0.063;29;44;0.99489;3.38;0.83;10.3;6 +7.4;0.47;0.46;2.2;0.114;7;20;0.99647;3.32;0.63;10.5;5 +6.6;0.7;0.08;2.6;0.106;14;27;0.99665;3.44;0.58;10.2;5 +6.5;0.53;0.06;2;0.063;29;44;0.99489;3.38;0.83;10.3;6 +6.9;0.48;0.2;1.9;0.082;9;23;0.99585;3.39;0.43;9.05;4 +6.1;0.32;0.25;2.3;0.071;23;58;0.99633;3.42;0.97;10.6;5 +6.8;0.48;0.25;2;0.076;29;61;0.9953;3.34;0.6;10.4;5 +6;0.42;0.19;2;0.075;22;47;0.99522;3.39;0.78;10;6 +6.7;0.48;0.08;2.1;0.064;18;34;0.99552;3.33;0.64;9.7;5 +6.8;0.47;0.08;2.2;0.064;18;38;0.99553;3.3;0.65;9.6;6 +7.1;0.53;0.07;1.7;0.071;15;24;0.9951;3.29;0.66;10.8;6 +7.9;0.29;0.49;2.2;0.096;21;59;0.99714;3.31;0.67;10.1;6 +7.1;0.69;0.08;2.1;0.063;42;52;0.99608;3.42;0.6;10.2;6 +6.6;0.44;0.09;2.2;0.063;9;18;0.99444;3.42;0.69;11.3;6 +6.1;0.705;0.1;2.8;0.081;13;28;0.99631;3.6;0.66;10.2;5 +7.2;0.53;0.13;2;0.058;18;22;0.99573;3.21;0.68;9.9;6 +8;0.39;0.3;1.9;0.074;32;84;0.99717;3.39;0.61;9;5 +6.6;0.56;0.14;2.4;0.064;13;29;0.99397;3.42;0.62;11.7;7 +7;0.55;0.13;2.2;0.075;15;35;0.9959;3.36;0.59;9.7;6 +6.1;0.53;0.08;1.9;0.077;24;45;0.99528;3.6;0.68;10.3;6 +5.4;0.58;0.08;1.9;0.059;20;31;0.99484;3.5;0.64;10.2;6 +6.2;0.64;0.09;2.5;0.081;15;26;0.99538;3.57;0.63;12;5 +7.2;0.39;0.32;1.8;0.065;34;60;0.99714;3.46;0.78;9.9;5 +6.2;0.52;0.08;4.4;0.071;11;32;0.99646;3.56;0.63;11.6;6 +7.4;0.25;0.29;2.2;0.054;19;49;0.99666;3.4;0.76;10.9;7 +6.7;0.855;0.02;1.9;0.064;29;38;0.99472;3.3;0.56;10.75;6 +11.1;0.44;0.42;2.2;0.064;14;19;0.99758;3.25;0.57;10.4;6 +8.4;0.37;0.43;2.3;0.063;12;19;0.9955;3.17;0.81;11.2;7 +6.5;0.63;0.33;1.8;0.059;16;28;0.99531;3.36;0.64;10.1;6 +7;0.57;0.02;2;0.072;17;26;0.99575;3.36;0.61;10.2;5 +6.3;0.6;0.1;1.6;0.048;12;26;0.99306;3.55;0.51;12.1;5 +11.2;0.4;0.5;2;0.099;19;50;0.99783;3.1;0.58;10.4;5 +7.4;0.36;0.3;1.8;0.074;17;24;0.99419;3.24;0.7;11.4;8 +7.1;0.68;0;2.3;0.087;17;26;0.99783;3.45;0.53;9.5;5 +7.1;0.67;0;2.3;0.083;18;27;0.99768;3.44;0.54;9.4;5 +6.3;0.68;0.01;3.7;0.103;32;54;0.99586;3.51;0.66;11.3;6 +7.3;0.735;0;2.2;0.08;18;28;0.99765;3.41;0.6;9.4;5 +6.6;0.855;0.02;2.4;0.062;15;23;0.99627;3.54;0.6;11;6 +7;0.56;0.17;1.7;0.065;15;24;0.99514;3.44;0.68;10.55;7 +6.6;0.88;0.04;2.2;0.066;12;20;0.99636;3.53;0.56;9.9;5 +6.6;0.855;0.02;2.4;0.062;15;23;0.99627;3.54;0.6;11;6 +6.9;0.63;0.33;6.7;0.235;66;115;0.99787;3.22;0.56;9.5;5 +7.8;0.6;0.26;2;0.08;31;131;0.99622;3.21;0.52;9.9;5 +7.8;0.6;0.26;2;0.08;31;131;0.99622;3.21;0.52;9.9;5 +7.8;0.6;0.26;2;0.08;31;131;0.99622;3.21;0.52;9.9;5 +7.2;0.695;0.13;2;0.076;12;20;0.99546;3.29;0.54;10.1;5 +7.2;0.695;0.13;2;0.076;12;20;0.99546;3.29;0.54;10.1;5 +7.2;0.695;0.13;2;0.076;12;20;0.99546;3.29;0.54;10.1;5 +6.7;0.67;0.02;1.9;0.061;26;42;0.99489;3.39;0.82;10.9;6 +6.7;0.16;0.64;2.1;0.059;24;52;0.99494;3.34;0.71;11.2;6 +7.2;0.695;0.13;2;0.076;12;20;0.99546;3.29;0.54;10.1;5 +7;0.56;0.13;1.6;0.077;25;42;0.99629;3.34;0.59;9.2;5 +6.2;0.51;0.14;1.9;0.056;15;34;0.99396;3.48;0.57;11.5;6 +6.4;0.36;0.53;2.2;0.23;19;35;0.9934;3.37;0.93;12.4;6 +6.4;0.38;0.14;2.2;0.038;15;25;0.99514;3.44;0.65;11.1;6 +7.3;0.69;0.32;2.2;0.069;35;104;0.99632;3.33;0.51;9.5;5 +6;0.58;0.2;2.4;0.075;15;50;0.99467;3.58;0.67;12.5;6 +5.6;0.31;0.78;13.9;0.074;23;92;0.99677;3.39;0.48;10.5;6 +7.5;0.52;0.4;2.2;0.06;12;20;0.99474;3.26;0.64;11.8;6 +8;0.3;0.63;1.6;0.081;16;29;0.99588;3.3;0.78;10.8;6 +6.2;0.7;0.15;5.1;0.076;13;27;0.99622;3.54;0.6;11.9;6 +6.8;0.67;0.15;1.8;0.118;13;20;0.9954;3.42;0.67;11.3;6 +6.2;0.56;0.09;1.7;0.053;24;32;0.99402;3.54;0.6;11.3;5 +7.4;0.35;0.33;2.4;0.068;9;26;0.9947;3.36;0.6;11.9;6 +6.2;0.56;0.09;1.7;0.053;24;32;0.99402;3.54;0.6;11.3;5 +6.1;0.715;0.1;2.6;0.053;13;27;0.99362;3.57;0.5;11.9;5 +6.2;0.46;0.29;2.1;0.074;32;98;0.99578;3.33;0.62;9.8;5 +6.7;0.32;0.44;2.4;0.061;24;34;0.99484;3.29;0.8;11.6;7 +7.2;0.39;0.44;2.6;0.066;22;48;0.99494;3.3;0.84;11.5;6 +7.5;0.31;0.41;2.4;0.065;34;60;0.99492;3.34;0.85;11.4;6 +5.8;0.61;0.11;1.8;0.066;18;28;0.99483;3.55;0.66;10.9;6 +7.2;0.66;0.33;2.5;0.068;34;102;0.99414;3.27;0.78;12.8;6 +6.6;0.725;0.2;7.8;0.073;29;79;0.9977;3.29;0.54;9.2;5 +6.3;0.55;0.15;1.8;0.077;26;35;0.99314;3.32;0.82;11.6;6 +5.4;0.74;0.09;1.7;0.089;16;26;0.99402;3.67;0.56;11.6;6 +6.3;0.51;0.13;2.3;0.076;29;40;0.99574;3.42;0.75;11;6 +6.8;0.62;0.08;1.9;0.068;28;38;0.99651;3.42;0.82;9.5;6 +6.2;0.6;0.08;2;0.09;32;44;0.9949;3.45;0.58;10.5;5 +5.9;0.55;0.1;2.2;0.062;39;51;0.99512;3.52;0.76;11.2;6 +6.3;0.51;0.13;2.3;0.076;29;40;0.99574;3.42;0.75;11;6 +5.9;0.645;0.12;2;0.075;32;44;0.99547;3.57;0.71;10.2;5 +6;0.31;0.47;3.6;0.067;18;42;0.99549;3.39;0.66;11;6 \ No newline at end of file diff --git a/flinkserver/src/main/resources/log4j.properties.bat b/flinkserver/src/main/resources/log4j.properties.bat new file mode 100644 index 0000000..2b6f230 --- /dev/null +++ b/flinkserver/src/main/resources/log4j.properties.bat @@ -0,0 +1,6 @@ + +log4j.rootLogger=INFO, console + +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %-60c %x - %m%n \ No newline at end of file diff --git a/flinkserver/src/main/resources/logback.xml.bat b/flinkserver/src/main/resources/logback.xml.bat new file mode 100644 index 0000000..4b99754 --- /dev/null +++ b/flinkserver/src/main/resources/logback.xml.bat @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{60} %X{sourceThread} - %msg%n + + + + + + + \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/BadDataHandler.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/BadDataHandler.scala new file mode 100644 index 0000000..7995cd5 --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/BadDataHandler.scala @@ -0,0 +1,20 @@ +package com.lightbend.modelserver + +import org.apache.flink.api.common.functions.FlatMapFunction +import org.apache.flink.util.Collector + + +import scala.util.{Failure, Success, Try} + +object BadDataHandler { + def apply[T] = new BadDataHandler[T] +} + +class BadDataHandler[T] extends FlatMapFunction[Try[T], T] { + override def flatMap(t: Try[T], out: Collector[T]): Unit = { + t match { + case Success(t) => out.collect(t) + case Failure(e) => println(s"BAD DATA: ${e.getMessage}") + } + } +} \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/DataProcessorKeyed.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/DataProcessorKeyed.scala new file mode 100644 index 0000000..fe15725 --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/DataProcessorKeyed.scala @@ -0,0 +1,125 @@ +package com.lightbend.modelserver.keyed + +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.model.scala.Model +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import com.lightbend.modelserver.typeschema.ModelTypeSerializer +import com.lightbend.modelserver.support.scala.{ModelToServe, ModelToServeStats} +import org.apache.flink.api.common.state.{ListState, ListStateDescriptor, ValueState, ValueStateDescriptor} +import org.apache.flink.api.scala.createTypeInformation +import org.apache.flink.configuration.Configuration +import org.apache.flink.runtime.state.{FunctionInitializationContext, FunctionSnapshotContext} +import org.apache.flink.streaming.api.checkpoint.{CheckpointedFunction, CheckpointedRestoring} +import org.apache.flink.streaming.api.functions.co.CoProcessFunction +import org.apache.flink.util.Collector + +/** + * Created by boris on 5/8/17. + * + * Main class processing data using models + * + * see http://dataartisans.github.io/flink-training/exercises/eventTimeJoin.html for details + */ + +object DataProcessorKeyed { + def apply() = new DataProcessorKeyed + private val factories = Map(ModelDescriptor.ModelType.PMML -> PMMLModel, + ModelDescriptor.ModelType.TENSORFLOW -> TensorFlowModel) +} + +class DataProcessorKeyed extends CoProcessFunction[WineRecord, ModelToServe, Double] + with CheckpointedFunction with CheckpointedRestoring[List[Option[Model]]] { + + // The managed keyed state see https://ci.apache.org/projects/flink/flink-docs-release-1.3/dev/stream/state.html + var modelState: ValueState[ModelToServeStats] = _ + var newModelState: ValueState[ModelToServeStats] = _ + + var currentModel : Option[Model] = None + var newModel : Option[Model] = None + + @transient private var checkpointedState: ListState[Option[Model]] = null + + + override def open(parameters: Configuration): Unit = { + val modelDesc = new ValueStateDescriptor[ModelToServeStats]( + "currentModel", // state name + createTypeInformation[ModelToServeStats]) // type information + modelDesc.setQueryable("currentModel") + + modelState = getRuntimeContext.getState(modelDesc) + val newModelDesc = new ValueStateDescriptor[ModelToServeStats]( + "newModel", // state name + createTypeInformation[ModelToServeStats]) // type information + newModelState = getRuntimeContext.getState(newModelDesc) + } + + override def snapshotState(context: FunctionSnapshotContext): Unit = { + checkpointedState.clear() + checkpointedState.add(currentModel) + checkpointedState.add(newModel) + } + + override def initializeState(context: FunctionInitializationContext): Unit = { + val descriptor = new ListStateDescriptor[Option[Model]] ( + "modelState", + new ModelTypeSerializer) + + checkpointedState = context.getOperatorStateStore.getListState (descriptor) + + if (context.isRestored) { + val iterator = checkpointedState.get().iterator() + currentModel = iterator.next() + newModel = iterator.next() + } + } + + override def restoreState(state: List[Option[Model]]): Unit = { + currentModel = state(0) + newModel = state(1) + } + + override def processElement2(model: ModelToServe, ctx: CoProcessFunction[WineRecord, ModelToServe, Double]#Context, out: Collector[Double]): Unit = { + + import DataProcessorKeyed._ + + println(s"New model - $model") + newModelState.update(new ModelToServeStats(model)) + newModel = factories.get(model.modelType) match { + case Some(factory) => factory.create (model) + case _ => None + } + } + + override def processElement1(record: WineRecord, ctx: CoProcessFunction[WineRecord, ModelToServe, Double]#Context, out: Collector[Double]): Unit = { + + // See if we have update for the model + newModel match { + case Some(model) => { + // Clean up current model + currentModel match { + case Some(m) => m.cleanup() + case _ => + } + // Update model + currentModel = Some(model) + modelState.update(newModelState.value()) + newModel = None + } + case _ => + } + currentModel match { + case Some(model) => { + val start = System.currentTimeMillis() + val quality = model.score(record.asInstanceOf[AnyVal]).asInstanceOf[Double] + val duration = System.currentTimeMillis() - start + modelState.update(modelState.value().incrementUsage(duration)) + println(s"Calculated quality - $quality calculated in $duration ms") + } + case _ => println("No model available - skipping") + } + } +} diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/ModelServingKeyedJob.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/ModelServingKeyedJob.scala new file mode 100644 index 0000000..3b220ea --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/keyed/ModelServingKeyedJob.scala @@ -0,0 +1,151 @@ +package com.lightbend.modelserver.keyed + +import java.util.Properties + +import com.lightbend.configuration.kafka.ApplicationKafkaParameters +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.typeschema.ByteArraySchema +import com.lightbend.modelserver.BadDataHandler +import com.lightbend.modelserver.support.scala.ModelToServe +import com.lightbend.modelserver.support.scala.DataReader +import org.apache.flink.api.scala._ +import org.apache.flink.configuration._ +import org.apache.flink.runtime.concurrent.Executors +import org.apache.flink.runtime.highavailability.HighAvailabilityServicesUtils +import org.apache.flink.runtime.minicluster.LocalFlinkMiniCluster +import org.apache.flink.streaming.api.TimeCharacteristic +import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment +import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010 + + +/** + * Created by boris on 5/9/17. + * loosely based on http://dataartisans.github.io/flink-training/exercises/eventTimeJoin.html approach + * for queriable state + * https://github.com/dataArtisans/flink-queryable_state_demo/blob/master/README.md + * Using Flink min server to enable Queryable data access + * see https://github.com/dataArtisans/flink-queryable_state_demo/blob/master/src/main/java/com/dataartisans/queryablestatedemo/EventCountJob.java + * + * This little application is based on a RichCoProcessFunction which works on a keyed streams. It is applicable + * when a single applications serves multiple different models for different data types. Every model is keyed with + * the type of data what it is designed for. Same key should be present in the data, if it wants to use a specific + * model. + * Scaling of the application is based on the data type - for every key there is a separate instance of the + * RichCoProcessFunction dedicated to this type. All messages of the same type are processed by the same instance + * of RichCoProcessFunction + */ +object ModelServingKeyedJob { + + def main(args: Array[String]): Unit = { +// executeLocal() + executeServer() + } + + // Execute on the local Flink server - to test queariable state + def executeServer() : Unit = { + + // We use a mini cluster here for sake of simplicity, because I don't want + // to require a Flink installation to run this demo. Everything should be + // contained in this JAR. + + val port = 6124 + val parallelism = 2 + + val config = new Configuration() + config.setInteger(JobManagerOptions.PORT, port) + config.setString(JobManagerOptions.ADDRESS, "localhost"); + config.setInteger(ConfigConstants.TASK_MANAGER_NUM_TASK_SLOTS, parallelism) + // In a non MiniCluster setup queryable state is enabled by default. + config.setBoolean(QueryableStateOptions.SERVER_ENABLE, true) + config.setBoolean(ConfigConstants.LOCAL_START_WEBSERVER, true); + // needed because queryable state server is always disabled with only one TaskManager + config.setInteger(ConfigConstants.LOCAL_NUMBER_TASK_MANAGER, 2); + + // Create a local Flink server + val flinkCluster = new LocalFlinkMiniCluster( + config, + HighAvailabilityServicesUtils.createHighAvailabilityServices( + config, + Executors.directExecutor(), + HighAvailabilityServicesUtils.AddressResolution.TRY_ADDRESS_RESOLUTION), + false); + try { + // Start server and create environment + flinkCluster.start(true); + + val env = StreamExecutionEnvironment.createRemoteEnvironment("localhost", port) + env.setParallelism(parallelism) + // Build Graph + buildGraph(env) + env.execute() + val jobGraph = env.getStreamGraph.getJobGraph + // Submit to the server and wait for completion + flinkCluster.submitJobAndWait(jobGraph, false) + } catch { + case e: Exception => e.printStackTrace() + } + } + + // Execute localle in the environment + def executeLocal() : Unit = { + val env = StreamExecutionEnvironment.getExecutionEnvironment + buildGraph(env) + System.out.println("[info] Job ID: " + env.getStreamGraph.getJobGraph.getJobID) + env.execute() + } + + // Build execution Graph + def buildGraph(env : StreamExecutionEnvironment) : Unit = { + env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) + env.enableCheckpointing(5000) + + // configure Kafka consumer + // Data + val dataKafkaProps = new Properties + dataKafkaProps.setProperty("zookeeper.connect", ApplicationKafkaParameters.LOCAL_ZOOKEEPER_HOST) + dataKafkaProps.setProperty("bootstrap.servers", ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) + dataKafkaProps.setProperty("group.id", ApplicationKafkaParameters.DATA_GROUP) + // always read the Kafka topic from the current location + dataKafkaProps.setProperty("auto.offset.reset", "latest") + + // Model + val modelKafkaProps = new Properties + modelKafkaProps.setProperty("zookeeper.connect", ApplicationKafkaParameters.LOCAL_ZOOKEEPER_HOST) + modelKafkaProps.setProperty("bootstrap.servers", ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) + modelKafkaProps.setProperty("group.id", ApplicationKafkaParameters.MODELS_GROUP) + // always read the Kafka topic from the current location + modelKafkaProps.setProperty("auto.offset.reset", "latest") + + // create a Kafka consumers + // Data + val dataConsumer = new FlinkKafkaConsumer010[Array[Byte]]( + ApplicationKafkaParameters.DATA_TOPIC, + new ByteArraySchema, + dataKafkaProps + ) + + // Model + val modelConsumer = new FlinkKafkaConsumer010[Array[Byte]]( + ApplicationKafkaParameters.MODELS_TOPIC, + new ByteArraySchema, + modelKafkaProps + ) + + // Create input data streams + val modelsStream = env.addSource(modelConsumer) + val dataStream = env.addSource(dataConsumer) + + // Read data from streams + val models = modelsStream.map(ModelToServe.fromByteArray(_)) + .flatMap(BadDataHandler[ModelToServe]) + .keyBy(_.dataType) + val data = dataStream.map(DataReader.fromByteArray(_)) + .flatMap(BadDataHandler[WineRecord]) + .keyBy(_.dataType) + + // Merge streams + data + .connect(models) + .process(DataProcessorKeyed()) + } +} \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/DataProcessorMap.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/DataProcessorMap.scala new file mode 100644 index 0000000..d9ee84d --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/DataProcessorMap.scala @@ -0,0 +1,97 @@ +package com.lightbend.modelserver.partitioned + +/** + * Created by boris on 5/14/17. + * + * Main class processing data using models + * + */ +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.model.scala.Model +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import com.lightbend.modelserver.typeschema.ModelTypeSerializer +import com.lightbend.modelserver.support.scala.ModelToServe +import org.apache.flink.api.common.state.{ListState, ListStateDescriptor} +import org.apache.flink.runtime.state.{FunctionInitializationContext, FunctionSnapshotContext} +import org.apache.flink.streaming.api.checkpoint.{CheckpointedFunction, CheckpointedRestoring} +import org.apache.flink.streaming.api.functions.co.RichCoFlatMapFunction +import org.apache.flink.util.Collector + +object DataProcessorMap{ + def apply() : DataProcessorMap = new DataProcessorMap() + + private val factories = Map(ModelDescriptor.ModelType.PMML -> PMMLModel, + ModelDescriptor.ModelType.TENSORFLOW -> TensorFlowModel) +} + +class DataProcessorMap extends RichCoFlatMapFunction[WineRecord, ModelToServe, Double] + with CheckpointedFunction with CheckpointedRestoring[List[Option[Model]]] { + + var currentModel : Option[Model] = None + var newModel : Option[Model] = None + @transient private var checkpointedState: ListState[Option[Model]] = null + + override def snapshotState(context: FunctionSnapshotContext): Unit = { + checkpointedState.clear() + checkpointedState.add(currentModel) + checkpointedState.add(newModel) + } + + override def initializeState(context: FunctionInitializationContext): Unit = { + val descriptor = new ListStateDescriptor[Option[Model]] ( + "modelState", + new ModelTypeSerializer) + + checkpointedState = context.getOperatorStateStore.getListState (descriptor) + + if (context.isRestored) { + val iterator = checkpointedState.get().iterator() + currentModel = iterator.next() + newModel = iterator.next() + } + } + + override def restoreState(state: List[Option[Model]]): Unit = { + currentModel = state(0) + newModel = state(1) + } + + override def flatMap2(model: ModelToServe, out: Collector[Double]): Unit = { + + import DataProcessorMap._ + + println(s"New model - $model") + newModel = factories.get(model.modelType) match{ + case Some(factory) => factory.create(model) + case _ => None + } + } + + override def flatMap1(record: WineRecord, out: Collector[Double]): Unit = { + // See if we need to update + newModel match { + case Some(model) => { + // close current model first + currentModel match { + case Some(m) => m.cleanup(); + case _ => + } + // Update model + currentModel = Some(model) + newModel = None + } + case _ => + } + currentModel match { + case Some(model) => { + val start = System.currentTimeMillis() + val quality = model.score(record.asInstanceOf[AnyVal]).asInstanceOf[Double] + val duration = System.currentTimeMillis() - start + println(s"Subtask ${this.getRuntimeContext.getIndexOfThisSubtask} calculated quality - $quality calculated in $duration ms") + } + case _ => println("No model available - skipping") + } + } +} \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/ModelServingFlatJob.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/ModelServingFlatJob.scala new file mode 100644 index 0000000..9a01978 --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/partitioned/ModelServingFlatJob.scala @@ -0,0 +1,145 @@ +package com.lightbend.modelserver.partitioned + +import java.util.Properties + +import com.lightbend.configuration.kafka.ApplicationKafkaParameters +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.typeschema.ByteArraySchema +import com.lightbend.modelserver.BadDataHandler +import com.lightbend.modelserver.support.scala.{DataReader, ModelToServe} +import org.apache.flink.api.scala._ +import org.apache.flink.configuration.{ConfigConstants, Configuration, JobManagerOptions, QueryableStateOptions} +import org.apache.flink.runtime.concurrent.Executors +import org.apache.flink.runtime.highavailability.HighAvailabilityServicesUtils +import org.apache.flink.runtime.minicluster.LocalFlinkMiniCluster +import org.apache.flink.streaming.api.TimeCharacteristic +import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment +import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer010 + + +/** + * Created by boris on 5/9/17. + * loosely based on http://dataartisans.github.io/flink-training/exercises/eventTimeJoin.html approach + * for queriable state + * https://github.com/dataArtisans/flink-queryable_state_demo/blob/master/README.md + * Using Flink min server to enable Queryable data access + * see https://github.com/dataArtisans/flink-queryable_state_demo/blob/master/src/main/java/com/dataartisans/queryablestatedemo/EventCountJob.java + * + * This little application is based on a RichCoFlatMapFunction which works on a non keyed streams. It is + * applicable when a single applications serves a single model(model set) for a single data type. + * Scaling of the application is based on the parallelism of input stream and RichCoFlatMapFunction. + * The model is broadcasted to all RichCoFlatMapFunction instances. The messages are processed by different + * instances of RichCoFlatMapFunction in a round-robin fashion. + */ + +object ModelServingFlatJob { + + def main(args: Array[String]): Unit = { +// executeLocal() + executeServer() + } + + // Execute on the local Flink server - to test queariable state + def executeServer() : Unit = { + + // We use a mini cluster here for sake of simplicity, because I don't want + // to require a Flink installation to run this demo. Everything should be + // contained in this JAR. + + val port = 6124 + val parallelism = 4 + + + val config = new Configuration() + config.setInteger(JobManagerOptions.PORT, port) + config.setString(JobManagerOptions.ADDRESS, "localhost"); + config.setInteger(ConfigConstants.TASK_MANAGER_NUM_TASK_SLOTS, parallelism) + // In a non MiniCluster setup queryable state is enabled by default. + config.setBoolean(QueryableStateOptions.SERVER_ENABLE, true) + config.setBoolean(ConfigConstants.LOCAL_START_WEBSERVER, true); + // needed because queryable state server is always disabled with only one TaskManager + config.setInteger(ConfigConstants.LOCAL_NUMBER_TASK_MANAGER, 2); + + // Create a local Flink server + val flinkCluster = new LocalFlinkMiniCluster( + config, + HighAvailabilityServicesUtils.createHighAvailabilityServices( + config, + Executors.directExecutor(), + HighAvailabilityServicesUtils.AddressResolution.TRY_ADDRESS_RESOLUTION), + false); + try { + // Start server and create environment + flinkCluster.start(true); + val env = StreamExecutionEnvironment.createRemoteEnvironment("localhost", port, parallelism) + // Build Graph + buildGraph(env) + env.execute() + val jobGraph = env.getStreamGraph.getJobGraph + // Submit to the server and wait for completion + flinkCluster.submitJobAndWait(jobGraph, false) + } catch { + case e: Exception => e.printStackTrace() + } + } + + // Execute localle in the environment + def executeLocal() : Unit = { + val env = StreamExecutionEnvironment.getExecutionEnvironment + buildGraph(env) + System.out.println("[info] Job ID: " + env.getStreamGraph.getJobGraph.getJobID) + env.execute() + } + + // Build execution Graph + def buildGraph(env : StreamExecutionEnvironment) : Unit = { + env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) + env.enableCheckpointing(5000) + // configure Kafka consumer + // Data + val dataKafkaProps = new Properties + dataKafkaProps.setProperty("zookeeper.connect", ApplicationKafkaParameters.LOCAL_ZOOKEEPER_HOST) + dataKafkaProps.setProperty("bootstrap.servers", ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) + dataKafkaProps.setProperty("group.id", ApplicationKafkaParameters.DATA_GROUP) + dataKafkaProps.setProperty("auto.offset.reset", "latest") + + // Model + val modelKafkaProps = new Properties + modelKafkaProps.setProperty("zookeeper.connect", ApplicationKafkaParameters.LOCAL_ZOOKEEPER_HOST) + modelKafkaProps.setProperty("bootstrap.servers", ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) + modelKafkaProps.setProperty("group.id", ApplicationKafkaParameters.MODELS_GROUP) + // always read the Kafka topic from the current location + modelKafkaProps.setProperty("auto.offset.reset", "latest") + + // create a Kafka consumers + // Data + val dataConsumer = new FlinkKafkaConsumer010[Array[Byte]]( + ApplicationKafkaParameters.DATA_TOPIC, + new ByteArraySchema, + dataKafkaProps + ) + + // Model + val modelConsumer = new FlinkKafkaConsumer010[Array[Byte]]( + ApplicationKafkaParameters.MODELS_TOPIC, + new ByteArraySchema, + modelKafkaProps + ) + + // Create input data streams + val modelsStream = env.addSource(modelConsumer) + val dataStream = env.addSource(dataConsumer) + + // Read data from streams + val models = modelsStream.map(ModelToServe.fromByteArray(_)) + .flatMap(BadDataHandler[ModelToServe]) + .broadcast + val data = dataStream.map(DataReader.fromByteArray(_)) + .flatMap(BadDataHandler[WineRecord]) + + // Merge streams + data + .connect(models) + .flatMap(DataProcessorMap()) + } +} \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/query/ModelStateQuery.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/query/ModelStateQuery.scala new file mode 100644 index 0000000..08ce96b --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/query/ModelStateQuery.scala @@ -0,0 +1,71 @@ +package com.lightbend.modelserver.query + +import java.util.concurrent.{Executors, TimeUnit} + +import com.lightbend.modelserver.support.scala.ModelToServeStats +import org.apache.flink.api.common.{ExecutionConfig, JobID} +import org.apache.flink.api.scala.createTypeInformation +import org.apache.flink.configuration.{Configuration, JobManagerOptions} +import org.apache.flink.runtime.highavailability.HighAvailabilityServicesUtils +import org.apache.flink.runtime.query.QueryableStateClient +import org.apache.flink.runtime.query.netty.message.KvStateRequestSerializer +import org.apache.flink.runtime.state.{VoidNamespace, VoidNamespaceSerializer} +import org.joda.time.DateTime + +import scala.concurrent.Await +import scala.concurrent.duration.FiniteDuration + +/** + * Created by boris on 5/12/17. + * see https://ci.apache.org/projects/flink/flink-docs-release-1.3/dev/stream/queryable_state.html + * It uses default port 6123 to access Flink server + */ +object ModelStateQuery { + + val timeInterval = 1000 * 20 // 20 sec + + def main(args: Array[String]) { + + val jobId = JobID.fromHexString("817bfacb1f0317eb15fb20c1201b9e1a") + val types = Array("wine") + + val config = new Configuration() + config.setString(JobManagerOptions.ADDRESS, "localhost") + config.setInteger(JobManagerOptions.PORT, 6124) + + val highAvailabilityServices = HighAvailabilityServicesUtils.createHighAvailabilityServices( + config, Executors.newSingleThreadScheduledExecutor, HighAvailabilityServicesUtils.AddressResolution.TRY_ADDRESS_RESOLUTION) + val client = new QueryableStateClient(config, highAvailabilityServices) + + val execConfig = new ExecutionConfig + val keySerializer = createTypeInformation[String].createSerializer(execConfig) + val valueSerializer = createTypeInformation[ModelToServeStats].createSerializer(execConfig) + + println(" Name | Description | Since | Average | Min | Max |") + while(true) { + val stats = for (key <- types) yield { + val serializedKey = KvStateRequestSerializer.serializeKeyAndNamespace( + key, keySerializer, + VoidNamespace.INSTANCE, VoidNamespaceSerializer.INSTANCE) + + // now wait for the result and return it + try { + val serializedResult = client.getKvState(jobId, "currentModel", key.hashCode(), serializedKey) + val serializedValue = Await.result(serializedResult, FiniteDuration(2, TimeUnit.SECONDS)) + val value = KvStateRequestSerializer.deserializeValue(serializedValue, valueSerializer) + List(value.name, value.description, value.since, value.usage, value.duration, value.min, value.max) + } catch { + case e: Exception => { + e.printStackTrace() + List() + } + } + } + stats.toList.filter(_.nonEmpty).foreach(row => + println(s" ${row(0)} | ${row(1)} | ${new DateTime(row(2)).toString("yyyy/MM/dd HH:MM:SS")} | ${row(3)} |" + + s" ${row(4).asInstanceOf[Double]/row(3).asInstanceOf[Long]} | ${row(5)} | ${row(6)} |") + ) + Thread.sleep(timeInterval) + } + } +} \ No newline at end of file diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ByteArraySchema.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ByteArraySchema.scala new file mode 100644 index 0000000..7559cc3 --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ByteArraySchema.scala @@ -0,0 +1,23 @@ +package com.lightbend.modelserver.typeschema + +/** + * Created by boris on 5/9/17. + */ +import org.apache.flink.api.common.typeinfo.TypeInformation +import org.apache.flink.api.java.typeutils.TypeExtractor +import org.apache.flink.streaming.util.serialization.{DeserializationSchema, SerializationSchema} + +class ByteArraySchema extends DeserializationSchema[Array[Byte]] with SerializationSchema[Array[Byte]] { + + private val serialVersionUID: Long = 1234567L + + override def isEndOfStream(nextElement: Array[Byte]): Boolean = false + + override def deserialize(message: Array[Byte]): Array[Byte] = message + + override def serialize(element: Array[Byte]): Array[Byte] = element + + override def getProducedType: TypeInformation[Array[Byte]] = + //PrimitiveArrayTypeInfo.BYTE_PRIMITIVE_ARRAY_TYPE_INFO + TypeExtractor.getForClass(classOf[Array[Byte]]) +} diff --git a/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ModelTypeSerializer.scala b/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ModelTypeSerializer.scala new file mode 100644 index 0000000..5afbda9 --- /dev/null +++ b/flinkserver/src/main/scala/com/lightbend/modelserver/typeschema/ModelTypeSerializer.scala @@ -0,0 +1,153 @@ +package com.lightbend.modelserver.typeschema + +import java.io.IOException + +import com.lightbend.model.modeldescriptor.ModelDescriptor +import org.apache.flink.api.common.typeutils.{CompatibilityResult, GenericTypeSerializerConfigSnapshot, TypeSerializer, TypeSerializerConfigSnapshot} +import com.lightbend.model.scala.Model +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import org.apache.flink.core.memory.{DataInputView, DataOutputView} + +class ModelTypeSerializer extends TypeSerializer[Option[Model]] { + + import ModelTypeSerializer._ + + override def createInstance(): Option[Model] = None + + override def canEqual(obj: scala.Any): Boolean = obj.isInstanceOf[ModelTypeSerializer] + + override def duplicate(): TypeSerializer[Option[Model]] = new ModelTypeSerializer + + override def ensureCompatibility(configSnapshot: TypeSerializerConfigSnapshot): CompatibilityResult[Option[Model]] = + configSnapshot match{ + case _ : ModelSerializerConfigSnapshot[Model] => CompatibilityResult.compatible() + case _ => CompatibilityResult.requiresMigration() + } + + override def serialize(record: Option[Model], target: DataOutputView): Unit = { + record match { + case Some(model) => { + target.writeBoolean(true) + val content = model.toBytes() + target.writeLong(model.getType) + target.writeLong(content.length) + target.write(content) + } + case _ => target.writeBoolean(false) + } + } + + override def isImmutableType: Boolean = false + + override def getLength: Int = -1 + + override def snapshotConfiguration(): TypeSerializerConfigSnapshot = new ModelSerializerConfigSnapshot + + override def copy(from: Option[Model]): Option[Model] = + from match { + case Some(model) => Some(factories.get(model.getType.asInstanceOf[Int]).get.restore(model.toBytes())) + case _ => None + } + + override def copy(from: Option[Model], reuse: Option[Model]): Option[Model] = + from match { + case Some(model) => Some(factories.get(model.getType.asInstanceOf[Int]).get.restore(model.toBytes())) + case _ => None + } + + override def copy(source: DataInputView, target: DataOutputView): Unit = { + val exist = source.readBoolean() + target.writeBoolean(exist) + exist match { + case true => { + target.writeLong (source.readLong () ) + val clen = source.readLong ().asInstanceOf[Int] + target.writeLong (clen) + val content = new Array[Byte] (clen) + source.read (content) + target.write (content) + } + case _ => + } + } + + override def deserialize(source: DataInputView): Option[Model] = + source.readBoolean() match { + case true => { + val t = source.readLong().asInstanceOf[Int] + val size = source.readLong().asInstanceOf[Int] + val content = new Array[Byte] (size) + source.read (content) + Some(factories.get(t).get.restore(content)) + } + case _ => None + } + + override def deserialize(reuse: Option[Model], source: DataInputView): Option[Model] = + source.readBoolean() match { + case true => { + val t = source.readLong().asInstanceOf[Int] + val size = source.readLong().asInstanceOf[Int] + val content = new Array[Byte] (size) + source.read (content) + Some(factories.get(t).get.restore(content)) + } + case _ => None + } + + override def equals(obj: scala.Any): Boolean = obj.isInstanceOf[ModelTypeSerializer] + + override def hashCode(): Int = 42 +} + +object ModelTypeSerializer{ + private val factories = Map(ModelDescriptor.ModelType.PMML.value -> PMMLModel, + ModelDescriptor.ModelType.TENSORFLOW.value -> TensorFlowModel) + + def apply : ModelTypeSerializer = new ModelTypeSerializer() +} + + +object ModelSerializerConfigSnapshot { + val VERSION = 1 +} + +class ModelSerializerConfigSnapshot[T <: Model] + extends TypeSerializerConfigSnapshot{ + + import ModelSerializerConfigSnapshot._ + +// def this() {this(classOf[T])} + + override def getVersion = VERSION + + var typeClass = classOf[Model] + + override def write(out: DataOutputView): Unit = { + super.write(out) + // write only the classname to avoid Java serialization + out.writeUTF(classOf[Model].getName) + } + + override def read(in: DataInputView): Unit = { + super.read(in) + val genericTypeClassname = in.readUTF + try + typeClass = Class.forName(genericTypeClassname, true, getUserCodeClassLoader).asInstanceOf[Class[Model]] + catch { + case e: ClassNotFoundException => + throw new IOException("Could not find the requested class " + genericTypeClassname + " in classpath.", e) + } + } + + def getTypeClass: Class[T] = typeClass.asInstanceOf[Class[T]] + + override def equals(obj: Any): Boolean = { + if (obj == this) return true + if (obj == null) return false + (obj.getClass == getClass) && typeClass == obj.asInstanceOf[GenericTypeSerializerConfigSnapshot[_]].getTypeClass + } + + override def hashCode: Int = 42 +} diff --git a/client/src/main/scala/com/lightbend/kafka/DataProvider.scala b/kafkaclient/src/main/scala/com/lightbend/kafka/DataProvider.scala similarity index 100% rename from client/src/main/scala/com/lightbend/kafka/DataProvider.scala rename to kafkaclient/src/main/scala/com/lightbend/kafka/DataProvider.scala diff --git a/client/src/main/scala/com/lightbend/kafka/KafkaMessageSender.scala b/kafkaclient/src/main/scala/com/lightbend/kafka/KafkaMessageSender.scala similarity index 100% rename from client/src/main/scala/com/lightbend/kafka/KafkaMessageSender.scala rename to kafkaclient/src/main/scala/com/lightbend/kafka/KafkaMessageSender.scala diff --git a/client/src/main/scala/com/lightbend/kafka/ModelProvider.scala b/kafkaclient/src/main/scala/com/lightbend/kafka/ModelProvider.scala similarity index 100% rename from client/src/main/scala/com/lightbend/kafka/ModelProvider.scala rename to kafkaclient/src/main/scala/com/lightbend/kafka/ModelProvider.scala diff --git a/configuration/src/main/java/com/lightbend/configuration/kafka/ApplicationKafkaParameters.java b/kafkaconfiguration/src/main/java/com/lightbend/configuration/kafka/ApplicationKafkaParameters.java similarity index 100% rename from configuration/src/main/java/com/lightbend/configuration/kafka/ApplicationKafkaParameters.java rename to kafkaconfiguration/src/main/java/com/lightbend/configuration/kafka/ApplicationKafkaParameters.java diff --git a/model/src/main/java/com/lightbend/model/ModelFactory.java b/model/src/main/java/com/lightbend/model/ModelFactory.java deleted file mode 100644 index 4178220..0000000 --- a/model/src/main/java/com/lightbend/model/ModelFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.lightbend.model; - -import java.util.Optional; - -/** - * Created by boris on 7/14/17. - */ -public interface ModelFactory { - Optional create(CurrentModelDescriptor descriptor); - Model restore(byte[] bytes); -} diff --git a/model/src/main/java/com/lightbend/model/Model.java b/model/src/main/java/com/lightbend/model/java/Model.java similarity index 87% rename from model/src/main/java/com/lightbend/model/Model.java rename to model/src/main/java/com/lightbend/model/java/Model.java index 3089191..598d863 100644 --- a/model/src/main/java/com/lightbend/model/Model.java +++ b/model/src/main/java/com/lightbend/model/java/Model.java @@ -1,4 +1,4 @@ -package com.lightbend.model; +package com.lightbend.model.java; import java.io.Serializable; diff --git a/model/src/main/java/com/lightbend/model/java/ModelFactory.java b/model/src/main/java/com/lightbend/model/java/ModelFactory.java new file mode 100644 index 0000000..a97ebdd --- /dev/null +++ b/model/src/main/java/com/lightbend/model/java/ModelFactory.java @@ -0,0 +1,13 @@ +package com.lightbend.model.java; + +import com.lightbend.modelserver.support.java.ModelToServe; + +import java.util.Optional; + +/** + * Created by boris on 7/14/17. + */ +public interface ModelFactory { + Optional create(ModelToServe descriptor); + Model restore(byte[] bytes); +} diff --git a/model/src/main/java/com/lightbend/model/PMML/PMMLModel.java b/model/src/main/java/com/lightbend/model/java/PMML/PMMLModel.java similarity index 98% rename from model/src/main/java/com/lightbend/model/PMML/PMMLModel.java rename to model/src/main/java/com/lightbend/model/java/PMML/PMMLModel.java index 17cb601..63b58b4 100644 --- a/model/src/main/java/com/lightbend/model/PMML/PMMLModel.java +++ b/model/src/main/java/com/lightbend/model/java/PMML/PMMLModel.java @@ -1,7 +1,7 @@ -package com.lightbend.model.PMML; +package com.lightbend.model.java.PMML; import com.google.protobuf.Descriptors; -import com.lightbend.model.Model; +import com.lightbend.model.java.Model; import com.lightbend.model.Modeldescriptor; import com.lightbend.model.Winerecord; import org.dmg.pmml.FieldName; diff --git a/model/src/main/java/com/lightbend/model/PMML/PMMLModelFactory.java b/model/src/main/java/com/lightbend/model/java/PMML/PMMLModelFactory.java similarity index 79% rename from model/src/main/java/com/lightbend/model/PMML/PMMLModelFactory.java rename to model/src/main/java/com/lightbend/model/java/PMML/PMMLModelFactory.java index 42f19a7..191ded3 100644 --- a/model/src/main/java/com/lightbend/model/PMML/PMMLModelFactory.java +++ b/model/src/main/java/com/lightbend/model/java/PMML/PMMLModelFactory.java @@ -1,8 +1,8 @@ -package com.lightbend.model.PMML; +package com.lightbend.model.java.PMML; -import com.lightbend.model.Model; -import com.lightbend.model.CurrentModelDescriptor; -import com.lightbend.model.ModelFactory; +import com.lightbend.model.java.Model; +import com.lightbend.model.java.ModelFactory; +import com.lightbend.modelserver.support.java.ModelToServe; import java.util.Optional; @@ -16,7 +16,7 @@ public class PMMLModelFactory implements ModelFactory { private PMMLModelFactory(){} @Override - public Optional create(CurrentModelDescriptor descriptor) { + public Optional create(ModelToServe descriptor) { try{ return Optional.of(new PMMLModel(descriptor.getModelData())); } diff --git a/model/src/main/java/com/lightbend/model/tensorflow/TensorflowModel.java b/model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModel.java similarity index 96% rename from model/src/main/java/com/lightbend/model/tensorflow/TensorflowModel.java rename to model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModel.java index f3a7721..6aebee4 100644 --- a/model/src/main/java/com/lightbend/model/tensorflow/TensorflowModel.java +++ b/model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModel.java @@ -1,10 +1,10 @@ -package com.lightbend.model.tensorflow; +package com.lightbend.model.java.tensorflow; /** * Created by boris on 5/26/17. */ -import com.lightbend.model.Model; +import com.lightbend.model.java.Model; import com.lightbend.model.Modeldescriptor; import com.lightbend.model.Winerecord; import org.tensorflow.Graph; diff --git a/model/src/main/java/com/lightbend/model/tensorflow/TensorflowModelFactory.java b/model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModelFactory.java similarity index 79% rename from model/src/main/java/com/lightbend/model/tensorflow/TensorflowModelFactory.java rename to model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModelFactory.java index d05e2b5..5624bc1 100644 --- a/model/src/main/java/com/lightbend/model/tensorflow/TensorflowModelFactory.java +++ b/model/src/main/java/com/lightbend/model/java/tensorflow/TensorflowModelFactory.java @@ -1,8 +1,8 @@ -package com.lightbend.model.tensorflow; +package com.lightbend.model.java.tensorflow; -import com.lightbend.model.Model; -import com.lightbend.model.CurrentModelDescriptor; -import com.lightbend.model.ModelFactory; +import com.lightbend.model.java.Model; +import com.lightbend.model.java.ModelFactory; +import com.lightbend.modelserver.support.java.ModelToServe; import java.util.Optional; @@ -14,7 +14,7 @@ public class TensorflowModelFactory implements ModelFactory { private static TensorflowModelFactory instance = null; @Override - public Optional create(CurrentModelDescriptor descriptor) { + public Optional create(ModelToServe descriptor) { try{ return Optional.of(new TensorflowModel(descriptor.getModelData())); diff --git a/model/src/main/scala/com/lightbend/model/scala/DataWithModel.scala b/model/src/main/scala/com/lightbend/model/scala/DataWithModel.scala new file mode 100644 index 0000000..e632faf --- /dev/null +++ b/model/src/main/scala/com/lightbend/model/scala/DataWithModel.scala @@ -0,0 +1,20 @@ +package com.lightbend.model.scala + +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.support.scala.ModelToServe + +/** + * Created by boris on 5/8/17. + */ + +case class DataWithModel(model: Option[ModelToServe], data : Option[WineRecord]){ + def isModel : Boolean = model.isDefined + def getModel : ModelToServe = model.get + def getData : WineRecord = data.get + def getDataType : String = { + if(isModel) + getModel.dataType + else + getData.dataType + } +} \ No newline at end of file diff --git a/model/src/main/scala/com/lightbend/model/scala/Model.scala b/model/src/main/scala/com/lightbend/model/scala/Model.scala new file mode 100644 index 0000000..d257228 --- /dev/null +++ b/model/src/main/scala/com/lightbend/model/scala/Model.scala @@ -0,0 +1,12 @@ +package com.lightbend.model.scala + +/** + * Created by boris on 5/9/17. + * Basic trait for model + */ +trait Model { + def score(input : AnyVal) : AnyVal + def cleanup() : Unit + def toBytes() : Array[Byte] + def getType : Long +} \ No newline at end of file diff --git a/model/src/main/scala/com/lightbend/model/scala/ModelFactory.scala b/model/src/main/scala/com/lightbend/model/scala/ModelFactory.scala new file mode 100644 index 0000000..71c4749 --- /dev/null +++ b/model/src/main/scala/com/lightbend/model/scala/ModelFactory.scala @@ -0,0 +1,13 @@ +package com.lightbend.model.scala + +import com.lightbend.modelserver.support.scala.ModelToServe + + +/** + * Created by boris on 5/9/17. + * Basic trait for model factory + */ +trait ModelFactory { + def create(input : ModelToServe) : Option[Model] + def restore(bytes : Array[Byte]) : Model +} \ No newline at end of file diff --git a/model/src/main/scala/com/lightbend/model/scala/PMML/PMMLModel.scala b/model/src/main/scala/com/lightbend/model/scala/PMML/PMMLModel.scala new file mode 100644 index 0000000..8948148 --- /dev/null +++ b/model/src/main/scala/com/lightbend/model/scala/PMML/PMMLModel.scala @@ -0,0 +1,110 @@ +package com.lightbend.model.scala.PMML + +/** + * Created by boris on 5/9/17. + * + * Class for PMML model + */ + +import java.io.{ByteArrayInputStream, ByteArrayOutputStream} + +import com.lightbend.model.scala.{Model, ModelFactory} +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.support.scala.ModelToServe +import org.dmg.pmml.{FieldName, PMML} +import org.jpmml.evaluator.visitors._ +import org.jpmml.evaluator.{Computable, FieldValue, ModelEvaluatorFactory, TargetField} +import org.jpmml.model.PMMLUtil + +import scala.collection.JavaConversions._ +import scala.collection._ + + +class PMMLModel(inputStream: Array[Byte]) extends Model { + + var arguments = mutable.Map[FieldName, FieldValue]() + + // Marshall PMML + val pmml = PMMLUtil.unmarshal(new ByteArrayInputStream(inputStream)) + + // Optimize model// Optimize model + PMMLModel.optimize(pmml) + + // Create and verify evaluator + val evaluator = ModelEvaluatorFactory.newInstance.newModelEvaluator(pmml) + evaluator.verify() + + // Get input/target fields + val inputFields = evaluator.getInputFields + val target: TargetField = evaluator.getTargetFields.get(0) + val tname = target.getName + + override def score(input: AnyVal): AnyVal = { + val inputs = input.asInstanceOf[WineRecord] + arguments.clear() + inputFields.foreach(field => { + arguments.put(field.getName, field.prepare(getValueByName(inputs, field.getName.getValue))) + }) + + // Calculate Output// Calculate Output + val result = evaluator.evaluate(arguments) + + // Prepare output + result.get(tname) match { + case c : Computable => c.getResult.toString.toDouble + case v : Any => v.asInstanceOf[Double] + } + } + + override def cleanup(): Unit = {} + + private def getValueByName(inputs : WineRecord, name: String) : Double = + PMMLModel.names.get(name) match { + case Some(index) => { + val v = inputs.getFieldByNumber(index + 1) + v.asInstanceOf[Double] + } + case _ => .0 + } + + override def toBytes : Array[Byte] = { + var stream = new ByteArrayOutputStream() + PMMLUtil.marshal(pmml, stream) + stream.toByteArray + } + + override def getType: Long = ModelDescriptor.ModelType.PMML.value +} + +object PMMLModel extends ModelFactory { + + private val optimizers = Array(new ExpressionOptimizer, new FieldOptimizer, new PredicateOptimizer, + new GeneralRegressionModelOptimizer, new NaiveBayesModelOptimizer, new RegressionModelOptimizer) + def optimize(pmml : PMML) = this.synchronized { + optimizers.foreach(opt => + try { + opt.applyTo(pmml) + } catch { + case t: Throwable => { + println(s"Error optimizing model for optimizer $opt") + t.printStackTrace() + } + } + ) + } + private val names = Map("fixed acidity" -> 0, + "volatile acidity" -> 1,"citric acid" ->2,"residual sugar" -> 3, + "chlorides" -> 4,"free sulfur dioxide" -> 5,"total sulfur dioxide" -> 6, + "density" -> 7,"pH" -> 8,"sulphates" ->9,"alcohol" -> 10) + + override def create(input: ModelToServe): Option[Model] = { + try { + Some(new PMMLModel(input.model)) + }catch{ + case t: Throwable => None + } + } + + override def restore(bytes: Array[Byte]): Model = new PMMLModel(bytes) +} \ No newline at end of file diff --git a/model/src/main/scala/com/lightbend/model/scala/tensorflow/TensorFlowModel.scala b/model/src/main/scala/com/lightbend/model/scala/tensorflow/TensorFlowModel.scala new file mode 100644 index 0000000..1955b7a --- /dev/null +++ b/model/src/main/scala/com/lightbend/model/scala/tensorflow/TensorFlowModel.scala @@ -0,0 +1,85 @@ +package com.lightbend.model.scala.tensorflow + +import com.lightbend.model.scala.{Model, ModelFactory} +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.winerecord.WineRecord +import com.lightbend.modelserver.support.scala.ModelToServe +import org.tensorflow.{Graph, Session, Tensor} + +/** + * Created by boris on 5/26/17. + * Implementation of tensorflow model + */ + +class TensorFlowModel(inputStream : Array[Byte]) extends Model{ + + val graph = new Graph + graph.importGraphDef(inputStream) + val session = new Session(graph) + + override def score(input: AnyVal): AnyVal = { + + val record = input.asInstanceOf[WineRecord] + val data = Array( + record.fixedAcidity.toFloat, + record.volatileAcidity.toFloat, + record.citricAcid.toFloat, + record.residualSugar.toFloat, + record.chlorides.toFloat, + record.freeSulfurDioxide.toFloat, + record.totalSulfurDioxide.toFloat, + record.density.toFloat, + record.pH.toFloat, + record.sulphates.toFloat, + record.alcohol.toFloat + ) + val modelInput = Tensor.create(Array(data)) + val result = session.runner.feed("dense_1_input", modelInput).fetch("dense_3/Sigmoid").run().get(0) + val rshape = result.shape + var rMatrix = Array.ofDim[Float](rshape(0).asInstanceOf[Int],rshape(1).asInstanceOf[Int]) + result.copyTo(rMatrix) + var value = (0, rMatrix(0)(0)) + 1 to (rshape(1).asInstanceOf[Int] -1) foreach{i => { + if(rMatrix(0)(i) > value._2) + value = (i, rMatrix(0)(i)) + }} + value._1.toDouble + } + + override def cleanup(): Unit = { + try{ + session.close + }catch { + case t: Throwable => // Swallow + } + try{ + graph.close + }catch { + case t: Throwable => // Swallow + } + } + + override def toBytes(): Array[Byte] = graph.toGraphDef + + override def getType: Long = ModelDescriptor.ModelType.TENSORFLOW.value +} + +object TensorFlowModel extends ModelFactory { + def apply(inputStream: Array[Byte]): Option[TensorFlowModel] = { + try { + Some(new TensorFlowModel(inputStream)) + }catch{ + case t: Throwable => None + } + } + + override def create(input: ModelToServe): Option[Model] = { + try { + Some(new TensorFlowModel(input.model)) + }catch{ + case t: Throwable => None + } + } + + override def restore(bytes: Array[Byte]): Model = new TensorFlowModel(bytes) +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 5fafd02..88002ce 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -9,8 +9,9 @@ object Dependencies { val akkaStream = "com.typesafe.akka" % "akka-stream_2.11" % akkaVersion val akkaHttp = "com.typesafe.akka" % "akka-http_2.11" % akkaHttpVersion - val akkaHttpJsonJackson = "de.heikoseeberger" % "akka-http-jackson_2.11" % akkaHttpJsonVersion - + val akkaHttpJsonJackson = "de.heikoseeberger" % "akka-http-jackson_2.11" % akkaHttpJsonVersion // exclude("com.fasterxml.jackson.module","jackson-module-scala_2.11") + val akkaslf = "com.typesafe.akka" % "akka-slf4j_2.11" % akkaSlfVersion + val kafka = "org.apache.kafka" % "kafka_2.11" % kafkaVersion val kafkaclients = "org.apache.kafka" % "kafka-clients" % kafkaVersion @@ -26,14 +27,39 @@ object Dependencies { val wsrs = "javax.ws.rs" % "javax.ws.rs-api" % wsrsVersion val tensorflow = "org.tensorflow" % "tensorflow" % tensorflowVersion + val tensorflowProto="org.tensorflow" % "proto" % tensorflowVersion val jpmml = "org.jpmml" % "pmml-evaluator" % PMMLVersion val jpmmlextras = "org.jpmml" % "pmml-evaluator-extension" % PMMLVersion + val flinkScala = "org.apache.flink" % "flink-scala_2.11" % flinkVersion + val flinkStreaming= "org.apache.flink" % "flink-streaming-scala_2.11" % flinkVersion + val flinkKafka = "org.apache.flink" % "flink-connector-kafka-0.10_2.11" % flinkVersion + + val joda = "joda-time" % "joda-time" % jodaVersion + + val kryo = "com.esotericsoftware.kryo" % "kryo" % kryoVersion + + val sparkcore = "org.apache.spark" % "spark-core_2.11" % sparkVersion + val sparkstreaming= "org.apache.spark" % "spark-streaming_2.11" % sparkVersion + val sparkkafka = "org.apache.spark" % "spark-streaming-kafka-0-10_2.11"% sparkVersion + + val scopt = "com.github.scopt" % "scopt_2.11" % scoptVersion + val sparkML = "org.apache.spark" % "spark-mllib_2.11" % sparkVersion + val sparkJPMML = "org.jpmml" % "jpmml-sparkml" % sparkPMMLVersion + + val modelsDependencies = Seq(jpmml, jpmmlextras, tensorflow) val kafkabaseDependencies = Seq(reactiveKafka) ++ Seq(kafka, kafkaclients) val kafkaDependencies = Seq(reactiveKafka) ++ Seq(kafka, kafkaclients, kafkastreams) val webDependencies = Seq(gson, jersey, jerseymedia, jettyserver, jettyservlet, wsrs) val akkaServerDependencies = Seq(reactiveKafka) ++ Seq(akkaStream, akkaHttp, akkaHttpJsonJackson, reactiveKafka) + val flinkDependencies = Seq(flinkScala, flinkStreaming, flinkKafka) + + val sparkDependencies = Seq(sparkcore, sparkstreaming, sparkkafka) + + val sparkMLDependencies = Seq(sparkML, sparkJPMML, scopt) + + } diff --git a/project/Versions.scala b/project/Versions.scala index c03a61c..10ee29f 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -2,8 +2,9 @@ * Created by boris on 7/14/17. */ object Versions { - val reactiveKafkaVersion = "0.16" + val reactiveKafkaVersion = "0.16" val akkaVersion = "2.4.19" + val akkaSlfVersion = "2.3.2" // older version to align with Flink val akkaHttpVersion = "10.0.9" val akkaHttpJsonVersion = "1.17.0" @@ -16,4 +17,17 @@ object Versions { val jerseyVersion = "2.25" val gsonVersion = "2.6.2" val wsrsVersion = "2.0.1" + + val flinkVersion = "1.3.1" + val jodaVersion = "2.9.7" + + val kryoVersion = "2.24.0" + val sparkVersion = "2.2.0" + + val scoptVersion = "3.5.0" + val sparkPMMLVersion = "1.1.7" + + val beamVersion = "2.0.0" + + } diff --git a/servingsamples/src/main/scala/com/lightbend/jpmml/WineQualityRandomForestClassifier.scala b/servingsamples/src/main/scala/com/lightbend/jpmml/WineQualityRandomForestClassifier.scala new file mode 100644 index 0000000..3115b84 --- /dev/null +++ b/servingsamples/src/main/scala/com/lightbend/jpmml/WineQualityRandomForestClassifier.scala @@ -0,0 +1,129 @@ +package com.lightbend.jpmml + +import java.io.{FileInputStream, InputStream} + +import org.dmg.pmml.{FieldName, PMML} +import org.jpmml.evaluator.visitors.{ExpressionOptimizer, _} +import org.jpmml.evaluator.{Computable, FieldValue, ModelEvaluatorFactory, TargetField} +import org.jpmml.model.PMMLUtil + +import scala.collection.JavaConversions._ +import scala.collection._ +import scala.io.Source + +/** + * Created by boris on 5/2/17. + * Test of JPMML evaluator for the model created by DecisionTreeClassificator in SparkML and exported + * using JPMML Spark https://github.com/jpmml/jpmml-spark + * Implementation is based on JPMML example at + * https://github.com/jpmml/jpmml-evaluator/blob/master/pmml-evaluator-example/src/main/java/org/jpmml/evaluator/EvaluationExample.java + */ + + +class WineQualityRandomForestClassifier(path : String) { + import WineQualityRandomForestClassifier._ + + var arguments = mutable.Map[FieldName, FieldValue]() + + // constructor + val pmml: PMML = readPMML(path) + optimize(pmml) + + // Create and verify evaluator + val evaluator = ModelEvaluatorFactory.newInstance().newModelEvaluator(pmml) + evaluator.verify() + + // Get input/target fields + val inputFields = evaluator.getInputFields + val target: TargetField = evaluator.getTargetFields.get(0) + val tname = target.getName + + def score(record : Array[Float]) : Double = { + arguments.clear() + inputFields.foreach(field => { + arguments.put(field.getName, field.prepare(getValueByName(record, field.getName.getValue))) + }) + + // Calculate Output// Calculate Output + val result = evaluator.evaluate(arguments) + + // Prepare output + result.get(tname) match { + case c : Computable => c.getResult.toString.toDouble + case v : Any => v.asInstanceOf[Double] + } + } + + private def getValueByName(inputs : Array[Float], name: String) : Double = { + names.get(name) match { + case Some(index) => { + val v = inputs(index) + v.asInstanceOf[Double] + } + case _ =>.0 + } + } +} + +object WineQualityRandomForestClassifier { + + def main(args: Array[String]): Unit = { + val model_path = "data/winequalityRandonForrestClassification.pmml" // model + val data_path = "data/winequality_red.csv" // data + val lmodel = new WineQualityRandomForestClassifier(model_path) + + val inputs = getListOfRecords(data_path) + inputs.foreach(record => + println(s"result ${lmodel.score(record._1)} expected ${record._2}")) + } + + def readPMML(file: String): PMML = { + var is = null.asInstanceOf[InputStream] + try { + is = new FileInputStream(file) + PMMLUtil.unmarshal(is) + } + finally if (is != null) is.close() + } + + private val optimizers = Array(new ExpressionOptimizer, new FieldOptimizer, new PredicateOptimizer, + new GeneralRegressionModelOptimizer, new NaiveBayesModelOptimizer, new RegressionModelOptimizer) + + def optimize(pmml : PMML) = this.synchronized { + optimizers.foreach(opt => + try { + opt.applyTo(pmml) + } catch { + case t: Throwable => { + println(s"Error optimizing model for optimizer $opt") + t.printStackTrace() + } + } + ) + } + + def getListOfRecords(file: String): Seq[(Array[Float], Float)] = { + + var result = Seq.empty[(Array[Float], Float)] + val bufferedSource = Source.fromFile(file) + var current = 0 + for (line <- bufferedSource.getLines) { + if (current == 0) + current = 1 + else { + val cols = line.split(";").map(_.trim) + val record = Array( + cols(0).toFloat, cols(1).toFloat, cols(2).toFloat, cols(3).toFloat, cols(4).toFloat, + cols(5).toFloat, cols(6).toFloat, cols(7).toFloat, cols(8).toFloat, cols(9).toFloat, cols(10).toFloat) + result = (record, cols(11).toFloat) +: result + } + } + bufferedSource.close + result + } + + private val names = Map("fixed acidity" -> 0, + "volatile acidity" -> 1,"citric acid" ->2,"residual sugar" -> 3, + "chlorides" -> 4,"free sulfur dioxide" -> 5,"total sulfur dioxide" -> 6, + "density" -> 7,"pH" -> 8,"sulphates" ->9,"alcohol" -> 10) +} diff --git a/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServing.scala b/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServing.scala new file mode 100644 index 0000000..4799724 --- /dev/null +++ b/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServing.scala @@ -0,0 +1,77 @@ +package com.lightbend.tensorflow + +import java.nio.file.{Files, Path, Paths} +import scala.io.Source +import org.tensorflow.{Graph, Session, Tensor} + +/** + * Created by boris on 5/25/17. + */ +class WineModelServing(path : String) { + import WineModelServing._ + + // Constructor + println(s"Loading saved model from $path") + val lg = readGraph(Paths.get (path)) + val ls = new Session (lg) + println("Model Loading complete") + + def score(record : Array[Float]) : Double = { + val input = Tensor.create(Array(record)) + val result = ls.runner.feed("dense_1_input", input).fetch("dense_3/Sigmoid").run().get(0) + val rshape = result.shape + var rMatrix = Array.ofDim[Float](rshape(0).asInstanceOf[Int],rshape(1).asInstanceOf[Int]) + result.copyTo(rMatrix) + var value = (0, rMatrix(0)(0)) + 1 to (rshape(1).asInstanceOf[Int] -1) foreach{i => { + if(rMatrix(0)(i) > value._2) + value = (i, rMatrix(0)(i)) + }} + value._1.toDouble + } + + def cleanup() : Unit = { + ls.close + } +} + +object WineModelServing{ + def main(args: Array[String]): Unit = { + val model_path = "data/optimized_WineQuality.pb" // model + val data_path = "data/winequality_red.csv" // data + + val lmodel = new WineModelServing(model_path) + val inputs = getListOfRecords(data_path) + inputs.foreach(record => + println(s"result ${lmodel.score(record._1)} expected ${record._2}")) + lmodel.cleanup() + } + + private def readGraph(path: Path) : Graph = { + try { + val graphData = Files.readAllBytes(path) + val g = new Graph + g.importGraphDef(graphData) + g + } catch { + case e: Throwable => + println("Failed to read graph [" + path + "]: " + e.getMessage) + System.exit(1) + null.asInstanceOf[Graph] + } + } + + def getListOfRecords(file: String): Seq[(Array[Float], Float)] = { + + var result = Seq.empty[(Array[Float], Float)] + val bufferedSource = Source.fromFile(file) + try for (line <- bufferedSource.getLines) { + val cols = line.split(";").map(_.trim) + val record = cols.take(11).map(_.toFloat) + result = (record, cols(11).toFloat) +: result + } finally + bufferedSource.close + result + } + +} \ No newline at end of file diff --git a/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServingBundle.scala b/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServingBundle.scala new file mode 100644 index 0000000..ac1a7ff --- /dev/null +++ b/servingsamples/src/main/scala/com/lightbend/tensorflow/WineModelServingBundle.scala @@ -0,0 +1,113 @@ +package com.lightbend.tensorflow + +/** + * Created by boris on 5/26/17. + */ + + +import org.tensorflow.{SavedModelBundle, Session, Tensor} +import org.tensorflow.framework.{MetaGraphDef, SignatureDef, TensorInfo, TensorShapeProto} + +import scala.collection.mutable._ +import scala.collection.JavaConverters._ +import scala.io.Source + +object WineModelServingBundle { + + def apply(path: String, label: String): WineModelServingBundle = new WineModelServingBundle(path, label) + def main(args: Array[String]): Unit = { + val data_path = "data/winequality_red.csv" // data + val saved_model_path = "data/WineQuality" // Saved model directory + val label = "serve" + val model = WineModelServingBundle(saved_model_path, label) + val inputs = getListOfRecords(data_path) + inputs.foreach(record => + println(s"result ${model.score(record._1)} expected ${record._2}")) + model.cleanup() + } + + def getListOfRecords(file: String): Seq[(Array[Float], Float)] = { + + var result = Seq.empty[(Array[Float], Float)] + val bufferedSource = Source.fromFile(file) + for (line <- bufferedSource.getLines) { + val cols = line.split(";").map(_.trim) + val record = cols.take(11).map(_.toFloat) + result = (record, cols(11).toFloat) +: result + } + bufferedSource.close + result + } +} + +class WineModelServingBundle(path : String, label : String){ + // Constructor + + println(s"Loading saved model from $path with label $label") + val bundle = SavedModelBundle.load(path, label) + val ls: Session = bundle.session + val metaGraphDef = MetaGraphDef.parseFrom(bundle.metaGraphDef()) + val signatures = parseSignature(metaGraphDef.getSignatureDefMap.asScala) + println("Model Loading complete") + + def score(record : Array[Float]) : Double = { + val input = Tensor.create(Array(record)) + val result = ls.runner.feed(signatures(0).inputs(0).name, input).fetch(signatures(0).outputs(0).name).run().get(0) + val rshape = result.shape + var rMatrix = Array.ofDim[Float](rshape(0).asInstanceOf[Int],rshape(1).asInstanceOf[Int]) + result.copyTo(rMatrix) + var value = (0, rMatrix(0)(0)) + 1 to (rshape(1).asInstanceOf[Int] -1) foreach{i => { + if(rMatrix(0)(i) > value._2) + value = (i, rMatrix(0)(i)) + }} + value._1.toDouble + } + + def cleanup() : Unit = { + ls.close + } + + def convertParameters(tensorInfo: Map[String,TensorInfo]) : Seq[Parameter] = { + + var parameters = Seq.empty[Parameter] + tensorInfo.foreach(input => { + val fields = input._2.getAllFields.asScala + var name = "" + var dtype = "" + var shape = Seq.empty[Int] + fields.foreach(descriptor => { + if(descriptor._1.getName.contains("shape") ){ + descriptor._2.asInstanceOf[TensorShapeProto].getDimList.toArray.map(d => + d.asInstanceOf[TensorShapeProto.Dim].getSize).toSeq.foreach(v => shape = shape :+ v.toInt) + + } + if(descriptor._1.getName.contains("name") ) { + name = descriptor._2.toString.split(":")(0) + } + if(descriptor._1.getName.contains("dtype") ) { + dtype = descriptor._2.toString + } + }) + parameters = Parameter(name, dtype, shape) +: parameters + }) + parameters + } + + def parseSignature(signatureMap : Map[String, SignatureDef]) : Seq[Signature] = { + + var signatures = Seq.empty[Signature] + signatureMap.foreach(definition => { + val inputDefs = definition._2.getInputsMap.asScala + val outputDefs = definition._2.getOutputsMap.asScala + val inputs = convertParameters(inputDefs) + val outputs = convertParameters(outputDefs) + signatures = Signature(definition._1, inputs, outputs) +: signatures + }) + signatures + } +} + +case class Parameter(name : String, dtype: String, dimensions: Seq[Int] = Seq.empty[Int]){} + +case class Signature(name : String, inputs: Seq[Parameter], outputs: Seq[Parameter]){} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeClassifier.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeClassifier.scala new file mode 100644 index 0000000..695a796 --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeClassifier.scala @@ -0,0 +1,98 @@ +package com.lightbend.spark.ml + +/** + * Created by boris on 5/1/17. + * + * Decision tree learning uses a decision tree as a predictive model observations about an item (represented in the + * branches) to conclusions about the item's target value (represented in the leaves). It is one of the predictive + * modelling approaches used in statistics, data mining and machine learning. Tree models where the target variable + * can take a finite set of values are called classification trees; in these tree structures, leaves represent class + * labels and branches represent conjunctions of features that lead to those class labels. Decision trees where the + * target variable can take continuous values (typically real numbers) are called regression trees. + * + */ + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.classification.{DecisionTreeClassificationModel, DecisionTreeClassifier} +import org.apache.spark.ml.feature.{IndexToString, StringIndexer, VectorAssembler} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions._ +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +object WineQualityDecisionTreeClassifier { + + def main(args: Array[String]): Unit = { + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeClassifierPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toDouble = udf[Double, String]( _.toDouble) + val dff = df. + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + + withColumn("citric acid", toDouble(df("citric acid"))). // 2 - + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + + withColumn("chlorides", toDouble(df("chlorides"))). // 4 - + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + + withColumn("density", toDouble(df("density"))). // 7 - + withColumn("pH", toDouble(df("pH"))). // 8 + + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + + + // Decision Tree operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + // Fit on whole dataset to include all labels in index. + val labelIndexer = new StringIndexer() + .setInputCol("quality") + .setOutputCol("indexedLabel") + .fit(dff) + + // Train a DecisionTree model. + val dt = new DecisionTreeClassifier() + .setLabelCol("indexedLabel") + .setFeaturesCol("features") + + // Convert indexed labels back to original labels. + val labelConverter = new IndexToString() + .setInputCol("prediction") + .setOutputCol("predictedLabel") + .setLabels(labelIndexer.labels) + + // create pileline + val pipeline = new Pipeline() + .setStages(Array(assembler, labelIndexer, dt, labelConverter)) + + // Train model + val model = pipeline.fit(dff) + + // Print results + val treeModel = model.stages(2).asInstanceOf[DecisionTreeClassificationModel] + println("Learned classification tree model:\n" + treeModel.toDebugString) + + // PMML + val schema = dff.schema + val pmml = ConverterUtil.toPMML(schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeRegressor.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeRegressor.scala new file mode 100644 index 0000000..0fe4c2d --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityDecisionTreeRegressor.scala @@ -0,0 +1,90 @@ +package com.lightbend.spark.ml + +/** + * Created by boris on 5/1/17. + * + * Decision tree learning uses a decision tree as a predictive model which maps observations about an item (represented in the + * branches) to conclusions about the item's target value (represented in the leaves). It is one of the predictive modelling + * approaches used in statistics, data mining and machine learning. Tree models where the target variable can take a finite set of + * values are called classification trees; in these tree structures, leaves represent class labels and branches represent + * conjunctions of features that lead to those class labels. Decision trees where the target variable can take continuous values + * (typically real numbers) are called regression trees. + * In decision analysis, a decision tree can be used to visually and explicitly represent decisions and decision making. In data + * mining, a decision tree describes data (but the resulting classification tree can be an input for decision making). This page + * deals with decision trees in data mining. + */ + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.feature.VectorAssembler +import org.apache.spark.ml.regression.{DecisionTreeRegressionModel, DecisionTreeRegressor} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions._ +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +object WineQualityDecisionTreeRegressor { + + def main(args: Array[String]): Unit = { + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeRegressorPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toInt = udf[Int, String]( _.toInt) + val toDouble = udf[Double, String]( _.toDouble) + val dff = df. + withColumn("quality", toInt(df("quality"))). + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + + withColumn("citric acid", toDouble(df("citric acid"))). // 2 - + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + + withColumn("chlorides", toDouble(df("chlorides"))). // 4 - + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + + withColumn("density", toDouble(df("density"))). // 7 - + withColumn("pH", toDouble(df("pH"))). // 8 + + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + + + // Decision Tree operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + // Train a DecisionTree model. + val dt = new DecisionTreeRegressor() + .setLabelCol("quality") + .setFeaturesCol("features") + + // create pileline + val pipeline = new Pipeline() + .setStages(Array(assembler, dt)) + + // Train model + val model = pipeline.fit(dff) + + // Print results + val lrModel = model.stages(1).asInstanceOf[DecisionTreeRegressionModel] + println("Learned regression tree model:\n" + lrModel.toDebugString) + + // PMML + val schema = dff.schema + val pmml = ConverterUtil.toPMML(schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityPerceptron.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityPerceptron.scala new file mode 100644 index 0000000..8ff7b78 --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityPerceptron.scala @@ -0,0 +1,107 @@ +package com.lightbend.spark.ml + +/** + * Created by boris on 5/1/17. + * + * A multilayer perceptron (MLP) is a feedforward artificial neural network model that maps sets of input data + * onto a set of appropriate outputs. An MLP consists of multiple layers of nodes in a directed graph, with each + * layer fully connected to the next one. Except for the input nodes, each node is a neuron (or processing element) + * with a nonlinear activation function. MLP utilizes a supervised learning technique called backpropagation for + * training the network. MLP is a modification of the standard linear perceptron and can distinguish data that is + * not linearly separable. + */ + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.classification.{MultilayerPerceptronClassificationModel, MultilayerPerceptronClassifier} +import org.apache.spark.ml.feature.{IndexToString, StringIndexer, VectorAssembler} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions._ +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +object WineQualityPerceptronPMML { + + def main(args: Array[String]): Unit = { + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeRegressorPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toDouble = udf[Double, String]( _.toDouble) + val dff = df. + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + + withColumn("citric acid", toDouble(df("citric acid"))). // 2 - + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + + withColumn("chlorides", toDouble(df("chlorides"))). // 4 - + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + + withColumn("density", toDouble(df("density"))). // 7 - + withColumn("pH", toDouble(df("pH"))). // 8 + + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + + + // Decision Tree operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + // Fit on whole dataset to include all labels in index. + val labelIndexer = new StringIndexer() + .setInputCol("quality") + .setOutputCol("indexedLabel") + .fit(dff) + + // specify layers for the neural network: + // input layer of size 11 (features), two intermediate of size 10 and 20 + // and output of size 6 (classes) + + val layers = Array[Int](11, 10, 20, 6) + + // Train a DecisionTree model. + val dt = new MultilayerPerceptronClassifier() + .setLayers(layers) + .setBlockSize(128) + .setSeed(1234L) + .setMaxIter(100) + .setLabelCol("indexedLabel") + .setFeaturesCol("features") + + // Convert indexed labels back to original labels. + val labelConverter = new IndexToString() + .setInputCol("prediction") + .setOutputCol("predictedLabel") + .setLabels(labelIndexer.labels) + + // create pileline + val pipeline = new Pipeline() + .setStages(Array(assembler, labelIndexer, dt, labelConverter)) + + // Train model + val model = pipeline.fit(dff) + + // Print results + val lrModel = model.stages(2).asInstanceOf[MultilayerPerceptronClassificationModel] + println(s"Coefficients: \n${lrModel}") + + // PMML + val schema = dff.schema + val pmml = ConverterUtil.toPMML(schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityRandomForrestClassifier.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityRandomForrestClassifier.scala new file mode 100644 index 0000000..3970148 --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WineQualityRandomForrestClassifier.scala @@ -0,0 +1,96 @@ +package com.lightbend.spark.ml + +/** + * Created by boris on 5/1/17. + * + * Random forests or random decision forests are an ensemble learning method for classification, regression and + * other tasks, that operate by constructing a multitude of decision trees at training time and outputting the + * class that is the mode of the classes (classification) or mean prediction (regression) of the individual trees. + * Random decision forests correct for decision trees' habit of overfitting to their training set. + */ + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.classification.{RandomForestClassificationModel, RandomForestClassifier} +import org.apache.spark.ml.feature.{IndexToString, StringIndexer, VectorAssembler} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions._ +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +object WineQualityRandomForrestClassifierPMML { + + def main(args: Array[String]): Unit = { + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeClassifierPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toDouble = udf[Double, String]( _.toDouble) + val dff = df. + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + + withColumn("citric acid", toDouble(df("citric acid"))). // 2 - + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + + withColumn("chlorides", toDouble(df("chlorides"))). // 4 - + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + + withColumn("density", toDouble(df("density"))). // 7 - + withColumn("pH", toDouble(df("pH"))). // 8 + + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + + + // Decision Tree operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + // Fit on whole dataset to include all labels in index. + val labelIndexer = new StringIndexer() + .setInputCol("quality") + .setOutputCol("indexedLabel") + .fit(dff) + + // Train a DecisionTree model. + val dt = new RandomForestClassifier() + .setLabelCol("indexedLabel") + .setFeaturesCol("features") + .setNumTrees(10) + + // Convert indexed labels back to original labels. + val labelConverter = new IndexToString() + .setInputCol("prediction") + .setOutputCol("predictedLabel") + .setLabels(labelIndexer.labels) + + // create pileline + val pipeline = new Pipeline() + .setStages(Array(assembler, labelIndexer, dt, labelConverter)) + + // Train model + val model = pipeline.fit(dff) + + // Print results + val treeModel = model.stages(2).asInstanceOf[RandomForestClassificationModel] + println("Learned classification tree model:\n" + treeModel.toDebugString) + + // PMML + val schema = dff.schema + val pmml = ConverterUtil.toPMML(schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityGeneralizedLinearRegression.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityGeneralizedLinearRegression.scala new file mode 100644 index 0000000..fa93f13 --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityGeneralizedLinearRegression.scala @@ -0,0 +1,111 @@ +package com.lightbend.spark.ml + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.feature.VectorAssembler +import org.apache.spark.ml.regression.{GeneralizedLinearRegression, GeneralizedLinearRegressionModel} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions.udf +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +/** + * Created by boris on 5/3/17. + * + * Contrasted with linear regression where the output is assumed to follow a Gaussian distribution, generalized + * linear models (GLMs) are specifications of linear models where the response variable YiYi follows some + * distribution from the exponential family of distributions. Spark’s GeneralizedLinearRegression interface allows + * for flexible specification of GLMs which can be used for various types of prediction problems including linear + * regression, Poisson regression, logistic regression, and others. Currently in spark.ml, only a following subset + * of the exponential family distributions are supported: + * Family Response Type Supported Links + * Gaussian Continuous Identity*, Log, Inverse + * Binomial Binary Logit*, Probit, CLogLog + * Poisson Count Log*, Identity, Sqrt + * Gamma Continuous Inverse*, Idenity, Log + */ + +object WinequalityGeneralizedLinearRegression { + + def main(args: Array[String]): Unit = { + + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeClassifierPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toDouble = udf[Double, String](_.toDouble) + val dff = df. + withColumn("quality", toDouble(df("quality"))). + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + withColumn("citric acid", toDouble(df("citric acid"))). // 2 + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + withColumn("chlorides", toDouble(df("chlorides"))). // 4 + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + withColumn("density", toDouble(df("density"))). // 7 + withColumn("pH", toDouble(df("pH"))). // 8 + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + // Regression operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + val lr = new GeneralizedLinearRegression() +// .setFamily("gaussian") + .setFamily("gamma") + .setLink("identity") + .setMaxIter(100) + .setRegParam(0.3) + .setFeaturesCol("features") + .setLabelCol("quality") + + // create pileline + val pipeline = new Pipeline().setStages(Array(assembler,lr)) + + // Fit the model + // val lrModel = lr.fit(dataFrame) + val model = pipeline.fit(dff) + val lrModel = model.stages(1).asInstanceOf[GeneralizedLinearRegressionModel] + + + // Summarize the model over the training set and print out some metrics + // Print the coefficients and intercept for generalized linear regression model + println(s"Coefficients: ${lrModel.coefficients}") + println(s"Intercept: ${lrModel.intercept}") + + // Summarize the model over the training set and print out some metrics + val summary = lrModel.summary + println(s"Coefficient Standard Errors: ${summary.coefficientStandardErrors.mkString(",")}") + println(s"T Values: ${summary.tValues.mkString(",")}") + println(s"P Values: ${summary.pValues.mkString(",")}") + println(s"Dispersion: ${summary.dispersion}") + println(s"Null Deviance: ${summary.nullDeviance}") + println(s"Residual Degree Of Freedom Null: ${summary.residualDegreeOfFreedomNull}") + println(s"Deviance: ${summary.deviance}") + println(s"Residual Degree Of Freedom: ${summary.residualDegreeOfFreedom}") + println(s"AIC: ${summary.aic}") + println("Deviance Residuals: ") + summary.residuals().show() + + // PMML + val pmml = ConverterUtil.toPMML(dff.schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityLinearRegression.scala b/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityLinearRegression.scala new file mode 100644 index 0000000..e92ac2d --- /dev/null +++ b/sparkML/src/main/scala/com/lightbend/spark/ml/WinequalityLinearRegression.scala @@ -0,0 +1,90 @@ +package com.lightbend.spark.ml + +import org.apache.spark.ml.Pipeline +import org.apache.spark.ml.feature.VectorAssembler +import org.apache.spark.ml.regression.{LinearRegression, LinearRegressionModel} +import org.apache.spark.sql.SparkSession +import org.apache.spark.sql.functions.udf +import org.jpmml.model.MetroJAXBUtil +import org.jpmml.sparkml.ConverterUtil + + +/** + * Created by boris on 5/3/17. + * Ordinary Linear regression + * + */ + +object WinequalityLinearRegression { + + def main(args: Array[String]): Unit = { + + val spark = SparkSession + .builder + .appName("WineQualityDecisionTreeClassifierPMML") + .master("local") + .getOrCreate() + + // Load and parse the data file. + val df = spark.read + .format("csv") + .option("header", "true") + .option("mode", "DROPMALFORMED") + .option("delimiter", ";") + .load("data/winequality_red_names.csv") + val inputFields = List("fixed acidity", "volatile acidity", "citric acid", "residual sugar", "chlorides", + "free sulfur dioxide", "total sulfur dioxide", "density", "pH", "sulphates", "alcohol") + + // CSV imports everything as Strings, fix the type + val toDouble = udf[Double, String](_.toDouble) + val dff = df. + withColumn("quality", toDouble(df("quality"))). + withColumn("fixed acidity", toDouble(df("fixed acidity"))). // 0 + withColumn("volatile acidity", toDouble(df("volatile acidity"))). // 1 + withColumn("citric acid", toDouble(df("citric acid"))). // 2 + withColumn("residual sugar", toDouble(df("residual sugar"))). // 3 + withColumn("chlorides", toDouble(df("chlorides"))). // 4 + withColumn("free sulfur dioxide", toDouble(df("free sulfur dioxide"))). // 5 + withColumn("total sulfur dioxide", toDouble(df("total sulfur dioxide"))). // 6 + withColumn("density", toDouble(df("density"))). // 7 + withColumn("pH", toDouble(df("pH"))). // 8 + withColumn("sulphates", toDouble(df("sulphates"))). // 9 + withColumn("alcohol", toDouble(df("alcohol"))) // 10 + + // Regression operates on feature vectors not individual features, so convert to DF again + val assembler = new VectorAssembler(). + setInputCols(inputFields.toArray). + setOutputCol("features") + + val lr = new LinearRegression() + .setMaxIter(50) + .setRegParam(0.1) + .setElasticNetParam(0.5) + .setFeaturesCol("features") + .setLabelCol("quality") + + // create pileline + val pipeline = new Pipeline().setStages(Array(assembler,lr)) + + // Fit the model + // val lrModel = lr.fit(dataFrame) + val model = pipeline.fit(dff) + val lrModel = model.stages(1).asInstanceOf[LinearRegressionModel] + + // Print the coefficients and intercept for linear regression + println(s"Coefficients: ${lrModel.coefficients} Intercept: ${lrModel.intercept}") + + // Summarize the model over the training set and print out some metrics + val trainingSummary = lrModel.summary + println(s"numIterations: ${trainingSummary.totalIterations}") + println(s"objectiveHistory: [${trainingSummary.objectiveHistory.mkString(",")}]") + trainingSummary.residuals.show() + println(s"RMSE: ${trainingSummary.rootMeanSquaredError}") + println(s"r2: ${trainingSummary.r2}") + + // PMML + val pmml = ConverterUtil.toPMML(dff.schema, model) + MetroJAXBUtil.marshalPMML(pmml, System.out) + spark.stop() + } +} \ No newline at end of file diff --git a/sparkserver/src/main/scala/com/lightbend/modelserver/DataRecord.scala b/sparkserver/src/main/scala/com/lightbend/modelserver/DataRecord.scala new file mode 100644 index 0000000..dad1bf4 --- /dev/null +++ b/sparkserver/src/main/scala/com/lightbend/modelserver/DataRecord.scala @@ -0,0 +1,16 @@ +package com.lightbend.modelserver + +import scala.util.Try +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.winerecord.WineRecord + +/** + * Created by boris on 5/8/17. + */ + +object DataRecord { + + def fromByteArray(message: Array[Byte]): Try[WineRecord] = Try { + WineRecord.parseFrom(message) + } +} diff --git a/sparkserver/src/main/scala/com/lightbend/modelserver/ModelSerializerKryo.scala b/sparkserver/src/main/scala/com/lightbend/modelserver/ModelSerializerKryo.scala new file mode 100644 index 0000000..ea17f6b --- /dev/null +++ b/sparkserver/src/main/scala/com/lightbend/modelserver/ModelSerializerKryo.scala @@ -0,0 +1,67 @@ +package com.lightbend.modelserver + +/** + * Created by boris on 6/2/17. + */ + +import com.esotericsoftware.kryo.io.{Input, Output} +import com.esotericsoftware.kryo.{Kryo, Serializer} +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.model.scala.Model +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import org.apache.spark.serializer.KryoRegistrator + + +class ModelSerializerKryo extends Serializer[Model]{ + + super.setAcceptsNull(false) + super.setImmutable(true) + + /** Reads bytes and returns a new object of the specified concrete type. + *

+ * Before Kryo can be used to read child objects, {@link Kryo#reference(Object)} must be called with the parent object to + * ensure it can be referenced by the child objects. Any serializer that uses {@link Kryo} to read a child object may need to + * be reentrant. + *

+ * This method should not be called directly, instead this serializer can be passed to {@link Kryo} read methods that accept a + * serialier. + * + * @return May be null if { @link #getAcceptsNull()} is true. */ + + override def read(kryo: Kryo, input: Input, `type`: Class[Model]): Model = { + import ModelSerializerKryo._ + + println("KRYO deserialization") + val mType = input.readLong().asInstanceOf[Int] + val bytes = Stream.continually(input.readByte()).takeWhile(_ != -1).toArray + factories.get(mType) match { + case Some(factory) => factory.restore(bytes) + case _ => throw new Exception(s"Unknown model type $mType to restore") + } + } + + /** Writes the bytes for the object to the output. + *

+ * This method should not be called directly, instead this serializer can be passed to {@link Kryo} write methods that accept a + * serialier. + * + * @param value May be null if { @link #getAcceptsNull()} is true. */ + + override def write(kryo: Kryo, output: Output, value: Model): Unit = { + println("KRYO serialization") + output.writeLong(value.getType) + output.write(value.toBytes) + } +} + +object ModelSerializerKryo{ + private val factories = Map(ModelDescriptor.ModelType.PMML.value -> PMMLModel, + ModelDescriptor.ModelType.TENSORFLOW.value -> TensorFlowModel) +} + +class ModelRegistrator extends KryoRegistrator { + override def registerClasses(kryo: Kryo) { + kryo.register(classOf[Model], new ModelSerializerKryo()) + } +} diff --git a/sparkserver/src/main/scala/com/lightbend/modelserver/SparkModelServer.scala b/sparkserver/src/main/scala/com/lightbend/modelserver/SparkModelServer.scala new file mode 100644 index 0000000..19e1762 --- /dev/null +++ b/sparkserver/src/main/scala/com/lightbend/modelserver/SparkModelServer.scala @@ -0,0 +1,91 @@ +package com.lightbend.modelserver + +/** + * Created by boris on 6/26/17. + */ + +import com.lightbend.configuration.kafka.ApplicationKafkaParameters +import com.lightbend.model.modeldescriptor.ModelDescriptor +import com.lightbend.modelserver.kafka.KafkaSupport +import com.lightbend.model.scala.PMML.PMMLModel +import com.lightbend.model.scala.tensorflow.TensorFlowModel +import com.lightbend.model.scala.{DataWithModel, Model} +import com.lightbend.modelserver.support.scala.ModelToServe +import org.apache.spark.streaming.kafka010._ +import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent +import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe +import org.apache.spark.streaming._ +import org.apache.spark.SparkConf + + +object SparkModelServer { + + private val factories = Map(ModelDescriptor.ModelType.PMML.value -> PMMLModel, + ModelDescriptor.ModelType.TENSORFLOW.value -> TensorFlowModel) + + def main(args: Array[String]): Unit = { + // Create context + val sparkConf = new SparkConf() + .setAppName("SparkModelServer") + .setMaster("local") + sparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") + sparkConf.set("spark.kryo.registrator", "com.lightbend.modelserver.ModelRegistrator") +// sparkConf.set("spark.kryo.registrator", "com.lightbend.modelServer.model.TensorFlowModelRegistrator") + val ssc = new StreamingContext(sparkConf, Seconds(1)) + ssc.checkpoint("./cpt") + + // Initial state RDD for current models + val modelsRDD = ssc.sparkContext.emptyRDD[(String, Model)] + + // Create models kafka stream + val kafkaParams = KafkaSupport.getKafkaConsumerConfig(ApplicationKafkaParameters.LOCAL_KAFKA_BROKER) + val modelsStream = KafkaUtils.createDirectStream[Array[Byte], Array[Byte]](ssc,PreferConsistent, + Subscribe[Array[Byte], Array[Byte]](Set(ApplicationKafkaParameters.MODELS_TOPIC),kafkaParams)) + // Create data kafka stream + val dataStream = KafkaUtils.createDirectStream[Array[Byte], Array[Byte]](ssc, PreferConsistent, + Subscribe[Array[Byte], Array[Byte]](Set(ApplicationKafkaParameters.DATA_TOPIC),kafkaParams)) + // Convert streams + val data = dataStream.map(r => + DataRecord.fromByteArray(r.value())). + filter(_.isSuccess).map(d => DataWithModel(None, Some(d.get))) + val models = modelsStream.map(r => ModelToServe.fromByteArray(r.value())). + filter(_.isSuccess).map(m => DataWithModel(Some(m.get), None)) + // Combine streams + val unionStream = ssc.union(Seq(data, models)).map(r => (r.getDataType, r)) + + // score model using mapWithState, where state contains current model + val mappingFunc = (dataType: String, dataWithModel: Option[DataWithModel], state: State[Model]) => { + val currentModel = state.getOption().getOrElse(null.asInstanceOf[Model]) + dataWithModel match { + case Some(value) => + if (value.isModel) { + // Proces model + if (currentModel != null) currentModel.cleanup() + val model = factories.get(value.getModel.modelType.value) match { + case Some(factory) => factory.create(value.getModel) + case _ => None + } + model match { + case Some(m) => state.update(m) + case _ => + } + None + } + else { + // process data + if (currentModel != null) + Some(currentModel.score(value.getData.asInstanceOf[AnyVal]).asInstanceOf[Double]) + else + None + } + case _ => None + } + } + // Define StateSpec - types are derived from function + val resultDstream = unionStream.mapWithState(StateSpec.function(mappingFunc).initialState(modelsRDD)) + resultDstream.print() + // Execute + ssc.start() + ssc.awaitTermination() + } +} \ No newline at end of file diff --git a/model/src/main/java/com/lightbend/model/DataConverter.java b/utils/src/main/java/com/lightbend/modelserver/support/java/DataConverter.java similarity index 84% rename from model/src/main/java/com/lightbend/model/DataConverter.java rename to utils/src/main/java/com/lightbend/modelserver/support/java/DataConverter.java index e60454a..fc7fe27 100644 --- a/model/src/main/java/com/lightbend/model/DataConverter.java +++ b/utils/src/main/java/com/lightbend/modelserver/support/java/DataConverter.java @@ -1,4 +1,7 @@ -package com.lightbend.model; +package com.lightbend.modelserver.support.java; + +import com.lightbend.model.Modeldescriptor; +import com.lightbend.model.Winerecord; import java.util.Optional; @@ -21,13 +24,13 @@ public static Optional convertData(byte[] binary){ } } - public static Optional convertModel(byte[] binary){ + public static Optional convertModel(byte[] binary){ try { // Unmarshall record Modeldescriptor.ModelDescriptor model = Modeldescriptor.ModelDescriptor.parseFrom(binary); // Return it if(model.getMessageContentCase().equals(Modeldescriptor.ModelDescriptor.MessageContentCase.DATA)){ - return Optional.of(new CurrentModelDescriptor( + return Optional.of(new ModelToServe( model.getName(), model.getDescription(), model.getModeltype(), model.getData().toByteArray(), null, model.getDataType())); } diff --git a/model/src/main/java/com/lightbend/model/CurrentModelDescriptor.java b/utils/src/main/java/com/lightbend/modelserver/support/java/ModelToServe.java similarity index 78% rename from model/src/main/java/com/lightbend/model/CurrentModelDescriptor.java rename to utils/src/main/java/com/lightbend/modelserver/support/java/ModelToServe.java index 2fe3bb4..ed3ca72 100644 --- a/model/src/main/java/com/lightbend/model/CurrentModelDescriptor.java +++ b/utils/src/main/java/com/lightbend/modelserver/support/java/ModelToServe.java @@ -1,11 +1,13 @@ -package com.lightbend.model; +package com.lightbend.modelserver.support.java; + +import com.lightbend.model.Modeldescriptor; import java.io.Serializable; /** * Created by boris on 6/28/17. */ -public class CurrentModelDescriptor implements Serializable { +public class ModelToServe implements Serializable { private String name; private String description; @@ -14,8 +16,8 @@ public class CurrentModelDescriptor implements Serializable { private String modelDataLocation; private String dataType; - public CurrentModelDescriptor(String name, String description, Modeldescriptor.ModelDescriptor.ModelType modelType, - byte[] dataContent, String modelDataLocation, String dataType){ + public ModelToServe(String name, String description, Modeldescriptor.ModelDescriptor.ModelType modelType, + byte[] dataContent, String modelDataLocation, String dataType){ this.name = name; this.description = description; this.modelType = modelType; diff --git a/akkaserver/src/main/scala/com/lightbend/modelServer/kafka/EmbeddedSingleNodeKafkaCluster.scala b/utils/src/main/scala/com/lightbend/modelserver/kafka/EmbeddedSingleNodeKafkaCluster.scala similarity index 98% rename from akkaserver/src/main/scala/com/lightbend/modelServer/kafka/EmbeddedSingleNodeKafkaCluster.scala rename to utils/src/main/scala/com/lightbend/modelserver/kafka/EmbeddedSingleNodeKafkaCluster.scala index 5a6944e..883f234 100644 --- a/akkaserver/src/main/scala/com/lightbend/modelServer/kafka/EmbeddedSingleNodeKafkaCluster.scala +++ b/utils/src/main/scala/com/lightbend/modelserver/kafka/EmbeddedSingleNodeKafkaCluster.scala @@ -1,10 +1,10 @@ -package com.lightbend.modelServer.kafka +package com.lightbend.modelserver.kafka -import org.slf4j.LoggerFactory -import org.apache.curator.test.TestingServer import java.util.Properties import kafka.server.KafkaConfig +import org.apache.curator.test.TestingServer +import org.slf4j.LoggerFactory object EmbeddedSingleNodeKafkaCluster { diff --git a/akkaserver/src/main/scala/com/lightbend/modelServer/kafka/KafkaEmbedded.scala b/utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaEmbedded.scala similarity index 97% rename from akkaserver/src/main/scala/com/lightbend/modelServer/kafka/KafkaEmbedded.scala rename to utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaEmbedded.scala index 36cd807..430475b 100644 --- a/akkaserver/src/main/scala/com/lightbend/modelServer/kafka/KafkaEmbedded.scala +++ b/utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaEmbedded.scala @@ -1,13 +1,13 @@ -package com.lightbend.modelServer.kafka +package com.lightbend.modelserver.kafka + +import java.io.File +import java.util.{Collections, Properties} import com.google.common.io.Files import kafka.admin.{AdminUtils, RackAwareMode} import kafka.server.{KafkaConfig, KafkaServerStartable} -import kafka.utils.{CoreUtils, ZkUtils, ZKStringSerializer} -import org.I0Itec.zkclient.{ZkClient, ZkConnection} +import kafka.utils.{CoreUtils, ZkUtils} import org.slf4j.LoggerFactory -import java.io.File -import java.util.{Collections, Properties} /** diff --git a/utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaSupport.scala b/utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaSupport.scala new file mode 100755 index 0000000..55696fe --- /dev/null +++ b/utils/src/main/scala/com/lightbend/modelserver/kafka/KafkaSupport.scala @@ -0,0 +1,36 @@ +package com.lightbend.modelserver.kafka + +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.serialization.ByteArrayDeserializer + + +/** + * Created by blublins on 9/7/16. + */ +@SerialVersionUID(102L) +object KafkaSupport extends Serializable { + + // Kafka consumer properties + private val sessionTimeout: Int = 10 * 1000 + private val connectionTimeout: Int = 8 * 1000 + private val AUTOCOMMITINTERVAL: String = "1000" + // Frequency off offset commits + private val SESSIONTIMEOUT: String = "30000" + // The timeout used to detect failures - should be greater then processing time + private val MAXPOLLRECORDS: String = "10" + // Max number of records consumed in a single poll + private val GROUPID: String = "Spark Streaming" // Consumer ID + + def getKafkaConsumerConfig(brokers: String): Map[String, String] = { + Map[String, String]( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers, + ConsumerConfig.GROUP_ID_CONFIG -> GROUPID, + ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG -> "true", + ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG -> AUTOCOMMITINTERVAL, + ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG -> SESSIONTIMEOUT, + ConsumerConfig.MAX_POLL_RECORDS_CONFIG -> MAXPOLLRECORDS, + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> "earliest", + ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> classOf[ByteArrayDeserializer].getTypeName, + ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> classOf[ByteArrayDeserializer].getTypeName) + } +} \ No newline at end of file diff --git a/utils/src/main/scala/com/lightbend/modelserver/support/scala/DataReader.scala b/utils/src/main/scala/com/lightbend/modelserver/support/scala/DataReader.scala new file mode 100644 index 0000000..c04fdb1 --- /dev/null +++ b/utils/src/main/scala/com/lightbend/modelserver/support/scala/DataReader.scala @@ -0,0 +1,15 @@ +package com.lightbend.modelserver.support.scala + +import com.lightbend.model.winerecord.WineRecord + +import scala.util.Try + +/** + * Created by boris on 5/8/17. + */ +object DataReader { + + def fromByteArray(message: Array[Byte]): Try[WineRecord] = Try { + WineRecord.parseFrom(message) + } +} \ No newline at end of file diff --git a/utils/src/main/scala/com/lightbend/modelserver/support/scala/ModelToServe.scala b/utils/src/main/scala/com/lightbend/modelserver/support/scala/ModelToServe.scala new file mode 100644 index 0000000..a026d97 --- /dev/null +++ b/utils/src/main/scala/com/lightbend/modelserver/support/scala/ModelToServe.scala @@ -0,0 +1,39 @@ +package com.lightbend.modelserver.support.scala + +import com.lightbend.model.modeldescriptor.ModelDescriptor + +import scala.util.Try + +/** + * Created by boris on 5/8/17. + */ +object ModelToServe { + def fromByteArray(message: Array[Byte]): Try[ModelToServe] = Try{ + val m = ModelDescriptor.parseFrom(message) + m.messageContent.isData match { + case true => new ModelToServe(m.name, m.description, m.modeltype, m.getData.toByteArray, m.dataType) + case _ => throw new Exception("Location based is not yet supported") + } + } +} + +case class ModelToServe(name: String, description: String, + modelType: ModelDescriptor.ModelType, + model : Array[Byte], dataType : String) {} + +case class ModelToServeStats(name: String, description: String, modelType: String, + since : Long, var usage : Long = 0, var duration : Double = .0, + var min : Long = Long.MaxValue, var max : Long = Long.MinValue) { + def this(m : ModelToServe) = this(m.name, m.description, m.modelType.name, System.currentTimeMillis()) + def incrementUsage(execution : Long) : ModelToServeStats = { + usage = usage + 1 + duration = duration + execution + if(execution < min) min = execution + if(execution > max) max = execution + this + } + } + +object ModelToServeStats{ + val empty = ModelToServeStats("None", "None", "None", 0, 0, .0, 0, 0) +} \ No newline at end of file