Skip to content

Got four basic examples working in Scala. #157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/scala/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ lazy val root: Project = (project in file(".")).
dependsOn(minimal % "test->compile")

// To build and run examples
// run with sbt 'examples/runMain org.openworm.trackercommons.examples.ExampleName'
// where `ExampleName` is one of the example files, e.g. `CountAnimals`
lazy val examples: Project = (project in file("src/examples")).
settings(commonSettings: _*).
dependsOn(root)
31 changes: 31 additions & 0 deletions src/scala/src/examples/CircleTravel.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.openworm.trackercommons.examples

import org.openworm.trackercommons._

object CircleTravel extends ExampleTemplate[String] {
val defaultLocation = ""

def run(args: Array[String]): Either[String, String] = {
val circle = Create.wcon.
setMeta(
Create.meta("circle example").addSoftware( Create.software.name("CircleTravel.scala") )
).
addData({
val w = Create.worm("1").add(0.0, Array(1.0), Array(0.0))
for (i <- 1 until 12)
w.add(i.toDouble, Array(math.cos(i*math.Pi/6)), Array(math.sin(i*math.Pi/6)))
w
}).
setUnits().
result
val output = defaultOutput
ReadWrite.write(circle, output) match {
case Left(err) => Left(err)
case _ => Right(output.getPath)
}
}

def succeed(s: String) {
println(f"Wrote a circularly traveling point animal to $s")
}
}
10 changes: 8 additions & 2 deletions src/scala/src/examples/CountAnimals.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package org.openworm.trackercommons.examples

import org.openworm.trackercommons._

object CountAnimals extends ExampleTemplate[Int] {
val defaultPath = "../../tests/count_animals.wcon"
val defaultLocation = "../../tests/examples/count_animals.wcon"

def run(args: Array[String]): Either[String, Int] = existingFile(args) match {
case Left(msg) => Left(msg)
case Right(file) => Right(-1)
case Right(file) =>
ReadWrite.readOne(file) match {
case Left(err) => Left(err.toString)
case Right(ds) => Right(ds.data.map(_.id).toSet.size)
}
}

def succeed(n: Int) {
Expand Down
11 changes: 8 additions & 3 deletions src/scala/src/examples/ExampleTemplate.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package org.openworm.trackercommons.examples


case class Selected(count: Int, where: String) {}

trait ExampleTemplate[A] {
def run(args: Array[String]): Either[String, A]
def succeed(a: A): Unit
def defaultPath: String
def defaultLocation: String
def defaultOutput: java.io.File = (new java.io.File("result.wcon")).getCanonicalFile

// Expect `run` to call this to get a single file
def existingFile(args: Array[String]): Either[String, java.io.File] = {
val theFileName = args.headOption.getOrElse(defaultPath)
val theFileName = args.headOption.getOrElse(defaultLocation)
val theFile = new java.io.File(theFileName)
if (!theFile.exists) {
Left(Seq(
Expand All @@ -20,7 +25,7 @@ trait ExampleTemplate[A] {
def main(args: Array[String]) {
run(args) match {
case Left(msg) => println(msg); sys.exit(1)
case Right(n) =>
case Right(a) => succeed(a)
}
}
}
70 changes: 70 additions & 0 deletions src/scala/src/examples/MovementSelect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.openworm.trackercommons.examples

import org.openworm.trackercommons._

object MovementSelect extends ExampleTemplate[Selected] {
val defaultLocation = "../../tests/examples/all_movements.wcon"

def sq(x: Double) = x*x

def movesFar(d: Data): Boolean = {
if (d.ts.length < 2) return false

val cx, cy, l = new Array[Double](d.ts.length)
for (i <- l.indices) {
val ni = d.spineN(i)
if (ni < 1) {
cx(i) = Double.NaN
cy(i) = Double.NaN
l(i) = 0
}
else {
cx(i) = if (d.cxs.length == 0) (d.x(i, 0) + d.x(i, ni-1))/2 else d.cxs(i)
cy(i) = if (d.cys.length == 0) (d.y(i, 0) + d.y(i, ni-1))/2 else d.cys(i)
l(i) = math.sqrt(sq(d.x(i, 0) - d.x(i, ni-1)) + sq(d.y(i, 0) - d.y(i, ni-1)))
}
}

val longest = l.max
if (longest == 0) return false // Reject worms without a size

var i0 = 0
while (i0 < cx.length && (cx(i0).isNaN || cy(i0).isNaN)) i0 += 1 // i0 is at first valid position
var i1 = cx.length -1
while (i1 > i0 && (cx(i1).isNaN || cy(i1).isNaN)) i1 -= 1 // i1 at last valid position
if (i1 <= i0) return false // Didn't go anywhere valid

var distmax = 0.0
var i = i0 + 1
while (i <= i1) {
while (cx(i).isNaN || cy(i).isNaN) i += 1
val dist = sq(cx(i) - cx(i0)) + sq(cy(i) - cy(i0))
if (dist > distmax) distmax = dist
i += 1
}
distmax = math.sqrt(distmax)

distmax > longest // We have to have traveled at least one nose-to-tail distance
}

def run(args: Array[String]): Either[String, Selected] = existingFile(args) match {
case Left(msg) => Left(msg)
case Right(file) =>
ReadWrite.readOne(file) match {
case Left(err) => Left(err.toString)
case Right(ds) =>
val selected = ds.groupByIDs().flatMap(d => if (movesFar(d)) Some(d) else None)
val output = defaultOutput
ReadWrite.write(selected, output) match {
case Left(err) => Left(err)
case _ => Right(
Selected(selected.data.length, output.getPath)
)
}
}
}

def succeed(s: Selected) {
println(f"Found ${s.count} records; wrote to ${s.where}")
}
}
6 changes: 5 additions & 1 deletion src/scala/src/main/scala/Create.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ object Create {
for (d <- more) w = w.addData(d)
w
}
def addData(builder: DataBuilder[YesData]): MakeWcon[U, YesData, F] = addData(builder.result)
def addData(builder1: DataBuilder[YesData], builder2: DataBuilder[YesData], more: DataBuilder[YesData]*): MakeWcon[U, YesData, F] =
addData(builder1.result, builder2.result, more.map(_.result): _*)
def dropData: MakeWcon[U, NoData, F] = { stale = false; new MakeWcon[U, NoData, F](building.copy(data = Array.empty), 0) }

def putCustom(key: String, value: Json) = {
Expand Down Expand Up @@ -221,7 +224,8 @@ object Create {
def result(implicit ev: I =:= YesID) = underlying
}

def meta() = new MakeMeta[NoID](Metadata.empty)
def meta(): MakeMeta[NoID] = new MakeMeta[NoID](Metadata.empty)
def meta(id: String): MakeMeta[YesID] = meta().setID(id)

final class MakeLab private[trackercommons] (val result: Laboratory) {
def isEmpty = result.isEmpty
Expand Down
9 changes: 5 additions & 4 deletions src/scala/src/main/scala/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,9 @@ extends AsJson with Customizable[Data] {
}

def reshaped(reshaper: Reshape, magic: Magic = Magic.expand, unshaped: Option[Unshaped] = None): Option[Data] = {
if (reshaper.length != 1) return None
if (reshaper.sizes.length != 1) return None
val nts = reshaper(Array(ts))
if (nts.length == 0) return None
val nxd = reshaper(Array(xDatas))
val nyd = reshaper(Array(yDatas))
val nrx = reshaper(Array(rxs))
Expand Down Expand Up @@ -476,9 +477,9 @@ object Data extends FromJson[Data] {

private def BAD(msg: String): Either[JastError, Nothing] = Left(JastError("Invalid data entries: " + msg))
private def IBAD(id: String, msg: String): Either[JastError, Nothing] =
BAD("Data points for " + id + " have " + msg)
BAD("Data points for " + id + " are wrong because " + msg)
private def MYBAD(id: String, t: Double, msg: String): Either[JastError, Nothing] =
BAD("Data point for " + id + " at time " + t + " has " + msg)
BAD("Data point for " + id + " at time " + t + " is wrong because " + msg)

private[trackercommons] val emptyD = new Array[Double](0)
private[trackercommons] val zeroD = Array(0.0)
Expand Down Expand Up @@ -783,7 +784,7 @@ object Data extends FromJson[Data] {
case Some(us) =>
val u = new Unshaped
val ans = concat(ds, magic, Some(u))
us += u
if (u.mistakes.nonEmpty) us += u
id -> ans
case _ =>
id -> concat(ds, magic)
Expand Down
20 changes: 20 additions & 0 deletions src/scala/src/main/scala/DataSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ extends AsJson with Customizable[DataSet] {
def map(f: Data => Data) = new DataSet(meta, unitmap, data.map(f), files, custom)
def flatMap(f: Data => Option[Data]) = new DataSet(meta, unitmap, data.flatMap(x => f(x)), files, custom)

def groupByIDs(unshaped: Option[collection.mutable.ArrayBuffer[Custom.Unshaped]] = None): DataSet = {
val groups = data.zipWithIndex.groupBy(_._1.id).toMap
if (groups.size == data.length) this
else {
val groupedData = groups.map{ case (_, vs) =>
val ix = vs.map(_._2).min
val ds = vs.map(_._1)
val re = Reshape.sortSet(ds.map(_.ts), deduplicate = true)
if (unshaped.nonEmpty) {
val u = new Custom.Unshaped
val join = Data.join(re, ds, unshaped = Some(u))
if (u.mistakes.nonEmpty) unshaped.get += u
ix -> join
}
else ix -> Data.join(re, ds)
}.toArray.sortBy(_._1).flatMap(_._2)
this.copy(data = groupedData)
}
}

def customFn(f: Json.Obj => Json.Obj) = copy(custom = f(custom))

def json = unitmap.unfix(
Expand Down
4 changes: 2 additions & 2 deletions src/scala/src/main/scala/Reshape.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ object Reshape {
n
}
val elements = {
var a = new Array[Int](count)
var a = new Array[Int](2*count)
var i, j = 0
while (i < indicator.length) { if (indicator(i)) { a(j) = i; j += 1 }; i +=1 }
while (i < indicator.length) { if (indicator(i)) { a(j) = 0; j += 1; a(j) = i; j += 1 }; i += 1 }
a
}
new Reshape(elements, Array(indicator.length))
Expand Down
29 changes: 29 additions & 0 deletions tests/examples/all_movements.wcon
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"metadata": {
"protocol": "Six animals that travel different distances."
},
"units": { "t":"s", "x":"mm", "y":"mm" },
"data": [
{ "id":"1", "t":[0, 1, 2, 3],
"x":[[1,2], [3,4], [5,6], [7,8]],
"y":[[3,4], [4,3], [3,2], [2,3]],
"comment":"Length = sqrt(2), travels more than 6 (include)"
},
{ "id":"2", "t":[0, 1, 2, 3],
"x":[[0,5], [1,6], [0,5], [-1,4]],
"y":[[-3,3], [-3,2], [-3,2], [-2,3]],
"comment":"Length over 6, travels less than 3 (reject)"
},
{ "id":"3", "t":[0], "x":[[1,3]], "y":[[3,5]] },
{ "id":"3", "t":[1], "x":[[2,4]], "y":[[3,5]] },
{ "id":"3", "t":[2], "x":[[3,5]], "y":[[3,5]] },
{ "id":"3", "t":[3], "x":[[4,6]], "y":[[3,5]] },
{ "id":"4", "t":[2], "x":[[-5,-5]], "y":[[3,4]] },
{ "id":"5", "t":[0, 1, 2, 3],
"x":[[2,2,2], [2,2,2], [2,2,2], [2,2,2]],
"y":[[4,4,4], [4,4,4], [4,4,4], [4,4,4]] },
{ "id":"6", "t":[0, 1, 2, 3],
"x":[[4], [4], [4], [4]],
"y":[[2], [2], [2], [2]] }
]
}
27 changes: 27 additions & 0 deletions tests/examples/all_times.wcon
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"metadata": {
"protocol": "Six animals present for different lengths of time."
},
"units": { "t":"s", "x":"mm", "y":"mm" },
"data": [
{ "id":"1", "t":[0, 8, 16, 24, 32, 40],
"x":[[1,2], [3,4], [5,6], [7,8], [9,8], [7,6]],
"y":[[3,4], [4,3], [3,2], [2,3], [1,2], [2,1]]
},
{ "id":"2", "t":[0, 3, 6, 9, 12, 15],
"x":[[8,9], [7,9], [6,9], [6,8], [6,7], [6,6]],
"y":[[7,6], [6,5], [5,4], [4,3], [3,2], [2,1]]
},
{ "id":"3", "t":[0, 1], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{ "id":"3", "t":[5, 11], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{ "id":"3", "t":[15, 16], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{ "id":"3", "t":[19, 26], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{ "id":"3", "t":[33, 34], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{ "id":"4", "t":[2], "x":[[-5,-5]], "y":[[3,4]] },
{ "id":"5", "t":[22], "x":[[-5,-5]], "y":[[3,4]] },
{ "id":"6", "t":[14, 18, 22, 26],
"x":[[-1,-2], [-3,-4], [-5,-6], [-7,-8]],
"y":[[-3,-4], [-4,-3], [-3,-2], [-2,-3]]
}
]
}
12 changes: 12 additions & 0 deletions tests/examples/count_animals.wcon
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"metadata": {
"protocol": "Three animals created by hand in four data points."
},
"units": { "t":"s", "x":"mm", "y":"mm" },
"data": [
{"id":"1", "t":[0], "x":[[1,2]], "y":[[3,4]] },
{"id":"2", "t":[0], "x":[[8,9]], "y":[[7,6]] },
{"id":"1", "t":[1, 2], "x":[[1,3],[2,3]], "y":[[3,5],[4,5]] },
{"id":"3", "t":[2], "x":[[-5,-5]], "y":[[3,4]] }
]
}