From a1786ca964dd330de229ed82d8f50e4162f74f3a Mon Sep 17 00:00:00 2001 From: jaeho Date: Thu, 7 Mar 2024 03:30:17 +0000 Subject: [PATCH] Change spray json to circe --- build.sbt | 4 +- src/main/scala/fhetest/Checker/Utils.scala | 230 +++++++++++------- .../fhetest/Generate/LibConfigGenerator.scala | 2 +- src/main/scala/fhetest/Phase/Check.scala | 18 +- src/main/scala/fhetest/Phase/Execute.scala | 6 + 5 files changed, 165 insertions(+), 95 deletions(-) diff --git a/build.sbt b/build.sbt index 9a10873..754f26e 100644 --- a/build.sbt +++ b/build.sbt @@ -139,7 +139,9 @@ lazy val root = project "org.twc" % "t2" % "1.0" from file( "lib/terminator-compiler-1.0.jar", ).toURI.toString, - "io.spray" %% "spray-json" % "1.3.6", + "io.circe" %% "circe-core" % "0.14.1", + "io.circe" %% "circe-generic" % "0.14.1", + "io.circe" %% "circe-parser" % "0.14.1", ), // set the main class for 'sbt run' Compile / mainClass := Some("fhetest.FHETest"), diff --git a/src/main/scala/fhetest/Checker/Utils.scala b/src/main/scala/fhetest/Checker/Utils.scala index 7f5bd29..686c2c1 100644 --- a/src/main/scala/fhetest/Checker/Utils.scala +++ b/src/main/scala/fhetest/Checker/Utils.scala @@ -2,108 +2,158 @@ package fhetest.Checker import fhetest.Utils.* import fhetest.Generate.T2Program - import fhetest.TEST_DIR +import fhetest.LibConfig + +import io.circe._ +import io.circe.generic.semiauto._ +import io.circe.syntax._ import java.io.{File, PrintWriter} import java.nio.file.{Files, Path, Paths, StandardCopyOption} -import spray.json._ -import spray.json.DefaultJsonProtocol._ -// file writer -def getPrintWriter(filename: String): PrintWriter = - new PrintWriter(new File(filename)) +case class Failure(library: String, failedResult: String) +case class ResultInfo( + programId: Int, + program: T2Program, + result: String, + failures: List[Failure], + expected: String, + SEAL: String, + OpenFHE: String, +) -// dump given data to a file -def dumpFile(data: Any, filename: String): Unit = { - val nf = getPrintWriter(filename) - nf.print(data) - nf.close() +// Define en/decoders using Circe +// Scheme +implicit val schemeEncoder: Encoder[Scheme] = Encoder.instance { + case Scheme.BFV => Json.fromString("BFV") + case Scheme.BGV => Json.fromString("BGV") + case Scheme.CKKS => Json.fromString("CKKS") +} +implicit val schemeDecoder: Decoder[Scheme] = Decoder.decodeString.emap { + case "BFV" => Right(Scheme.BFV) + case "BGV" => Right(Scheme.BGV) + case "CKKS" => Right(Scheme.CKKS) + case other => Left(s"Unknown scheme: $other") } -// dump given data as JSON -def dumpJson[T](data: T, filename: String)(implicit - writer: JsonWriter[T], -): Unit = - dumpFile(data.toJson.prettyPrint, filename) +// SecurityLevel +implicit val securityLevelEncoder: Encoder[SecurityLevel] = Encoder.instance { + case SecurityLevel.HEStd_128_classic => Json.fromString("HEStd_128_classic") + case SecurityLevel.HEStd_192_classic => Json.fromString("HEStd_192_classic") + case SecurityLevel.HEStd_256_classic => Json.fromString("HEStd_256_classic") + case SecurityLevel.HEStd_NotSet => Json.fromString("HEStd_NotSet") +} +implicit val securityLevelDecoder: Decoder[SecurityLevel] = + Decoder.decodeString.emap { + case "HEStd_128_classic" => Right(SecurityLevel.HEStd_128_classic) + case "HEStd_192_classic" => Right(SecurityLevel.HEStd_192_classic) + case "HEStd_256_classic" => Right(SecurityLevel.HEStd_256_classic) + case "HEStd_NotSet" => Right(SecurityLevel.HEStd_NotSet) + case other => Left(s"Unknown security level: $other") + } -// FIXME: LibConfig -def dumpResult( - program: T2Program, - i: Int, - res: CheckResult, - sealVersion: String, - openfheVersion: String, -): Unit = { - val backend_info = Map( - ("SEAL" -> JsString(sealVersion)), - ("OpenFHE" -> JsString(openfheVersion)), - ) - val libConfig = program.libConfig - val encParams = libConfig.encParams - val encParams_info = JsObject( - ("ringDim" -> JsString(encParams.ringDim.toString)), - ("multDepth" -> JsString(encParams.mulDepth.toString)), - ("plainMod" -> JsString(encParams.plainMod.toString)), - ) - val libConfig_info = JsObject( - ("scheme" -> JsString(libConfig.scheme.toString)), - ("encParams" -> encParams_info.toJson), - ("firstModSize" -> JsString(libConfig.firstModSize.toString)), - ("scalingModSize" -> JsString(libConfig.scalingModSize.toString)), - ("securityLevel" -> JsString(libConfig.securityLevel.toString)), - ("scalingTechnique" -> JsString(libConfig.scalingTechnique.toString)), - ("lenOpt" -> JsString(libConfig.lenOpt.getOrElse(0).toString)), - ("boundOpt" -> JsString(libConfig.boundOpt.getOrElse(0).toString)), - ) - val pgm_info = Map( - ("programId" -> JsString(i.toString)), - ("program" -> JsString(program.content)), - ("libConfig" -> libConfig_info), - ) - val info = pgm_info ++ backend_info - res match { - case Same(res) => { - val (expectedLst, obtainedLst) = res.partition(_.backend == "CLEAR") - val expected_res = expectedLst.apply(0).result - val result = info ++ Map( - "result" -> JsString("Success"), - "failedLibraires" -> JsString("0"), - "failures" -> JsArray(), - "expected" -> JsString(expected_res.toString), - ) - val succFilename = s"$succDir/$i.json" - dumpJson(result, succFilename) +// ScalingTechnique +implicit val scalingTechniqueEncoder: Encoder[ScalingTechnique] = + Encoder.instance { + case ScalingTechnique.FIXEDMANUAL => Json.fromString("FIXEDMANUAL") + case ScalingTechnique.FIXEDAUTO => Json.fromString("FIXEDAUTO") + case ScalingTechnique.FLEXIBLEAUTO => Json.fromString("FLEXIBLEAUTO") + case ScalingTechnique.FLEXIBLEAUTOEXT => Json.fromString("FLEXIBLEAUTOEXT") + case ScalingTechnique.NORESCALE => Json.fromString("NORESCALE") + } +implicit val scalingTechniqueDecoder: Decoder[ScalingTechnique] = + Decoder.decodeString.emap { + case "FIXEDMANUAL" => Right(ScalingTechnique.FIXEDMANUAL) + case "FIXEDAUTO" => Right(ScalingTechnique.FIXEDAUTO) + case "FLEXIBLEAUTO" => Right(ScalingTechnique.FLEXIBLEAUTO) + case "FLEXIBLEAUTOEXT" => Right(ScalingTechnique.FLEXIBLEAUTOEXT) + case "NORESCALE" => Right(ScalingTechnique.NORESCALE) + case other => Left(s"Unknown scaling technique: $other") + } +implicit val encParamsEncoder: Encoder[EncParams] = deriveEncoder +implicit val libConfigEncoder: Encoder[LibConfig] = deriveEncoder +implicit val t2ProgramEncoder: Encoder[T2Program] = deriveEncoder +implicit val failureEncoder: Encoder[Failure] = deriveEncoder +implicit val resultInfoEncoder: Encoder[ResultInfo] = Encoder.forProduct7( + "programId", + "program", + "result", + "failures", + "expected", + "SEAL", + "OpenFHE", +)(ri => + ( + ri.programId, + ri.program, + ri.result, + ri.failures, + ri.expected, + ri.SEAL, + ri.OpenFHE, + ), +) +implicit val encodeIntOrDouble: Encoder[Int | Double] = Encoder.instance { + case i: Int => Json.fromInt(i) + case d: Double => Json.fromDoubleOrNull(d) +} + +object DumpUtil { + // 파일에 문자열 데이터 쓰기 함수 + def dumpFile(data: String, filename: String): Unit = { + val writer = new PrintWriter(filename) + try writer.write(data) + finally writer.close() + } + + // dumpResult 함수 구현 + def dumpResult( + program: T2Program, // 가정: 이미 정의되어 있음 + i: Int, + res: CheckResult, + sealVersion: String, + openfheVersion: String, + ): Unit = { + val resultString = res match { + case Same(_) => "Success" + case Diff(_, fails) => "Fail" + case ParserError(_) => "ParseError" } - case Diff(res, fails) => { - val (expectedLst, obtainedLst) = res.partition(_.backend == "CLEAR") - val expected = expectedLst.apply(0) - // val diffResults = obtainedLst.filter(isDiff(expected, _, )) - val failures = fails.map(r => - Map( - ("library" -> r.backend), - ("failedResult" -> r.result.toString), - ), - ) - val result = info ++ Map( - "result" -> JsString("Fail"), - "failedLibraires" -> JsString(fails.size.toString), - "failures" -> failures.toJson, - "expected" -> JsString(expected._2.toString), - ) - val failFilename = s"$failDir/$i.json" - dumpJson(result, failFilename) + + val failures = res match { + case Diff(_, fails) => + fails.map(fail => Failure(fail.backend, fail.result.toString())) + case _ => List.empty[Failure] } - case ParserError(_) => { - val result = info ++ Map( - "result" -> JsString("ParseError"), - "failedLibraires" -> JsString("NaN"), - "failures" -> JsArray(), - "expected" -> JsString(""), - ) - val psrErrFilename = s"$psrErrDir/$i.json" - dumpJson(result, psrErrFilename) + + val expected = res match { + case Same(results) => + results.headOption.map(_.result.toString()).getOrElse("") + case Diff(results, _) => + results + .partition(_.backend == "CLEAR") + ._1 + .headOption + .map(_.result.toString()) + .getOrElse("") } + + val resultInfo = ResultInfo( + i, + program, + resultString, + failures, + expected, + sealVersion, + openfheVersion, + ) + + val filename = res match + case Same(_) => s"$succDir$i.json" + case Diff(_, fails) => s"$failDir/$i.json" + case ParserError(_) => s"$psrErrDir/$i.json" + dumpFile(resultInfo.asJson.spaces2, filename) } } diff --git a/src/main/scala/fhetest/Generate/LibConfigGenerator.scala b/src/main/scala/fhetest/Generate/LibConfigGenerator.scala index bc3f83f..7c911b0 100644 --- a/src/main/scala/fhetest/Generate/LibConfigGenerator.scala +++ b/src/main/scala/fhetest/Generate/LibConfigGenerator.scala @@ -26,7 +26,7 @@ def generateLibConfig(encType: ENC_TYPE): LibConfig = { val randomLenOpt: Option[Int] = Some(Random.between(1, 100000 + 1)) val randomBoundOpt: Option[Int | Double] = randomScheme match { case Scheme.BFV | Scheme.BGV => - Some(Random.nextInt(Int.MaxValue)) + Some(Random.nextInt(100000)) case Scheme.CKKS => Some(Random.between(0, math.pow(2, 64))) } LibConfig( diff --git a/src/main/scala/fhetest/Phase/Check.scala b/src/main/scala/fhetest/Phase/Check.scala index 00f981d..81543db 100644 --- a/src/main/scala/fhetest/Phase/Check.scala +++ b/src/main/scala/fhetest/Phase/Check.scala @@ -4,6 +4,7 @@ import fhetest.Checker.* import fhetest.Generate.T2Program import fhetest.LibConfig import fhetest.Utils.* +import fhetest.Checker.DumpUtil import org.twc.terminator.t2dsl_compiler.T2DSLsyntaxtree.*; import org.twc.terminator.SymbolTable; @@ -11,7 +12,6 @@ import org.twc.terminator.SymbolTable; import java.nio.file.{Files, Paths}; import java.io.{File, InputStream, ByteArrayInputStream} import scala.jdk.CollectionConverters._ -import spray.json._ import scala.util.{Try, Success, Failure} @@ -90,7 +90,13 @@ case object Check { encParams.plainMod, ) if (toJson) - dumpResult(program, i, checkResult, sealVersion, openfheVersion) + DumpUtil.dumpResult( + program, + i, + checkResult, + sealVersion, + openfheVersion, + ) if (debug) { println(s"Program $i:") } @@ -140,7 +146,13 @@ case object Check { val program = T2Program(fileStr, libConfig) val checkResult = apply(program, backends, encParamsOpt) if (toJson) - dumpResult(program, i, checkResult, sealVersion, openfheVersion) + DumpUtil.dumpResult( + program, + i, + checkResult, + sealVersion, + openfheVersion, + ) val pgmStr = "-" * 10 + " Program " + "-" * 10 + "\n" + fileStr + "\n" val libConfigStr = "-" * 10 + " LibConfig " + "-" * 10 + "\n" + libConfig diff --git a/src/main/scala/fhetest/Phase/Execute.scala b/src/main/scala/fhetest/Phase/Execute.scala index f788d8c..d1d5eab 100644 --- a/src/main/scala/fhetest/Phase/Execute.scala +++ b/src/main/scala/fhetest/Phase/Execute.scala @@ -50,6 +50,12 @@ case object Execute { "Program terminated with segmentation fault (exit code 139).\n", ) return errorSB.toString() + } else if (executeExitCode == 136) { + // If program terminated with segmentation fault, return error message + errorSB.append( + "Program terminated with floating point exception (exit code 136).\n", + ) + return errorSB.toString() } else { // If execute failed, append error message return errorSB.toString()