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
This commit is contained in:
InversionSpaces 2023-10-08 12:52:54 +02:00 committed by GitHub
parent feb7a167a2
commit b298eebf5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 347 additions and 260 deletions

View File

@ -1,14 +1,6 @@
package aqua.compiler package aqua.compiler
import aqua.model.{ import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel}
CallModel,
ForModel,
FunctorModel,
IntoIndexModel,
LiteralModel,
ValueModel,
VarModel
}
import aqua.model.transform.ModelBuilder import aqua.model.transform.ModelBuilder
import aqua.model.transform.TransformConfig import aqua.model.transform.TransformConfig
import aqua.model.transform.Transform import aqua.model.transform.Transform
@ -115,8 +107,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
private val init = LiteralModel.fromRaw(ValueRaw.InitPeerId) private val init = LiteralModel.fromRaw(ValueRaw.InitPeerId)
private def join(vm: VarModel, idx: ValueModel) = private def join(vm: VarModel, size: ValueModel) =
ResBuilder.join(vm, idx, init) ResBuilder.join(vm, size, init)
"aqua compiler" should "create right topology" in { "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 canonResult = VarModel("-" + results.name + "-fix-0", CanonStreamType(resultsType.element))
val flatResult = VarModel("-results-flat-0", ArrayType(ScalarType.string)) val flatResult = VarModel("-results-flat-0", ArrayType(ScalarType.string))
val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId) val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId)
val sizeVar = VarModel("results_size", LiteralType.unsigned)
val retVar = VarModel("ret", ScalarType.string) val retVar = VarModel("ret", ScalarType.string)
val expected = 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, CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf,
ApRes( ApRes(
canonResult, canonResult,

View File

@ -6,12 +6,12 @@ import aqua.model.*
import aqua.model.inline.raw.{ import aqua.model.inline.raw.{
ApplyBinaryOpRawInliner, ApplyBinaryOpRawInliner,
ApplyFunctorRawInliner, ApplyFunctorRawInliner,
ApplyGateRawInliner,
ApplyPropertiesRawInliner, ApplyPropertiesRawInliner,
ApplyUnaryOpRawInliner, ApplyUnaryOpRawInliner,
CallArrowRawInliner, CallArrowRawInliner,
CollectionRawInliner, CollectionRawInliner,
MakeAbilityRawInliner MakeAbilityRawInliner,
StreamGateInliner
} }
import aqua.raw.ops.* import aqua.raw.ops.*
import aqua.raw.value.* import aqua.raw.value.*
@ -48,9 +48,6 @@ object RawValueInliner extends Logging {
case alr: ApplyPropertyRaw => case alr: ApplyPropertyRaw =>
ApplyPropertiesRawInliner(alr, propertiesAllowed) ApplyPropertiesRawInliner(alr, propertiesAllowed)
case agr: ApplyGateRaw =>
ApplyGateRawInliner(agr, propertiesAllowed)
case cr: CollectionRaw => case cr: CollectionRaw =>
CollectionRawInliner(cr, propertiesAllowed) CollectionRawInliner(cr, propertiesAllowed)

View File

@ -18,6 +18,7 @@ import cats.syntax.bifunctor.*
import cats.syntax.foldable.* import cats.syntax.foldable.*
import cats.syntax.monoid.* import cats.syntax.monoid.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.option.*
import scribe.Logging import scribe.Logging
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with 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]( private def unfoldAbilityProperty[S: Mangler: Exports: Arrows](
varModel: VarModel, varModel: VarModel,
abilityType: NamedType, abilityType: NamedType,
@ -143,9 +133,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
case f @ FunctorRaw(_, _) => case f @ FunctorRaw(_, _) =>
for { for {
flattenVI <- flattenVI <- removeProperties(varModel)
if (varModel.properties.nonEmpty) removeProperties(varModel)
else State.pure(varModel, Inline.empty)
(flatten, inline) = flattenVI (flatten, inline) = flattenVI
newVI <- ApplyFunctorRawInliner(flatten, f) newVI <- ApplyFunctorRawInliner(flatten, f)
} yield { } yield {
@ -157,9 +145,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
case ic @ IntoCopyRaw(_, _) => case ic @ IntoCopyRaw(_, _) =>
for { for {
flattenVI <- flattenVI <- removeProperties(varModel)
if (varModel.properties.nonEmpty) removeProperties(varModel)
else State.pure(varModel, Inline.empty)
(flatten, inline) = flattenVI (flatten, inline) = flattenVI
newVI <- ApplyIntoCopyRawInliner(flatten, ic) newVI <- ApplyIntoCopyRawInliner(flatten, ic)
} yield { } yield {
@ -182,15 +168,31 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
unfold(vr, propertiesAllowed = false).flatMap { unfold(vr, propertiesAllowed = false).flatMap {
case (vm @ VarModel(_, _, _), inline) if vm.properties.nonEmpty => case (vm @ VarModel(_, _, _), inline) if vm.properties.nonEmpty =>
removeProperties(vm).map { case (vf, inlf) => 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, inline.predo ++ inlf.predo,
mergeMode = SeqMode mergeMode = SeqMode
) )
} }
case (VarModel(name, _, _), inline) => case (vm, inline) =>
State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(name, t))) -> inline) (
case (LiteralModel(literal, _), inline) => PropertyRawWithModel(
State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(literal, t))) -> inline) 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) 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]( private def unfoldRawWithProperties[S: Mangler: Exports: Arrows](
raw: ValueRaw, raw: ValueRaw,
properties: Chain[PropertyRaw], properties: Chain[PropertyRaw],
propertiesAllowed: Boolean propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] = { ): State[S, (ValueModel, Inline)] = {
((raw, properties.headOption) match { ((raw, properties.uncons) match {
case (vr @ VarRaw(_, st @ StreamType(_)), Some(IntoIndexRaw(idx, _))) => /**
* To inline
*/
case (
vr @ VarRaw(_, st @ StreamType(_)),
Some(IntoIndexRaw(idx, _), otherProperties)
) =>
unfold(vr).flatMap { unfold(vr).flatMap {
case (VarModel(nameVM, _, _), inl) => case (VarModel(nameVM, _, _), inl) =>
val gateRaw = ApplyGateRaw(nameVM, st, idx) for {
unfold(gateRaw).flatMap { gateInlined <- unfoldStreamGate(nameVM, st, idx)
case (gateResVal: VarModel, gateResInline) => (gateVM, gateInline) = gateInlined
unfoldProperties(gateResInline, gateResVal, properties, propertiesAllowed).map { propsInlined <- unfoldProperties(
case (v, i) => gateInline,
v -> Inline( gateVM,
inl.predo ++ i.predo, otherProperties,
mergeMode = SeqMode propertiesAllowed
) )
} } yield propsInlined
case (v, i) =>
// what if pass nil as stream argument?
internalError(
s"Unfolded stream ($gateRaw) cannot be a literal"
)
}
case l => case l =>
internalError( internalError(
s"Unfolded stream ($vr) cannot be a literal" 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]( override def apply[S: Mangler: Exports: Arrows](
apr: ApplyPropertyRaw, apr: ApplyPropertyRaw,
propertiesAllowed: Boolean propertiesAllowed: Boolean

View File

@ -1,9 +1,10 @@
package aqua.model.inline.raw package aqua.model.inline.raw
import aqua.errors.Errors.internalError
import aqua.model.* import aqua.model.*
import aqua.model.inline.Inline import aqua.model.inline.Inline
import aqua.model.inline.state.{Arrows, Exports, Mangler} 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.model.inline.RawValueInliner.unfold
import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType} import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType}
@ -11,16 +12,17 @@ import cats.data.State
import cats.data.Chain import cats.data.Chain
import cats.syntax.monoid.* import cats.syntax.monoid.*
import cats.syntax.option.* import cats.syntax.option.*
import cats.syntax.applicative.*
import scribe.Logging 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: * To wait for size elements of a stream,
* the following model is generated:
* (seq * (seq
* (seq * (seq
* (seq
* (call <peer> ("math" "add") [0 1] stream_incr)
* (fold $stream s * (fold $stream s
* (seq * (seq
* (seq * (seq
@ -28,7 +30,7 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
* (canon <peer> $stream_test #stream_iter_canon) * (canon <peer> $stream_test #stream_iter_canon)
* ) * )
* (xor * (xor
* (match #stream_iter_canon.length stream_incr * (match #stream_iter_canon.length size
* (null) * (null)
* ) * )
* (next s) * (next s)
@ -36,7 +38,6 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
* ) * )
* (never) * (never)
* ) * )
* )
* (canon <peer> $stream_test #stream_result_canon) * (canon <peer> $stream_test #stream_result_canon)
* ) * )
* (ap #stream_result_canon stream_gate) * (ap #stream_result_canon stream_gate)
@ -45,8 +46,7 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
def joinStreamOnIndexModel( def joinStreamOnIndexModel(
streamName: String, streamName: String,
streamType: StreamType, streamType: StreamType,
idxModel: ValueModel, sizeModel: ValueModel,
idxIncrName: String,
testName: String, testName: String,
iterName: String, iterName: String,
canonName: String, canonName: String,
@ -55,16 +55,10 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
): OpModel.Tree = { ): OpModel.Tree = {
val varSTest = VarModel(testName, streamType) val varSTest = VarModel(testName, streamType)
val iter = VarModel(iterName, streamType.element) val iter = VarModel(iterName, streamType.element)
val iterCanon = VarModel(iterCanonName, CanonStreamType(streamType.element)) val iterCanon = VarModel(iterCanonName, CanonStreamType(streamType.element))
val resultCanon = VarModel(canonName, CanonStreamType(streamType.element))
val resultCanon =
VarModel(canonName, CanonStreamType(streamType.element))
val incrVar = VarModel(idxIncrName, ScalarType.u32)
RestrictionModel(varSTest.name, streamType).wrap( RestrictionModel(varSTest.name, streamType).wrap(
increment(idxModel, incrVar),
ForModel(iter.name, VarModel(streamName, streamType), ForModel.Mode.Never.some).wrap( ForModel(iter.name, VarModel(streamName, streamType), ForModel.Mode.Never.some).wrap(
PushToStreamModel( PushToStreamModel(
iter, iter,
@ -77,8 +71,10 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
XorModel.wrap( XorModel.wrap(
MatchMismatchModel( MatchMismatchModel(
iterCanon iterCanon
.copy(properties = Chain.one(FunctorModel("length", ScalarType.`u32`))), .withProperty(
incrVar, FunctorModel("length", ScalarType.`u32`)
),
sizeModel,
true true
).leaf, ).leaf,
NextModel(iter.name).leaf NextModel(iter.name).leaf
@ -95,25 +91,23 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
) )
} }
override def apply[S: Mangler: Exports: Arrows]( def apply[S: Mangler: Exports: Arrows](
afr: ApplyGateRaw, streamName: String,
propertyAllowed: Boolean streamType: StreamType,
): State[S, (ValueModel, Inline)] = sizeModel: ValueModel
): State[S, (VarModel, Inline)] =
for { for {
uniqueCanonName <- Mangler[S].findAndForbidName(afr.name + "_result_canon") uniqueCanonName <- Mangler[S].findAndForbidName(streamName + "_result_canon")
uniqueResultName <- Mangler[S].findAndForbidName(afr.name + "_gate") uniqueResultName <- Mangler[S].findAndForbidName(streamName + "_gate")
uniqueTestName <- Mangler[S].findAndForbidName(afr.name + "_test") uniqueTestName <- Mangler[S].findAndForbidName(streamName + "_test")
uniqueIdxIncr <- Mangler[S].findAndForbidName(afr.name + "_incr") uniqueIdxIncr <- Mangler[S].findAndForbidName(streamName + "_incr")
uniqueIterCanon <- Mangler[S].findAndForbidName(afr.name + "_iter_canon") uniqueIterCanon <- Mangler[S].findAndForbidName(streamName + "_iter_canon")
uniqueIter <- Mangler[S].findAndForbidName(afr.name + "_fold_var") uniqueIter <- Mangler[S].findAndForbidName(streamName + "_fold_var")
idxFolded <- unfold(afr.idx)
(idxModel, idxInline) = idxFolded
} yield { } yield {
val gate = joinStreamOnIndexModel( val gate = joinStreamOnIndexModel(
streamName = afr.name, streamName = streamName,
streamType = afr.streamType, streamType = streamType,
idxModel = idxModel, sizeModel = sizeModel,
idxIncrName = uniqueIdxIncr,
testName = uniqueTestName, testName = uniqueTestName,
iterName = uniqueIter, iterName = uniqueIter,
canonName = uniqueCanonName, canonName = uniqueCanonName,
@ -121,24 +115,12 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
resultName = uniqueResultName resultName = uniqueResultName
) )
val tree = SeqModel.wrap(idxInline.predo.toList :+ gate) val inline = Inline(predo = Chain.one(gate))
val value = VarModel(
val treeInline = Inline(predo = Chain.one(tree)) uniqueResultName,
ArrayType(streamType.element)
(
VarModel(uniqueResultName, ArrayType(afr.streamType.element)),
treeInline
) )
(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
} }

View File

@ -58,8 +58,8 @@ object ValueRaw {
errorType errorType
) )
type ApplyRaw = ApplyGateRaw | ApplyPropertyRaw | CallArrowRaw | CollectionRaw | type ApplyRaw = ApplyPropertyRaw | CallArrowRaw | CollectionRaw | ApplyBinaryOpRaw |
ApplyBinaryOpRaw | ApplyUnaryOpRaw ApplyUnaryOpRaw
} }
case class ApplyPropertyRaw(value: ValueRaw, property: PropertyRaw) extends ValueRaw { 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 { case class VarRaw(name: String, baseType: Type) extends ValueRaw {
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(this) override def map(f: ValueRaw => ValueRaw): ValueRaw = f(this)

View File

@ -9,32 +9,22 @@ import cats.syntax.option.*
object ResBuilder { 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 testVM = VarModel(stream.name + "_test", stream.`type`)
val testStreamType = stream.`type`.asInstanceOf[StreamType] // Unsafe val testStreamType = stream.`type`.asInstanceOf[StreamType] // Unsafe
val iter = VarModel(stream.name + "_fold_var", ScalarType.string) val iter = VarModel(stream.name + "_fold_var", ScalarType.string)
val canon = VarModel(stream.name + "_iter_canon", CanonStreamType(ScalarType.string)) val canon = VarModel(stream.name + "_iter_canon", CanonStreamType(ScalarType.string))
val canonRes = VarModel(stream.name + "_result_canon", CanonStreamType(ScalarType.string)) val canonRes = VarModel(stream.name + "_result_canon", CanonStreamType(ScalarType.string))
val arrayRes = VarModel(stream.name + "_gate", ArrayType(ScalarType.string)) val arrayRes = VarModel(stream.name + "_gate", ArrayType(ScalarType.string))
val idx = VarModel(stream.name + "_incr", ScalarType.u32)
RestrictionRes(testVM.name, testStreamType).wrap( 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( FoldRes(iter.name, stream, ForModel.Mode.Never.some).wrap(
ApRes(iter, CallModel.Export(testVM.name, testVM.`type`)).leaf, ApRes(iter, CallModel.Export(testVM.name, testVM.`type`)).leaf,
CanonRes(testVM, peer, CallModel.Export(canon.name, canon.`type`)).leaf, CanonRes(testVM, peer, CallModel.Export(canon.name, canon.`type`)).leaf,
XorRes.wrap( XorRes.wrap(
MatchMismatchRes( MatchMismatchRes(
canon.copy(properties = Chain.one(FunctorModel("length", ScalarType.u32))), canon.copy(properties = Chain.one(FunctorModel("length", ScalarType.u32))),
idx, sizeModel,
true true
).leaf, ).leaf,
NextRes(iter.name).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
} }

View File

@ -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) case class VarModel(name: String, baseType: Type, properties: Chain[PropertyModel] = Chain.empty)
extends ValueModel with Logging { extends ValueModel with Logging {
@ -173,14 +182,13 @@ case class VarModel(name: String, baseType: Type, properties: Chain[PropertyMode
private def deriveFrom(vm: VarModel): VarModel = private def deriveFrom(vm: VarModel): VarModel =
vm.copy(properties = vm.properties ++ properties) vm.copy(properties = vm.properties ++ properties)
def withProperty(p: PropertyModel): VarModel =
copy(properties = properties :+ p)
def intoField(field: String): Option[VarModel] = `type` match { def intoField(field: String): Option[VarModel] = `type` match {
case StructType(_, fields) => case StructType(_, fields) =>
fields(field) fields(field)
.map(fieldType => .map(fieldType => withProperty(IntoFieldModel(field, fieldType)))
copy(
properties = properties :+ IntoFieldModel(field, fieldType)
)
)
case _ => none case _ => none
} }

View File

@ -8,7 +8,7 @@ import aqua.res.{CallRes, CallServiceRes, MakeRes}
import aqua.types.{ArrayType, LiteralType, ScalarType} import aqua.types.{ArrayType, LiteralType, ScalarType}
import aqua.types.StreamType import aqua.types.StreamType
import aqua.model.IntoIndexModel import aqua.model.IntoIndexModel
import aqua.model.inline.raw.ApplyGateRawInliner import aqua.model.inline.raw.StreamGateInliner
import aqua.model.OnModel import aqua.model.OnModel
import aqua.model.FailModel import aqua.model.FailModel
import aqua.res.ResolvedOp import aqua.res.ResolvedOp
@ -142,21 +142,20 @@ object ModelBuilder {
/** /**
* @param stream stream [[VarModel]] * @param stream stream [[VarModel]]
* @param idx id [[ValueModel]] * @param size size [[ValueModel]]
* @return [[OpModel.Tree]] of join of `stream[idx]` * @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 { stream match {
case VarModel( case VarModel(
streamName, streamName,
streamType: StreamType, streamType: StreamType,
Chain.`nil` Chain.`nil`
) => ) =>
ApplyGateRawInliner.joinStreamOnIndexModel( StreamGateInliner.joinStreamOnIndexModel(
streamName = streamName, streamName = streamName,
streamType = streamType, streamType = streamType,
idxModel = idx, sizeModel = size,
idxIncrName = streamName + "_incr",
testName = streamName + "_test", testName = streamName + "_test",
iterName = streamName + "_fold_var", iterName = streamName + "_fold_var",
canonName = streamName + "_result_canon", canonName = streamName + "_result_canon",
@ -165,4 +164,12 @@ object ModelBuilder {
) )
case _ => ??? case _ => ???
} }
def add(a: ValueModel, b: ValueModel, res: VarModel): OpModel.Tree =
CallServiceModel(
"math",
"add",
args = List(a, b),
result = res
).leaf
} }

View File

@ -25,22 +25,45 @@ class TopologySpec extends AnyFlatSpec with Matchers {
import ModelBuilder.{join as joinModel, *} import ModelBuilder.{join as joinModel, *}
import ResBuilder.join as joinRes 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 { streamEl match {
case vm: ValueModel => vm case vm: ValueModel => vm
case vr: ValueRaw => ValueModel.fromRaw(vr) case vr: ValueRaw => ValueModel.fromRaw(vr)
} match { } match {
case stream @ VarModel(name, baseType, IntoIndexModel(idx, idxType) ==: Chain.`nil`) => case stream @ VarModel(name, baseType, IntoIndexModel(idx, idxType) ==: Chain.`nil`) =>
val idxModel = val idxModel =
if (idx.forall(Character.isDigit)) LiteralModel(idx, idxType) if (idx.forall(Character.isDigit)) LiteralModel(idx, ScalarType.u32)
else VarModel(idx, idxType) else VarModel(idx, ScalarType.u32)
val streamWithoutIdx = stream.copy(properties = Chain.`nil`) val streamWithoutIdx = stream.copy(properties = Chain.`nil`)
( val sizeModel = VarModel(s"${name}_size", ScalarType.u32)
joinModel(streamWithoutIdx, idxModel), val sizeTree = ModelBuilder.add(
joinRes(streamWithoutIdx, idxModel, ValueModel.fromRaw(initPeer)) 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 _ => ??? case _ => ???
} }
@ -462,10 +485,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val (joinModel, joinRes) = joinModelRes(streamEl) val (joinModel, joinRes) = joinModelRes(streamEl)
val init = SeqModel.wrap( val foldModel = foldPar(
DeclareStreamModel(stream).leaf,
OnModel(initPeer, Chain.one(relay)).wrap(
foldPar(
"i", "i",
valueArray, valueArray,
OnModel(iRelay, Chain.empty).wrap( OnModel(iRelay, Chain.empty).wrap(
@ -476,17 +496,19 @@ class TopologySpec extends AnyFlatSpec with Matchers {
) )
) )
) )
), )
joinModel, val init = SeqModel.wrap(
DeclareStreamModel(stream).leaf,
OnModel(initPeer, Chain.one(relay)).wrap(
foldModel +:
joinModel :+
callModel(3, Nil, streamRaw :: Nil) callModel(3, Nil, streamRaw :: Nil)
) )
) )
val proc = Topology.resolve(init).value val proc = Topology.resolve(init).value
val expected = SeqRes.wrap( val foldRes = ParRes.wrap(
through(relay),
ParRes.wrap(
FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap(
ParRes.wrap( ParRes.wrap(
// better if first relay will be outside `for` // better if first relay will be outside `for`
@ -507,8 +529,13 @@ class TopologySpec extends AnyFlatSpec with Matchers {
NextRes("i").leaf NextRes("i").leaf
) )
) )
), )
joinRes, val expected = SeqRes.wrap(
Chain(
through(relay),
foldRes
) ++
joinRes :+
callRes(3, initPeer, None, stream :: Nil) callRes(3, initPeer, None, stream :: Nil)
) )
@ -543,17 +570,15 @@ class TopologySpec extends AnyFlatSpec with Matchers {
) )
) )
) )
), ) +:
joinModel, joinModel :+
callModel(3, Nil, streamRaw :: Nil) callModel(3, Nil, streamRaw :: Nil)
) )
) )
val proc = Topology.resolve(init).value val proc = Topology.resolve(init).value
val expected = SeqRes.wrap( val fold = ParRes.wrap(
through(relay),
ParRes.wrap(
FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap( FoldRes("i", valueArray, ForModel.Mode.Never.some).wrap(
ParRes.wrap( ParRes.wrap(
// better if first relay will be outside `for` // better if first relay will be outside `for`
@ -576,8 +601,13 @@ class TopologySpec extends AnyFlatSpec with Matchers {
NextRes("i").leaf NextRes("i").leaf
) )
) )
), )
joinRes, val expected = SeqRes.wrap(
Chain(
through(relay),
fold
) ++
joinRes :+
callRes(3, initPeer, None, stream :: Nil) callRes(3, initPeer, None, stream :: Nil)
) )
@ -804,8 +834,8 @@ class TopologySpec extends AnyFlatSpec with Matchers {
OnModel(i, Chain.empty).wrap( OnModel(i, Chain.empty).wrap(
callModel(1, CallModel.Export(used.name, used.`type`) :: Nil) callModel(1, CallModel.Export(used.name, used.`type`) :: Nil)
) )
), ) +:
joinModel, joinModel :+
callModel(3, Nil, used :: Nil) callModel(3, Nil, used :: Nil)
) )
@ -828,8 +858,8 @@ class TopologySpec extends AnyFlatSpec with Matchers {
NextRes("i").leaf NextRes("i").leaf
) )
) )
), ) +:
joinRes, joinRes :+
callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil)
) )
@ -844,8 +874,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val (joinModel, joinRes) = joinModelRes(usedWithIdx) val (joinModel, joinRes) = joinModelRes(usedWithIdx)
val init = OnModel(initPeer, Chain.one(relay)).wrap( val foldModel = foldPar(
foldPar(
"i", "i",
valueArray, valueArray,
OnModel(i, Chain.empty).wrap( OnModel(i, Chain.empty).wrap(
@ -853,15 +882,16 @@ class TopologySpec extends AnyFlatSpec with Matchers {
callModel(1, CallModel.Export(used.name, used.`type`) :: Nil) callModel(1, CallModel.Export(used.name, used.`type`) :: Nil)
) )
) )
), )
joinModel, val init = OnModel(initPeer, Chain.one(relay)).wrap(
foldModel +:
joinModel :+
callModel(3, Nil, used :: Nil) callModel(3, Nil, used :: Nil)
) )
val proc = Topology.resolve(init).value val proc = Topology.resolve(init).value
val expected = SeqRes.wrap( val foldRes = ParRes.wrap(
ParRes.wrap(
FoldRes("i", ValueModel.fromRaw(valueArray), ForModel.Mode.Never.some).wrap( FoldRes("i", ValueModel.fromRaw(valueArray), ForModel.Mode.Never.some).wrap(
ParRes.wrap( ParRes.wrap(
SeqRes.wrap( SeqRes.wrap(
@ -881,8 +911,10 @@ class TopologySpec extends AnyFlatSpec with Matchers {
NextRes("i").leaf NextRes("i").leaf
) )
) )
), )
joinRes, val expected = SeqRes.wrap(
foldRes +:
joinRes :+
callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil) callRes(3, initPeer, None, ValueModel.fromRaw(used) :: Nil)
) )