Skip to content

Commit

Permalink
add release planning problem to csp
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornregnell committed Jan 7, 2025
1 parent 3330d91 commit e34ae50
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 3 deletions.
82 changes: 80 additions & 2 deletions src/main/scala/05-constraints.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package reqt

import scala.compiletime.ops.double

/** A Scala-embedded DSL for expressing integer constraint satisfaction problems. */
object csp:
def constraints(cs: Constr*): Seq[Constr] = cs.toSeq
Expand Down Expand Up @@ -251,6 +249,7 @@ object csp:
lazy val seq2 = load
lazy val constSeq1 = size


object parseConstraints:
import parseUtils.*
object mk:
Expand Down Expand Up @@ -340,3 +339,82 @@ object csp:
case scala.util.Failure(exception) => (Seq(), exception.getMessage)
case scala.util.Success(value) => (value, "")

end parseConstraints

object releasePlanningProblem:
val requiredEntityTypes = Seq(Release, Feature, Stakeholder, Resource)

def missingEntityTypes(m: Model): Seq[EntType] =
val ets = m.entTypes.toSet
requiredEntityTypes.filterNot(et => ets.contains(et))

def isValidReleasePlan(m: Model): Boolean = missingEntityTypes(m).isEmpty

def apply(m: Model): Seq[Constr] =
if !isValidReleasePlan(m) then Seq() else

val stakeholders = m.ents.filter(_.t == Stakeholder).distinct
val features = m.ents.filter(_.t == Feature).distinct
val releases = m.ents.filter(_.t == Release).distinct
val resources = m.ents.filter(_.t == Resource).distinct

val featureOrder: Seq[Constr] = forAll(features) { f => Var(f.has/Order).in(1 to releases.size) }
val releaseOrder: Seq[Constr] = forAll(releases) { r => Var(r.has/Order).in(1 to releases.size) }

println(releases)

val weightedBenefit: Seq[Constr] = forAll(stakeholders, features): (s, f) =>
Var(f.has/s.has/Benefit) === (Var(s.has/f.has/Benefit) * Var(s.has/Prio))

val featureBenefitSum: Seq[Constr] = forAll(features): f =>
Var(f.has/Benefit) === sumForAll(stakeholders)(s => Var(f.has/s.has/Benefit))

val featureBenefitPerRelease: Seq[Constr] = forAll(releases, features) { (r, f) =>
IfThenElse(Var(f.has/Order) === Var(r.has/Order),
Var(r.has/f.has/Benefit) === Var(f.has/Benefit),
Var(r.has/f.has/Benefit) === 0) }

val benefitPerRelease: Seq[Constr] = forAll(releases): r =>
Var(r.has/Benefit) === sumForAll(features)(f => Var(r.has/f.has/Benefit))

val featureCostPerReleasePerResource: Seq[Constr] =
forAll(releases,features, resources): (r, f, res) =>
IfThenElse(Var(f.has/Order) === Var(r.has/Order),
Var(r.has/res.has/f.has/Cost) === Var(res.has/f.has/Cost),
Var(r.has/res.has/f.has/Cost) === 0)

val resourceCostPerRelease: Seq[Constr] = forAll(releases,resources): (r, res) =>
Var(r.has/res.has/Cost) === sumForAll(features)(f => Var(r.has/res.has/f.has/Cost))

val featureCostPerRelease: Seq[Constr] = forAll(releases,features): (r, f) =>
Var(r.has/f.has/Cost) === sumForAll(resources)(res => Var(r.has/res.has/f.has/Cost))

val costPerRelease: Seq[Constr] = forAll(releases): r =>
Var(r.has/Cost) === sumForAll(features)(f => Var(r.has/f.has/Cost))

val costLimitPerResource: Seq[Constr] = forAll(releases, resources): (r, res) =>
Var(r.has/res.has/Cost) <= Var(res.has/r.has/Capacity)

val totalCostPerRelease: Seq[Constr] = forAll(releases): r =>
Var(r.has/Cost) === sumForAll(resources)(res => Var(r.has/res.has/Cost))

val rs = m.rels

val precedences = rs.collect:
case Rel(e1, Precedes, Model(Vector(e2: Ent))) => Var(e1.has/Order) < Var(e2.has/Order)

val exclusions = rs.collect:
case Rel(e1, Excludes, Model(Vector(e2: Ent))) => Var(e1.has/Order) =/= Var(e2.has/Order)

val couplings = rs.collect:
case Rel(e1, Requires, Model(Vector(e2: Ent))) => Var(e1.has/Order) === Var(e2.has/Order)

val inputConstraints: Seq[Constr] = m.paths.collect:
case AttrPath(links, dest: IntAttr) => Var(AttrTypePath(links, dest.t)) === dest.value

Seq(inputConstraints, featureOrder, releaseOrder, weightedBenefit, featureBenefitSum, featureBenefitPerRelease, benefitPerRelease, featureCostPerReleasePerResource, resourceCostPerRelease, featureCostPerRelease, costPerRelease, costLimitPerResource, totalCostPerRelease, precedences, exclusions, couplings).flatten
end if
end apply
end releasePlanningProblem

end csp
32 changes: 31 additions & 1 deletion src/main/scala/06-examples.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,42 @@ val menu: Map[String, Model] = Map(
"Model with Sections" -> Lauesen.ModelWithSections,
"Task: Hotel Reception Work" -> Lauesen.TaskHotelReceptionWork,
"Prioritization: 100$ test" -> Prioritization.DollarTest,
"Release Plan, simple" -> constraintProblems.releasePlanSimple,
"QUPER Model: StartupQuality" -> QualityModel.StartupQuality,
"Variability Model: Color" -> VariabilityModel.AppearanceVariation,
)

object constraintProblems:
val releasePlanningSimple = ???
val releasePlanSimple = Model(
Stakeholder("X") has (
Prio(1),
Feature("1") has Benefit(4),
Feature("2") has Benefit(2),
Feature("3") has Benefit(1),
),
Stakeholder("Y") has (
Prio(2),
Feature("1") has Benefit(2),
Feature("2") has Benefit(1),
Feature("3") has Benefit(1),
),
Release("A") precedes Release("B"),
Resource("dev") has (
Feature("1") has Cost(10),
Feature("2") has Cost(70),
Feature("3") has Cost(40),
Release("A") has Capacity(100),
Release("B") has Capacity(100),
),
Resource("test") has (
Feature("1") has Cost(40),
Feature("2") has Cost(10),
Feature("3") has Cost(70),
Release("A") has Capacity(100),
Release("B") has Capacity(100),
),
Feature("3") precedes Feature("1")
)

/** Examples from "Software Requirements - Styles and techniques" by S. Lauesen (2002)". */
object Lauesen:
Expand Down

0 comments on commit e34ae50

Please sign in to comment.