Skip to content

Commit

Permalink
Implement stop condition.
Browse files Browse the repository at this point in the history
  • Loading branch information
romainreuillon committed Apr 19, 2016
1 parent 2cb1877 commit f557217
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 11 deletions.
8 changes: 5 additions & 3 deletions src/main/scala/fr/geocites/micmac/Test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import dynamic._
import concurrent.duration._
import sir._
import util._
import observable._
import stop._

object Test extends App {

Expand Down Expand Up @@ -66,7 +68,7 @@ object Test extends App {

val world = randomNetwork[Context](edges).map( w => MicMacState(w, Vector.empty))

def evolve[M[_]: Monad: RNG: Step] = {
def evolve[M[_]: Monad: RNG: Step: Observable] = {
def updateAirportSIR = Kleisli[M, MicMacState, MicMacState] { state =>
dynamic.updateSIRs[MicMacState](
airportIntegrator,
Expand All @@ -87,7 +89,7 @@ object Test extends App {
destination = dynamic.randomDestination[M],
buildSIR = sir) andThen
dynamic.planeArrivals[M](planeSpeed) andThen
updateStep[M]
updateMaxStepI[M] andThen updateStep[M]
}


Expand All @@ -97,7 +99,7 @@ object Test extends App {
initialise >>=
context.run(
evolve,
stopAfter[Context](100)
or(endOfEpidemy[Context], stopAfter[Context](10000))
)

val initialState = SimulationState(0, new Random(42), None)
Expand Down
12 changes: 6 additions & 6 deletions src/main/scala/fr/geocites/micmac/context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import monocle.state.all._
object context {

@Lenses case class MicMacState(network: Network, flyingPlanes: Vector[Plane])
@Lenses case class SimulationState(step: Long, rng: Random, maxIStep: Option[Long])
@Lenses case class SimulationState(step: Long, rng: Random, maxIStep: Option[MaxIStep])

type Context[X] = State[SimulationState, X]

Expand All @@ -49,13 +49,13 @@ object context {
override def step = SimulationState.step.mod
}

def run[T](step: Kleisli[Context, T, T], stop: Context[Boolean]): Kleisli[Context, T, T] = {
def runStep(result: T, s: SimulationState): (SimulationState, T) = {
val (s1, stop1) = stop.run(s)
def run[T](step: Kleisli[Context, T, T], stop: Kleisli[Context, T, Boolean]): Kleisli[Context, T, T] = {
def runStep(modelState: T, state: SimulationState): (SimulationState, T) = {
val (s1, stop1) = stop.run(modelState)(state)

if(stop1) (s1, result)
if(stop1) (s1, modelState)
else {
val (s2, result2) = step.run(result).run(s1)
val (s2, result2) = step.run(modelState).run(s1)
runStep(result2, s2)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/fr/geocites/micmac/dynamic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,6 @@ object dynamic {
}

def updateStep[M[_]: Monad](implicit step: Step[M]) = step.step.modify(_ + 1)
def stopAfter[M[_]: Monad](s: Long)(implicit step: Step[M]) = step.step.get.map { _ >= s }


}
47 changes: 47 additions & 0 deletions src/main/scala/fr/geocites/micmac/observable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Created by Romain Reuillon on 19/04/16.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package fr.geocites.micmac

import fr.geocites.micmac.context._

import scalaz._
import Scalaz._

object observable {

def totalI(state: MicMacState) =
state.flyingPlanes.map(_.sir.i).sum +
state.network.airports.map(_.sir.i).sum

def updateMaxStepI[M[_]: Monad](implicit obs: Observable[M], step: Step[M]) = Kleisli[M, MicMacState, MicMacState] { state: MicMacState =>
val totalIValue = totalI(state)

def update(step: Long) =
obs.maxIStep.modify {
case Some(current) =>
if (totalIValue > current.value) Some(MaxIStep(step, totalIValue)) else Some(current)
case None => Some(MaxIStep(step, totalIValue))
}

for {
s <- step.step.get
_ <- update(s)
} yield state
}

}
4 changes: 3 additions & 1 deletion src/main/scala/fr/geocites/micmac/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ package object micmac {
def step: Field[M, Long]
}

@Lenses case class MaxIStep(step: Long, value: Double)

@typeclass trait Observable[M[_]] {
def maxIStep: Field[M, Option[Long]]
def maxIStep: Field[M, Option[MaxIStep]]
}

/*trait ModelState[M[_], S] {
Expand Down
56 changes: 56 additions & 0 deletions src/main/scala/fr/geocites/micmac/stop.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Created by Romain Reuillon on 19/04/16.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package fr.geocites.micmac

import fr.geocites.micmac.context.MicMacState

import scalaz._
import Scalaz._

import observable._

object stop {

type StopCondition[M[_]] = Kleisli[M, MicMacState, Boolean]

def endOfEpidemy[M[_]: Monad](implicit observable: Observable[M], step: Step[M]): StopCondition[M] = Kleisli { state =>
def finished(maxIStep: Option[MaxIStep], step: Long) =
maxIStep match {
case Some(maxIStep) =>
if(step >= maxIStep.step + 1) totalI(state) < 1.0
else false
case None => false
}

for {
s <- step.step.get
maxIStep <- observable.maxIStep.get
} yield finished(maxIStep, s)
}

def stopAfter[M[_]: Monad](s: Long)(implicit step: Step[M]): StopCondition[M] = Kleisli { state =>
step.step.get.map { _ >= s }
}

def or[M[_]: Monad](s1: StopCondition[M], s2: StopCondition[M]) =
for {
c1 <- s1
c2 <- s2
} yield c1 || c2

}

0 comments on commit f557217

Please sign in to comment.