From b298eebf5ea1b4c091603ebf0cacde0957191632 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Sun, 8 Oct 2023 12:52:54 +0200 Subject: [PATCH] fix(compiler): Fix gate inlining [LNG-253] (#924) * Fix gate inlining * Remake stream gate inlining, fix unit tests * Fix: add flat inline * Refactor, add comments --- .../aqua/compiler/AquaCompilerSpec.scala | 23 +- .../aqua/model/inline/RawValueInliner.scala | 7 +- .../raw/ApplyPropertiesRawInliner.scala | 152 ++++++++--- ...wInliner.scala => StreamGateInliner.scala} | 88 +++---- .../main/scala/aqua/raw/value/ValueRaw.scala | 20 +- .../src/test/scala/aqua/res/ResBuilder.scala | 30 ++- .../main/scala/aqua/model/ValueModel.scala | 18 +- .../aqua/model/transform/ModelBuilder.scala | 21 +- .../transform/topology/TopologySpec.scala | 248 ++++++++++-------- 9 files changed, 347 insertions(+), 260 deletions(-) rename model/inline/src/main/scala/aqua/model/inline/raw/{ApplyGateRawInliner.scala => StreamGateInliner.scala} (50%) diff --git a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala index cb7d82c7..3b8dd0a7 100644 --- a/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala +++ b/compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala @@ -1,14 +1,6 @@ package aqua.compiler -import aqua.model.{ - CallModel, - ForModel, - FunctorModel, - IntoIndexModel, - LiteralModel, - ValueModel, - VarModel -} +import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel} import aqua.model.transform.ModelBuilder import aqua.model.transform.TransformConfig import aqua.model.transform.Transform @@ -115,8 +107,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers { private val init = LiteralModel.fromRaw(ValueRaw.InitPeerId) - private def join(vm: VarModel, idx: ValueModel) = - ResBuilder.join(vm, idx, init) + private def join(vm: VarModel, size: ValueModel) = + ResBuilder.join(vm, size, init) "aqua compiler" should "create right topology" in { @@ -156,6 +148,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers { val canonResult = VarModel("-" + results.name + "-fix-0", CanonStreamType(resultsType.element)) val flatResult = VarModel("-results-flat-0", ArrayType(ScalarType.string)) val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId) + val sizeVar = VarModel("results_size", LiteralType.unsigned) val retVar = VarModel("ret", ScalarType.string) val expected = @@ -195,7 +188,13 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers { ) ) ), - join(results, LiteralModel.fromRaw(LiteralRaw.number(2))), + ResBuilder.add( + LiteralModel.number(2), + LiteralModel.number(1), + sizeVar, + initPeer + ), + join(results, sizeVar), CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf, ApRes( canonResult, diff --git a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala index 46ecc26a..b94e510f 100644 --- a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala @@ -6,12 +6,12 @@ import aqua.model.* import aqua.model.inline.raw.{ ApplyBinaryOpRawInliner, ApplyFunctorRawInliner, - ApplyGateRawInliner, ApplyPropertiesRawInliner, ApplyUnaryOpRawInliner, CallArrowRawInliner, CollectionRawInliner, - MakeAbilityRawInliner + MakeAbilityRawInliner, + StreamGateInliner } import aqua.raw.ops.* import aqua.raw.value.* @@ -48,9 +48,6 @@ object RawValueInliner extends Logging { case alr: ApplyPropertyRaw => ApplyPropertiesRawInliner(alr, propertiesAllowed) - case agr: ApplyGateRaw => - ApplyGateRawInliner(agr, propertiesAllowed) - case cr: CollectionRaw => CollectionRawInliner(cr, propertiesAllowed) diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala index bd5130f9..1f0318a1 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala @@ -18,6 +18,7 @@ import cats.syntax.bifunctor.* import cats.syntax.foldable.* import cats.syntax.monoid.* import cats.syntax.traverse.* +import cats.syntax.option.* import scribe.Logging object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging { @@ -48,17 +49,6 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi } } - private def removeProperties[S: Mangler]( - varModel: VarModel - ): State[S, (VarModel, Inline)] = { - for { - nn <- Mangler[S].findAndForbidName(varModel.name + "_flat") - } yield { - val flatten = VarModel(nn, varModel.`type`) - flatten -> Inline.tree(FlattenModel(varModel, flatten.name).leaf) - } - } - private def unfoldAbilityProperty[S: Mangler: Exports: Arrows]( varModel: VarModel, abilityType: NamedType, @@ -143,9 +133,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi case f @ FunctorRaw(_, _) => for { - flattenVI <- - if (varModel.properties.nonEmpty) removeProperties(varModel) - else State.pure(varModel, Inline.empty) + flattenVI <- removeProperties(varModel) (flatten, inline) = flattenVI newVI <- ApplyFunctorRawInliner(flatten, f) } yield { @@ -157,9 +145,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi case ic @ IntoCopyRaw(_, _) => for { - flattenVI <- - if (varModel.properties.nonEmpty) removeProperties(varModel) - else State.pure(varModel, Inline.empty) + flattenVI <- removeProperties(varModel) (flatten, inline) = flattenVI newVI <- ApplyIntoCopyRawInliner(flatten, ic) } yield { @@ -182,15 +168,31 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi unfold(vr, propertiesAllowed = false).flatMap { case (vm @ VarModel(_, _, _), inline) if vm.properties.nonEmpty => removeProperties(vm).map { case (vf, inlf) => - PropertyRawWithModel(iir, Option(IntoIndexModel(vf.name, t))) -> Inline( + PropertyRawWithModel( + iir, + IntoIndexModel + .fromValueModel(vf, t) + .getOrElse( + internalError(s"Unexpected: could not convert ($vf) to IntoIndexModel") + ) + .some + ) -> Inline( inline.predo ++ inlf.predo, mergeMode = SeqMode ) } - case (VarModel(name, _, _), inline) => - State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(name, t))) -> inline) - case (LiteralModel(literal, _), inline) => - State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(literal, t))) -> inline) + case (vm, inline) => + ( + PropertyRawWithModel( + iir, + IntoIndexModel + .fromValueModel(vm, t) + .getOrElse( + internalError(s"Unexpected: could not convert ($vm) to IntoIndexModel") + ) + .some + ) -> inline + ).pure } case p => State.pure(PropertyRawWithModel(p, None) -> Inline.empty) @@ -246,31 +248,88 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi } } + /** + * Unfold `stream[idx]` + */ + private def unfoldStreamGate[S: Mangler: Exports: Arrows]( + streamName: String, + streamType: StreamType, + idx: ValueRaw + ): State[S, (VarModel, Inline)] = for { + /** + * Inline idx + */ + idxInlined <- unfold(idx) + (idxVM, idxInline) = idxInlined + /** + * Inline size which is `idx + 1` + * TODO: Refactor to apply optimizations + */ + sizeName <- Mangler[S].findAndForbidName(s"${streamName}_size") + sizeVar = VarModel(sizeName, idxVM.`type`) + sizeInline = CallServiceModel( + "math", + funcName = "add", + args = List(idxVM, LiteralModel.number(1)), + result = sizeVar + ).leaf + gateInlined <- StreamGateInliner(streamName, streamType, sizeVar) + (gateVM, gateInline) = gateInlined + /** + * Remove properties from idx + * as we need to use it in index + * TODO: Do not generate it + * if it is not needed, + * e.g. in `join` + */ + idxFlattened <- idxVM match { + case vr: VarModel => removeProperties(vr) + case _ => (idxVM, Inline.empty).pure[State[S, *]] + } + (idxFlat, idxFlatInline) = idxFlattened + /** + * Construct stream[idx] + */ + gate = gateVM.withProperty( + IntoIndexModel + .fromValueModel(idxFlat, streamType.element) + .getOrElse( + internalError(s"Unexpected: could not convert ($idxFlat) to IntoIndexModel") + ) + ) + } yield gate -> Inline( + idxInline.predo + .append(sizeInline) ++ + gateInline.predo ++ + idxFlatInline.predo, + mergeMode = SeqMode + ) + private def unfoldRawWithProperties[S: Mangler: Exports: Arrows]( raw: ValueRaw, properties: Chain[PropertyRaw], propertiesAllowed: Boolean ): State[S, (ValueModel, Inline)] = { - ((raw, properties.headOption) match { - case (vr @ VarRaw(_, st @ StreamType(_)), Some(IntoIndexRaw(idx, _))) => + ((raw, properties.uncons) match { + /** + * To inline + */ + case ( + vr @ VarRaw(_, st @ StreamType(_)), + Some(IntoIndexRaw(idx, _), otherProperties) + ) => unfold(vr).flatMap { case (VarModel(nameVM, _, _), inl) => - val gateRaw = ApplyGateRaw(nameVM, st, idx) - unfold(gateRaw).flatMap { - case (gateResVal: VarModel, gateResInline) => - unfoldProperties(gateResInline, gateResVal, properties, propertiesAllowed).map { - case (v, i) => - v -> Inline( - inl.predo ++ i.predo, - mergeMode = SeqMode - ) - } - case (v, i) => - // what if pass nil as stream argument? - internalError( - s"Unfolded stream ($gateRaw) cannot be a literal" - ) - } + for { + gateInlined <- unfoldStreamGate(nameVM, st, idx) + (gateVM, gateInline) = gateInlined + propsInlined <- unfoldProperties( + gateInline, + gateVM, + otherProperties, + propertiesAllowed + ) + } yield propsInlined case l => internalError( s"Unfolded stream ($vr) cannot be a literal" @@ -299,6 +358,19 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi } + /** + * Remove properties from the var and return a new var without them + */ + private def removeProperties[S: Mangler]( + varModel: VarModel + ): State[S, (VarModel, Inline)] = + if (varModel.properties.isEmpty) (varModel, Inline.empty).pure + else + for { + nn <- Mangler[S].findAndForbidName(varModel.name + "_flat") + flatten = VarModel(nn, varModel.`type`) + } yield flatten -> Inline.tree(FlattenModel(varModel, flatten.name).leaf) + override def apply[S: Mangler: Exports: Arrows]( apr: ApplyPropertyRaw, propertiesAllowed: Boolean diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyGateRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/StreamGateInliner.scala similarity index 50% rename from model/inline/src/main/scala/aqua/model/inline/raw/ApplyGateRawInliner.scala rename to model/inline/src/main/scala/aqua/model/inline/raw/StreamGateInliner.scala index 0b3f4b49..0c52bee1 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyGateRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/StreamGateInliner.scala @@ -1,9 +1,10 @@ package aqua.model.inline.raw +import aqua.errors.Errors.internalError import aqua.model.* import aqua.model.inline.Inline import aqua.model.inline.state.{Arrows, Exports, Mangler} -import aqua.raw.value.{ApplyGateRaw, LiteralRaw, VarRaw} +import aqua.raw.value.{LiteralRaw, VarRaw} import aqua.model.inline.RawValueInliner.unfold import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType} @@ -11,16 +12,17 @@ import cats.data.State import cats.data.Chain import cats.syntax.monoid.* import cats.syntax.option.* +import cats.syntax.applicative.* import scribe.Logging +import cats.instances.stream -object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { +object StreamGateInliner extends Logging { /** - * To wait for the element of a stream by the given index, the following model is generated: - * (seq + * To wait for size elements of a stream, + * the following model is generated: * (seq * (seq - * (call ("math" "add") [0 1] stream_incr) * (fold $stream s * (seq * (seq @@ -28,7 +30,7 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { * (canon $stream_test #stream_iter_canon) * ) * (xor - * (match #stream_iter_canon.length stream_incr + * (match #stream_iter_canon.length size * (null) * ) * (next s) @@ -36,17 +38,15 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { * ) * (never) * ) + * (canon $stream_test #stream_result_canon) * ) - * (canon $stream_test #stream_result_canon) - * ) - * (ap #stream_result_canon stream_gate) + * (ap #stream_result_canon stream_gate) * ) */ def joinStreamOnIndexModel( streamName: String, streamType: StreamType, - idxModel: ValueModel, - idxIncrName: String, + sizeModel: ValueModel, testName: String, iterName: String, canonName: String, @@ -55,16 +55,10 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { ): OpModel.Tree = { val varSTest = VarModel(testName, streamType) val iter = VarModel(iterName, streamType.element) - val iterCanon = VarModel(iterCanonName, CanonStreamType(streamType.element)) - - val resultCanon = - VarModel(canonName, CanonStreamType(streamType.element)) - - val incrVar = VarModel(idxIncrName, ScalarType.u32) + val resultCanon = VarModel(canonName, CanonStreamType(streamType.element)) RestrictionModel(varSTest.name, streamType).wrap( - increment(idxModel, incrVar), ForModel(iter.name, VarModel(streamName, streamType), ForModel.Mode.Never.some).wrap( PushToStreamModel( iter, @@ -77,8 +71,10 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { XorModel.wrap( MatchMismatchModel( iterCanon - .copy(properties = Chain.one(FunctorModel("length", ScalarType.`u32`))), - incrVar, + .withProperty( + FunctorModel("length", ScalarType.`u32`) + ), + sizeModel, true ).leaf, NextModel(iter.name).leaf @@ -95,25 +91,23 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { ) } - override def apply[S: Mangler: Exports: Arrows]( - afr: ApplyGateRaw, - propertyAllowed: Boolean - ): State[S, (ValueModel, Inline)] = + def apply[S: Mangler: Exports: Arrows]( + streamName: String, + streamType: StreamType, + sizeModel: ValueModel + ): State[S, (VarModel, Inline)] = for { - uniqueCanonName <- Mangler[S].findAndForbidName(afr.name + "_result_canon") - uniqueResultName <- Mangler[S].findAndForbidName(afr.name + "_gate") - uniqueTestName <- Mangler[S].findAndForbidName(afr.name + "_test") - uniqueIdxIncr <- Mangler[S].findAndForbidName(afr.name + "_incr") - uniqueIterCanon <- Mangler[S].findAndForbidName(afr.name + "_iter_canon") - uniqueIter <- Mangler[S].findAndForbidName(afr.name + "_fold_var") - idxFolded <- unfold(afr.idx) - (idxModel, idxInline) = idxFolded + uniqueCanonName <- Mangler[S].findAndForbidName(streamName + "_result_canon") + uniqueResultName <- Mangler[S].findAndForbidName(streamName + "_gate") + uniqueTestName <- Mangler[S].findAndForbidName(streamName + "_test") + uniqueIdxIncr <- Mangler[S].findAndForbidName(streamName + "_incr") + uniqueIterCanon <- Mangler[S].findAndForbidName(streamName + "_iter_canon") + uniqueIter <- Mangler[S].findAndForbidName(streamName + "_fold_var") } yield { val gate = joinStreamOnIndexModel( - streamName = afr.name, - streamType = afr.streamType, - idxModel = idxModel, - idxIncrName = uniqueIdxIncr, + streamName = streamName, + streamType = streamType, + sizeModel = sizeModel, testName = uniqueTestName, iterName = uniqueIter, canonName = uniqueCanonName, @@ -121,24 +115,12 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging { resultName = uniqueResultName ) - val tree = SeqModel.wrap(idxInline.predo.toList :+ gate) - - val treeInline = Inline(predo = Chain.one(tree)) - - ( - VarModel(uniqueResultName, ArrayType(afr.streamType.element)), - treeInline + val inline = Inline(predo = Chain.one(gate)) + val value = VarModel( + uniqueResultName, + ArrayType(streamType.element) ) + (value, inline) } - - private def increment(v: ValueModel, result: VarModel) = - CallServiceModel( - LiteralModel("\"math\"", ScalarType.string), - "add", - CallModel( - v :: LiteralModel.fromRaw(LiteralRaw.number(1)) :: Nil, - CallModel.Export(result.name, result.`type`) :: Nil - ) - ).leaf } diff --git a/model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala b/model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala index 4bd6980a..87fefdce 100644 --- a/model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala +++ b/model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala @@ -58,8 +58,8 @@ object ValueRaw { errorType ) - type ApplyRaw = ApplyGateRaw | ApplyPropertyRaw | CallArrowRaw | CollectionRaw | - ApplyBinaryOpRaw | ApplyUnaryOpRaw + type ApplyRaw = ApplyPropertyRaw | CallArrowRaw | CollectionRaw | ApplyBinaryOpRaw | + ApplyUnaryOpRaw } case class ApplyPropertyRaw(value: ValueRaw, property: PropertyRaw) extends ValueRaw { @@ -94,22 +94,6 @@ object ApplyPropertyRaw { } } -case class ApplyGateRaw(name: String, streamType: StreamType, idx: ValueRaw) extends ValueRaw { - override def baseType: Type = streamType - - override def `type`: Type = idx.`type` - - override def renameVars(map: Map[String, String]): ValueRaw = - copy(name = map.getOrElse(name, name), idx = idx.renameVars(map)) - - override def map(f: ValueRaw => ValueRaw): ValueRaw = - f(copy(idx = f(idx))) - - override def toString: String = s"gate $name.$idx" - - override def varNames: Set[String] = Set(name) ++ idx.varNames -} - case class VarRaw(name: String, baseType: Type) extends ValueRaw { override def map(f: ValueRaw => ValueRaw): ValueRaw = f(this) diff --git a/model/res/src/test/scala/aqua/res/ResBuilder.scala b/model/res/src/test/scala/aqua/res/ResBuilder.scala index 7c38cb6a..6d5a4a9b 100644 --- a/model/res/src/test/scala/aqua/res/ResBuilder.scala +++ b/model/res/src/test/scala/aqua/res/ResBuilder.scala @@ -9,32 +9,22 @@ import cats.syntax.option.* object ResBuilder { - def join(stream: VarModel, onIdx: ValueModel, peer: ValueModel) = { + def join(stream: VarModel, sizeModel: ValueModel, peer: ValueModel) = { val testVM = VarModel(stream.name + "_test", stream.`type`) val testStreamType = stream.`type`.asInstanceOf[StreamType] // Unsafe val iter = VarModel(stream.name + "_fold_var", ScalarType.string) val canon = VarModel(stream.name + "_iter_canon", CanonStreamType(ScalarType.string)) val canonRes = VarModel(stream.name + "_result_canon", CanonStreamType(ScalarType.string)) val arrayRes = VarModel(stream.name + "_gate", ArrayType(ScalarType.string)) - val idx = VarModel(stream.name + "_incr", ScalarType.u32) RestrictionRes(testVM.name, testStreamType).wrap( - CallServiceRes( - LiteralModel("\"math\"", ScalarType.string), - "add", - CallRes( - onIdx :: LiteralModel.fromRaw(LiteralRaw.number(1)) :: Nil, - Some(CallModel.Export(idx.name, idx.`type`)) - ), - peer - ).leaf, FoldRes(iter.name, stream, ForModel.Mode.Never.some).wrap( ApRes(iter, CallModel.Export(testVM.name, testVM.`type`)).leaf, CanonRes(testVM, peer, CallModel.Export(canon.name, canon.`type`)).leaf, XorRes.wrap( MatchMismatchRes( canon.copy(properties = Chain.one(FunctorModel("length", ScalarType.u32))), - idx, + sizeModel, true ).leaf, NextRes(iter.name).leaf @@ -45,4 +35,20 @@ object ResBuilder { ) } + def add( + a: ValueModel, + b: ValueModel, + res: VarModel, + peer: ValueModel + ): ResolvedOp.Tree = + CallServiceRes( + LiteralModel.quote("math"), + "add", + CallRes( + a :: b :: Nil, + Some(CallModel.Export(res.name, res.`type`)) + ), + peer + ).leaf + } diff --git a/model/src/main/scala/aqua/model/ValueModel.scala b/model/src/main/scala/aqua/model/ValueModel.scala index 794acf01..4a243127 100644 --- a/model/src/main/scala/aqua/model/ValueModel.scala +++ b/model/src/main/scala/aqua/model/ValueModel.scala @@ -157,6 +157,15 @@ case class IntoIndexModel(idx: String, `type`: Type) extends PropertyModel { ) } +object IntoIndexModel { + + def fromValueModel(vm: ValueModel, `type`: Type): Option[IntoIndexModel] = vm match { + case VarModel(name, _, Chain.nil) => IntoIndexModel(name, `type`).some + case LiteralModel(value, _) => IntoIndexModel(value, `type`).some + case _ => none + } +} + case class VarModel(name: String, baseType: Type, properties: Chain[PropertyModel] = Chain.empty) extends ValueModel with Logging { @@ -173,14 +182,13 @@ case class VarModel(name: String, baseType: Type, properties: Chain[PropertyMode private def deriveFrom(vm: VarModel): VarModel = vm.copy(properties = vm.properties ++ properties) + def withProperty(p: PropertyModel): VarModel = + copy(properties = properties :+ p) + def intoField(field: String): Option[VarModel] = `type` match { case StructType(_, fields) => fields(field) - .map(fieldType => - copy( - properties = properties :+ IntoFieldModel(field, fieldType) - ) - ) + .map(fieldType => withProperty(IntoFieldModel(field, fieldType))) case _ => none } diff --git a/model/transform/src/test/scala/aqua/model/transform/ModelBuilder.scala b/model/transform/src/test/scala/aqua/model/transform/ModelBuilder.scala index ca92238a..30bbed51 100644 --- a/model/transform/src/test/scala/aqua/model/transform/ModelBuilder.scala +++ b/model/transform/src/test/scala/aqua/model/transform/ModelBuilder.scala @@ -8,7 +8,7 @@ import aqua.res.{CallRes, CallServiceRes, MakeRes} import aqua.types.{ArrayType, LiteralType, ScalarType} import aqua.types.StreamType import aqua.model.IntoIndexModel -import aqua.model.inline.raw.ApplyGateRawInliner +import aqua.model.inline.raw.StreamGateInliner import aqua.model.OnModel import aqua.model.FailModel import aqua.res.ResolvedOp @@ -142,21 +142,20 @@ object ModelBuilder { /** * @param stream stream [[VarModel]] - * @param idx id [[ValueModel]] - * @return [[OpModel.Tree]] of join of `stream[idx]` + * @param size size [[ValueModel]] + * @return [[OpModel.Tree]] of join of size elements of stream */ - def join(stream: VarModel, idx: ValueModel): OpModel.Tree = + def join(stream: VarModel, size: ValueModel): OpModel.Tree = stream match { case VarModel( streamName, streamType: StreamType, Chain.`nil` ) => - ApplyGateRawInliner.joinStreamOnIndexModel( + StreamGateInliner.joinStreamOnIndexModel( streamName = streamName, streamType = streamType, - idxModel = idx, - idxIncrName = streamName + "_incr", + sizeModel = size, testName = streamName + "_test", iterName = streamName + "_fold_var", canonName = streamName + "_result_canon", @@ -165,4 +164,12 @@ object ModelBuilder { ) case _ => ??? } + + def add(a: ValueModel, b: ValueModel, res: VarModel): OpModel.Tree = + CallServiceModel( + "math", + "add", + args = List(a, b), + result = res + ).leaf } diff --git a/model/transform/src/test/scala/aqua/model/transform/topology/TopologySpec.scala b/model/transform/src/test/scala/aqua/model/transform/topology/TopologySpec.scala index 4b25cfe7..ef51b3d8 100644 --- a/model/transform/src/test/scala/aqua/model/transform/topology/TopologySpec.scala +++ b/model/transform/src/test/scala/aqua/model/transform/topology/TopologySpec.scala @@ -25,22 +25,45 @@ class TopologySpec extends AnyFlatSpec with Matchers { import ModelBuilder.{join as joinModel, *} import ResBuilder.join as joinRes - def joinModelRes(streamEl: ValueRaw | ValueModel): (OpModel.Tree, ResolvedOp.Tree) = + def joinModelRes( + streamEl: ValueRaw | ValueModel + ): (Chain[OpModel.Tree], Chain[ResolvedOp.Tree]) = streamEl match { case vm: ValueModel => vm case vr: ValueRaw => ValueModel.fromRaw(vr) } match { case stream @ VarModel(name, baseType, IntoIndexModel(idx, idxType) ==: Chain.`nil`) => val idxModel = - if (idx.forall(Character.isDigit)) LiteralModel(idx, idxType) - else VarModel(idx, idxType) + if (idx.forall(Character.isDigit)) LiteralModel(idx, ScalarType.u32) + else VarModel(idx, ScalarType.u32) val streamWithoutIdx = stream.copy(properties = Chain.`nil`) - ( - joinModel(streamWithoutIdx, idxModel), - joinRes(streamWithoutIdx, idxModel, ValueModel.fromRaw(initPeer)) + val sizeModel = VarModel(s"${name}_size", ScalarType.u32) + val sizeTree = ModelBuilder.add( + idxModel, + LiteralModel.number(1), + sizeModel ) + + val model = Chain( + sizeTree, + joinModel(streamWithoutIdx, sizeModel) + ) + + val sizeTreeResolved = ResBuilder.add( + idxModel, + LiteralModel.number(1), + sizeModel, + ValueModel.fromRaw(initPeer) + ) + + val resolved = Chain( + sizeTreeResolved, + joinRes(streamWithoutIdx, sizeModel, ValueModel.fromRaw(initPeer)) + ) + + (model, resolved) case _ => ??? } @@ -462,54 +485,58 @@ class TopologySpec extends AnyFlatSpec with Matchers { val (joinModel, joinRes) = joinModelRes(streamEl) + val foldModel = foldPar( + "i", + valueArray, + OnModel(iRelay, Chain.empty).wrap( + XorModel.wrap( + callModel(2, CallModel.Export(streamRaw.name, streamRaw.`type`) :: Nil), + OnModel(initPeer, Chain.one(relay)).wrap( + callModel(4, Nil, Nil) + ) + ) + ) + ) val init = SeqModel.wrap( DeclareStreamModel(stream).leaf, OnModel(initPeer, Chain.one(relay)).wrap( - foldPar( - "i", - valueArray, - OnModel(iRelay, Chain.empty).wrap( - XorModel.wrap( - callModel(2, CallModel.Export(streamRaw.name, streamRaw.`type`) :: Nil), - OnModel(initPeer, Chain.one(relay)).wrap( - callModel(4, Nil, Nil) - ) - ) - ) - ), - joinModel, - callModel(3, Nil, streamRaw :: Nil) + foldModel +: + joinModel :+ + callModel(3, Nil, streamRaw :: Nil) ) ) val proc = Topology.resolve(init).value - val expected = SeqRes.wrap( - through(relay), - ParRes.wrap( - FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( - ParRes.wrap( - // better if first relay will be outside `for` - SeqRes.wrap( - through(relay), - XorRes.wrap( - SeqRes.wrap( - callRes(2, iRelay, Some(CallModel.Export(streamRaw.name, streamRaw.`type`))), - through(relay), - through(initPeer) - ), - SeqRes.wrap( - through(relay), - callRes(4, initPeer) - ) + val foldRes = ParRes.wrap( + FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( + ParRes.wrap( + // better if first relay will be outside `for` + SeqRes.wrap( + through(relay), + XorRes.wrap( + SeqRes.wrap( + callRes(2, iRelay, Some(CallModel.Export(streamRaw.name, streamRaw.`type`))), + through(relay), + through(initPeer) + ), + SeqRes.wrap( + through(relay), + callRes(4, initPeer) ) - ), - NextRes("i").leaf - ) + ) + ), + NextRes("i").leaf ) - ), - joinRes, - callRes(3, initPeer, None, stream :: Nil) + ) + ) + val expected = SeqRes.wrap( + Chain( + through(relay), + foldRes + ) ++ + joinRes :+ + callRes(3, initPeer, None, stream :: Nil) ) proc.equalsOrShowDiff(expected) should be(true) @@ -543,42 +570,45 @@ class TopologySpec extends AnyFlatSpec with Matchers { ) ) ) - ), - joinModel, - callModel(3, Nil, streamRaw :: Nil) + ) +: + joinModel :+ + callModel(3, Nil, streamRaw :: Nil) ) ) val proc = Topology.resolve(init).value - val expected = SeqRes.wrap( - through(relay), - ParRes.wrap( - FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( - ParRes.wrap( - // better if first relay will be outside `for` - SeqRes.wrap( - through(relay), + val fold = ParRes.wrap( + FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( + ParRes.wrap( + // better if first relay will be outside `for` + SeqRes.wrap( + through(relay), + XorRes.wrap( XorRes.wrap( - XorRes.wrap( - SeqRes.wrap( - callRes(2, iRelay, Some(CallModel.Export(streamRaw.name, streamRaw.`type`))), - through(relay), - through(initPeer) - ) - ), SeqRes.wrap( + callRes(2, iRelay, Some(CallModel.Export(streamRaw.name, streamRaw.`type`))), through(relay), - callRes(4, initPeer) + through(initPeer) ) + ), + SeqRes.wrap( + through(relay), + callRes(4, initPeer) ) - ), - NextRes("i").leaf - ) + ) + ), + NextRes("i").leaf ) - ), - joinRes, - callRes(3, initPeer, None, stream :: Nil) + ) + ) + val expected = SeqRes.wrap( + Chain( + through(relay), + fold + ) ++ + joinRes :+ + callRes(3, initPeer, None, stream :: Nil) ) // println(Console.MAGENTA + init.show + Console.RESET) @@ -804,9 +834,9 @@ class TopologySpec extends AnyFlatSpec with Matchers { OnModel(i, Chain.empty).wrap( callModel(1, CallModel.Export(used.name, used.`type`) :: Nil) ) - ), - joinModel, - callModel(3, Nil, used :: Nil) + ) +: + joinModel :+ + callModel(3, Nil, used :: Nil) ) val proc = Topology.resolve(init).value @@ -828,9 +858,9 @@ class TopologySpec extends AnyFlatSpec with Matchers { NextRes("i").leaf ) ) - ), - joinRes, - callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) + ) +: + joinRes :+ + callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) ) proc.equalsOrShowDiff(expected) should be(true) @@ -844,46 +874,48 @@ class TopologySpec extends AnyFlatSpec with Matchers { val (joinModel, joinRes) = joinModelRes(usedWithIdx) - val init = OnModel(initPeer, Chain.one(relay)).wrap( - foldPar( - "i", - valueArray, - OnModel(i, Chain.empty).wrap( - XorModel.wrap( - callModel(1, CallModel.Export(used.name, used.`type`) :: Nil) - ) + val foldModel = foldPar( + "i", + valueArray, + OnModel(i, Chain.empty).wrap( + XorModel.wrap( + callModel(1, CallModel.Export(used.name, used.`type`) :: Nil) ) - ), - joinModel, - callModel(3, Nil, used :: Nil) + ) + ) + val init = OnModel(initPeer, Chain.one(relay)).wrap( + foldModel +: + joinModel :+ + callModel(3, Nil, used :: Nil) ) val proc = Topology.resolve(init).value - val expected = SeqRes.wrap( - ParRes.wrap( - FoldRes("i", ValueModel.fromRaw(valueArray), ForModel.Mode.Never.some).wrap( - ParRes.wrap( - SeqRes.wrap( - through(relay), - XorRes.wrap( - SeqRes.wrap( - callRes( - 1, - ValueModel.fromRaw(i), - Some(CallModel.Export(used.name, used.`type`)) - ), - through(relay), - through(initPeer) - ) + val foldRes = ParRes.wrap( + FoldRes("i", ValueModel.fromRaw(valueArray), ForModel.Mode.Never.some).wrap( + ParRes.wrap( + SeqRes.wrap( + through(relay), + XorRes.wrap( + SeqRes.wrap( + callRes( + 1, + ValueModel.fromRaw(i), + Some(CallModel.Export(used.name, used.`type`)) + ), + through(relay), + through(initPeer) ) - ), - NextRes("i").leaf - ) + ) + ), + NextRes("i").leaf ) - ), - joinRes, - callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) + ) + ) + val expected = SeqRes.wrap( + foldRes +: + joinRes :+ + callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) ) proc.equalsOrShowDiff(expected) should be(true)