Skip to content

Commit

Permalink
Merge pull request #16 from edin-dal/feature/division
Browse files Browse the repository at this point in the history
Tensor and scalar division added
  • Loading branch information
mtghorbani authored Jul 11, 2024
2 parents 07b3605 + fb61cee commit eb12f45
Show file tree
Hide file tree
Showing 30 changed files with 985 additions and 146 deletions.
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ Following is an example on how you can use scalar tensors and constant numbers i
```
symbols: N
A(i) := 3.14 * B() * C(i) + D(i) * (-1)
A(i) := 3.14 * B() * C(i)
A:D(i) := (0 <= i < N)
C:D(i) := (0 <= i < N)
D:D(i) := (0 <= i < N)
```
This computation will generate the following code:
Expand All @@ -73,13 +72,41 @@ This computation will generate the following code:
for (int i = 0; i < N; ++i) {
A[i] += (3.14 * B * C[i]);
}
```

As shown, the values of the array `C` are multiplied by `3.14` and the value of variable `B`.

#### Subtraction

Subtraction can be represented by multiplying the second operand of the addition by -1. Following is an example of using tensor subtraction with STUR syntax.

```
symbols: N
A(i) := B(i) + (-1) * C(i)
A:D(i) := (0 <= i < N)
B:D(i) := (0 <= i < N)
C:D(i) := (0 <= i < N)
```

#### Division

Division is represented by adding `^-1` to the name of the tensors or constants. Following is an example of using tensor divison with STUR syntax.

```
symbols: N
A(i) := B(i) * 3.14^-1 * C^-1()
A:D(i) := (0 <= i < N)
B:D(i) := (0 <= i < N)
```

This computation will generate the following code:

```c++
for (int i = 0; i < N; ++i) {
A[i] += (-1 * D[i]);
A[i] += (1. / C * 1. / 3.14 * B[i]);
}
```

As shown, the values of the array `C` are multiplied by `3.14` and the value of variable `B`. Then the array `D` is subtracted from the array `A`.

### Advanced Syntax

#### Preprocessing
Expand Down
11 changes: 11 additions & 0 deletions examples/inverse-complex.stur
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
symbols: N
A(i, j) := B^-1(i, j) * 5^-1 * f^-1()
C(i, j) := A^-1(i, j)
X(i, j) := C^-1(i, j)
D(i, j) := B(i, j) * B^-1(i, j)
A:D(i, j) := (0 <= i < N) * (0 <= j < N)
B:D(i, j) := (0 <= i < N) * (0 <= j < N)
B:U(i, j) := (i = j)
C:D(i, j) := (0 <= i < N) * (0 <= j < N)
D:D(i, j) := (0 <= i < N) * (0 <= j < N)
X:D(i, j) := (0 <= i < N) * (0 <= j < N)
4 changes: 4 additions & 0 deletions examples/inverse-tensor-scalar.stur
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
symbols: N
A(i, j) := B^-1(i, j) * -5.123^-1 * f^-1()
A:D(i, j) := (0 <= i < N) * (0 <= j < N)
B:D(i, j) := (0 <= i < N) * (0 <= j < N)
4 changes: 4 additions & 0 deletions examples/inverse-tensor.stur
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
symbols: N
A(i, j) := f(i) * f^-1(j)
A:D(i, j) := (0 <= i < N) * (0 <= j < N)
f:D(i) := (0 <= i < N)
4 changes: 4 additions & 0 deletions examples/inverse-with-structure.stur
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
symbols: N
A(i, j) := f^-1(i) * f^-1(j)
A:D(i, j) := (0 <= i < N) * (0 <= j < N)
f:D(i) := (0 <= i < N)
10 changes: 8 additions & 2 deletions src/main/scala/uk/ac/ed/dal/structtensor/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,19 @@ object Main extends App {
)
)
(
acc._1 + (usRule.head -> usRule),
acc._1 + (usRule.head -> usRule) + (usRule.head
.inverseHead() -> usRule),
acc._2 + (Access(
rmRule.head.name,
usRule.head.vars,
rmRule.head.kind
) -> rmRule) + (Access(
rmRule.head.name.inverseName,
usRule.head.vars,
rmRule.head.kind
) -> rmRule),
acc._3 + (ccRule.head -> ccRule),
acc._3 + (ccRule.head -> ccRule) + (ccRule.head
.inverseHead() -> ccRule.inverse()),
acc._4 :+ ccRule,
acc._5 :+ rcRule
)
Expand Down
19 changes: 13 additions & 6 deletions src/main/scala/uk/ac/ed/dal/structtensor/codegen/Bodygen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ object Bodygen {
val c2 = read_argv(codeLang, argv_names)
val decimal_pattern = """-?\d+(\.\d+)?""".r
val c3 = all_tensors
.map(_.deinversifiedHead())
.distinctBy(_.name)
.filter(_.kind == Tensor)
.filterNot(only_lhs_heads.contains)
.filterNot(decimal_pattern matches _.name)
.filterNot(t => decimal_pattern.matches(t.name))
.map(t =>
alloc_and_gen_random_number(
codeLang,
Expand Down Expand Up @@ -134,12 +135,13 @@ extern "C"
val decimal_pattern = """-?\d+(\.\d+)?""".r
val tensor_to_str = all_tensors
.filterNot(only_lhs_heads_not_in_output.contains)
.distinctBy(_.name)
.filterNot(decimal_pattern matches _.name)
.filterNot(t => decimal_pattern.matches(t.name.deinversifiedName))
.map(t =>
"double " + (if (t.vars.isEmpty) "&"
else "*" * t.vars.length) + " " + t.name
else
"*" * t.vars.length) + " " + t.name.deinversifiedName
)
.distinct
.mkString(", ")
val symbols_to_str = symbols.map(v => "int " + v.name).mkString(", ")
val c2 =
Expand Down Expand Up @@ -173,15 +175,20 @@ extern "C"
}
val c1 = outputs_names.isEmpty match {
case true =>
all_tensors.map(t => printerr(codeLang, t)).mkString("\n")
all_tensors
.map(_.deinversifiedHead())
.distinctBy(_.name)
.map(t => printerr(codeLang, t))
.mkString("\n")
case false =>
all_tensors
.filter(e => outputs_names.contains(e.name))
.map(t => printerr(codeLang, t))
.map(t => printerr(codeLang, t.deinversifiedHead()))
.mkString("\n")
}
val c2 = all_tensors
.filterNot(only_lhs_heads_not_in_output.contains)
.map(_.deinversifiedHead())
.distinctBy(_.name)
.filter(all_dimensions.contains)
.map(t => free(codeLang, t.name, all_dimensions(t)))
Expand Down
11 changes: 6 additions & 5 deletions src/main/scala/uk/ac/ed/dal/structtensor/codegen/Codegen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ object Codegen {

def CPPFormat(a: Any): String = {
a match {
case s: String => s
case i: Int => i.toString
case d: Double => d.toString
case s: String => if (s.endsWith("^-1")) s"1. / ${s.dropRight(3)}" else s
case i: Int => i.toString
case d: Double => d.toString
case v: Variable => v.name
case i: Index =>
i match {
Expand All @@ -28,8 +28,9 @@ object Codegen {
case _ => ""
}
case a: Access =>
if (a.vars.isEmpty) a.name
else s"${a.name}[${a.vars.map(_.name).mkString("][")}]"
if (a.vars.isEmpty) CPPFormat(a.name)
else
s"${CPPFormat(a.name)}[${a.vars.map(_.name).mkString("][")}]"
case c @ Comparison(op, i, v) =>
s"(${CPPFormat(i)} ${op} ${CPPFormat(v)})"
case p: Prod =>
Expand Down
13 changes: 13 additions & 0 deletions src/main/scala/uk/ac/ed/dal/structtensor/compiler/STUR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ case class Access(name: String, vars: Seq[Variable], kind: AccessType)
def compressedHead(): Access =
Access(name.compressedName, vars, CompressedTensor)
def dimensionHead(): Access = Access(name.dimensionName, vars, DimensionType)
def inverseHead(): Access = Access(name.inverseName, vars, kind)
def deinversifiedHead(): Access = Access(name.deinversifiedName, vars, kind)
}

case class Comparison(op: String, index: Index, variable: Variable)
Expand All @@ -98,6 +100,14 @@ case class Prod(exps: Seq[Exp]) {
val pr = exps.map(_.prettyFormat).mkString(" * ")
if (pr.isEmpty) "" else pr
}
def inverse(): Prod = Prod(
exps.map(e =>
e match {
case a: Access => a.inverseHead()
case _ => e
}
)
)
}

case class SoP(prods: Seq[Prod]) {
Expand All @@ -111,10 +121,13 @@ case class SoP(prods: Seq[Prod]) {
Prod(prod.exps.map(_.vars2RedundancyVars))
})
}

def inverse(): SoP = SoP(prods.map(_.inverse()))
}

case class Rule(head: Access, body: SoP) {
def prettyFormat(): String = s"${head.prettyFormat} := ${body.prettyFormat}"
def inverse(): Rule = Rule(head.inverseHead(), body.inverse())
}

case class Interval(begin: Seq[Index], end: Seq[Index])
Expand Down
77 changes: 58 additions & 19 deletions src/main/scala/uk/ac/ed/dal/structtensor/parser/Convertor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,41 @@ object Convertor {
Map.empty[Access, Rule]
)
)((acc, r) => {
val head = Access(r.head.name, r.head.vars, Tensor)
val head = Access(r.head.name.deinversifiedName, r.head.vars, Tensor)
val head_inverse = head.inverseHead()
r.head.kind match {
case Tensor => (acc._1 ++ Map(head -> r), acc._2, acc._3, acc._4)
case UniqueSet => (acc._1, acc._2 ++ Map(head -> r), acc._3, acc._4)
case RedundancyMap => (acc._1, acc._2, acc._3 ++ Map(head -> r), acc._4)
case DimensionType => (acc._1, acc._2, acc._3, acc._4 ++ Map(head -> r))
case _ => throw new Exception("Unknown kind of tensor")
case Tensor => (acc._1 ++ Map(head -> r), acc._2, acc._3, acc._4)
case UniqueSet =>
(
acc._1,
acc._2 ++ Map(
head -> r,
head_inverse -> Rule(r.head, r.body.inverse())
),
acc._3,
acc._4
)
case RedundancyMap =>
(
acc._1,
acc._2,
acc._3 ++ Map(
head -> r,
head_inverse -> Rule(r.head, r.body.inverse())
),
acc._4
)
case DimensionType =>
(
acc._1,
acc._2,
acc._3,
acc._4 ++ Map(
head -> r,
head_inverse -> Rule(r.head, r.body.inverse())
)
)
case _ => throw new Exception("Unknown kind of tensor")
}
})
}
Expand Down Expand Up @@ -99,18 +127,26 @@ object Convertor {
if (!dimsAvailable)
throw new Exception("Dimensions not available for all tensors")

val (dimInfos, headToDimInfoMap) = headsRequiringDim.map { a =>
val r = headToDimensionMap.getByAccessNameAndReplaceVars(a).get
val oldHead = r.head
val name =
if (r.head.name.contains("_D"))
r.head.name.substring(0, r.head.name.length - 2)
else r.head.name
val head = Access(name, r.head.vars, Tensor)
val body = r.body

val dimSeq = head.vars.map(v => findUpperBound(v, body))
(DimInfo(head, dimSeq), oldHead -> DimInfo(r.head, dimSeq))
val headAndHeadInverseRequiringDim =
headsRequiringDim ++ headsRequiringDim.map(_.inverseHead())

val (dimInfos, headToDimInfoMap) = headAndHeadInverseRequiringDim.collect {
a =>
headToDimensionMap.containsByName(a.name) match {
case true => {
val r = headToDimensionMap.getByAccessNameAndReplaceVars(a).get
val oldHead = r.head
val name =
if (r.head.name.contains("_D"))
r.head.name.substring(0, r.head.name.length - 2)
else r.head.name
val head = Access(name, r.head.vars, Tensor)
val body = r.body

val dimSeq = head.vars.map(v => findUpperBound(v, body))
(DimInfo(head, dimSeq), oldHead -> DimInfo(r.head, dimSeq))
}
}
}.unzip
(dimInfos, headToDimInfoMap.toMap)
}
Expand All @@ -122,7 +158,10 @@ object Convertor {
kind: AccessType
): Map[Access, Rule] = {
dimInfoMap.collect {
case (old_head, dimInfo) if !lhs.containsByName(old_head.name) =>
case (old_head, dimInfo)
if (!lhs.containsByName(old_head.name) && !lhs.containsByName(
old_head.name.inverseName
)) =>
val head = Access(dimInfo.access.name, dimInfo.access.vars, Tensor)
val body = dimInfo.toSoP
val newBody =
Expand Down
22 changes: 16 additions & 6 deletions src/main/scala/uk/ac/ed/dal/structtensor/parser/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ object Parser {
def variableSeq[$: P]: P[Seq[Variable]] = P(variable.rep(sep = ","))

def name[$: P]: P[String] = P(
CharIn("A-Za-z") ~ CharIn("A-Za-z0-9._").rep ~ (":" ~ CharIn("DUR")).?
CharIn("A-Za-z") ~ CharIn("A-Za-z0-9._").rep ~ "^-1".? ~ (":" ~ CharIn(
"DUR"
)).?
).!

def access[$: P]: P[Access] = P(name.! ~ "(" ~ variableSeq ~ ")").map {
Expand Down Expand Up @@ -123,11 +125,15 @@ object Parser {

def exp_or_const[$: P]: P[Seq[Exp]] =
P(exp) |
P(decimal)
.map(d => Access(d.value.toString(), Seq(), Tensor))
P(decimal ~ "^-1".?.!)
.map({ case (d, inv) =>
Access(d.value.toString() + inv, Seq(), Tensor)
})
.map(Seq(_)) |
P(integer)
.map(d => Access(d.value.toString(), Seq(), Tensor))
P(integer ~ "^-1".?.!)
.map({ case (d, inv) =>
Access(d.value.toString() + inv, Seq(), Tensor)
})
.map(Seq(_)) |
P("(" ~ exp_or_const ~ ")")

Expand All @@ -141,7 +147,11 @@ object Parser {
} | P("(" ~ sop ~ ")")

def rule[$: P]: P[Rule] = P(access ~ ":=" ~ sop).map({ case (a, b) =>
Rule(a, b)
if (a.name.contains("^-1"))
throw new Exception(
"Invalid access name. You can't use ^-1 on the lhs of a computatoin."
)
else Rule(a, b)
}) | P("(" ~ rule ~ ")")

def program[$: P]: P[Seq[Rule]] = P(rule.rep(sep = "\n"))
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/uk/ac/ed/dal/structtensor/utils/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ object Utils {
def redundancyName = s"${s}_RM"
def compressedName = s"${s}_C"
def dimensionName = s"${s}_D"
def inverseName = if (s.endsWith("^-1")) s.dropRight(3) else s"${s}^-1"
def deinversifiedName = if (s.endsWith("^-1")) s.dropRight(3) else s
def toVar: Variable = Variable(s)
def redundancyVars: Variable = s.toVar.redundancyVars
}
Expand Down
2 changes: 0 additions & 2 deletions src/test/resources/correct_test_outputs/LRA_w_body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,8 @@ time_reconstruction = end_reconstruction - start_reconstruction;
cout << time_reconstruction << endl;
cerr << A[0][0] << endl;
cerr << f[0] << endl;
cerr << f[0] << endl;
cerr << B[0][0] << endl;
cerr << g[0] << endl;
cerr << g[0] << endl;
cerr << C[0][0] << endl;
for (size_t i0 = 0; i0 < N; ++i0) {delete[] A[i0];
}delete[] A;
Expand Down
1 change: 0 additions & 1 deletion src/test/resources/correct_test_outputs/LRC_w_body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ time_reconstruction = end_reconstruction - start_reconstruction;
cout << time_reconstruction << endl;
cerr << A[0][0] << endl;
cerr << f[0] << endl;
cerr << f[0] << endl;
for (size_t i0 = 0; i0 < N; ++i0) {delete[] A[i0];
}delete[] A;
delete[] f;
Expand Down
Loading

0 comments on commit eb12f45

Please sign in to comment.