stat-methods/notebooks/kotlin/Issue-sim.ipynb

571 lines
66 KiB
Plaintext
Raw Normal View History

2023-09-23 09:41:04 +03:00
{
"cells":[
{
"cell_type":"code",
"source":[
"@file:Repository(\"https:\/\/repo.kotlin.link\")\n",
"@file:DependsOn(\"space.kscience:plotlykt-jupyter:0.5.0\")"
],
"execution_count":8,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"HfS15oY8SNHBvPjmbefYtL",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"Plotly.jupyter.notebook()"
],
"execution_count":9,
"outputs":[
{
"data":{
"text\/html":[
"<div style=\"color: blue;\">Plotly notebook integration switch into the legacy mode.<\/div>\n"
]
},
"metadata":{
},
"output_type":"display_data"
}
],
"metadata":{
"datalore":{
"node_id":"YuyqME04fpadggceqziOPH",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"# API"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"Xsajx1IH4TCAM5CqFQWzAF",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"enum class Severity(val penalty: Double){\n",
" MINOR(1.0),\n",
" MAJOR(2.0),\n",
" CRITICAL(3.0)\n",
"}\n",
"\n",
"enum class State{\n",
" OPEN,\n",
" ASSIGNED,\n",
" RESOLVED\n",
"}\n",
"\n",
"data class Issue(val id: String, val dayCreated: Int, val severity: Severity, val complexity: Int, \n",
" var state: State = State.OPEN, var dayAssigned: Int? = null, var dayResolved: Int? = null){\n",
" fun activate(day: Int){ \n",
" state = State.ASSIGNED\n",
" dayAssigned = day\n",
" }\n",
" \n",
" fun resolve(day: Int){\n",
" state = State.RESOLVED\n",
" dayResolved = day\n",
" }\n",
" \n",
" internal fun tryResolve(day: Int){\n",
" if(state == State.ASSIGNED && day >= (dayAssigned ?: 0) + complexity ){\n",
" resolve(day)\n",
" }\n",
" }\n",
"}\n",
"\n",
"class Worker(val name: String){\n",
" var currentIssue: Issue? = null\n",
" private set\n",
" \n",
" fun isBusy(): Boolean = currentIssue != null\n",
" \n",
" fun update(day: Int){\n",
" currentIssue?.tryResolve(day)\n",
" if(currentIssue?.state == State.RESOLVED){\n",
" currentIssue = null\n",
" }\n",
" }\n",
" \n",
" fun assign(day: Int, issue: Issue){\n",
" if(currentIssue != null) error(\"Can't assign work to a worker which is busy\")\n",
" issue.activate(day)\n",
" currentIssue = issue\n",
" }\n",
"}\n",
"\n",
"interface IssueGenerator{\n",
" fun generate(day: Int): List<Issue>\n",
"}\n",
"\n",
"interface Strategy{\n",
" fun selectIssue(day: Int, issues: List<Issue>): Issue?\n",
"}\n",
"\n",
"class WorkResult(val issues: List<Issue>, val workers: Int, val days: Int)\n",
"\n",
"@OptIn(kotlin.ExperimentalStdlibApi::class)\n",
"fun simulate(generator: IssueGenerator, strategy: Strategy, numWorkers: Int = 10, days: Int = 100): WorkResult{\n",
" val workers = (0 until numWorkers).map{Worker(\"worker $it\")}\n",
" val issues = buildList<Issue>{\n",
" for(day in 0 until days){\n",
" \/\/update all workers\n",
" workers.forEach { it.update(day) }\n",
" \/\/generate new issues\n",
" val newIssues = generator.generate(day)\n",
" addAll(newIssues)\n",
" \/\/Select all free workers\n",
" workers.filter { !it.isBusy() }.forEach { worker->\n",
" val unasigned = filter { it.state == State.OPEN }\n",
" val anIssue = strategy.selectIssue(day, unasigned) \/\/select an issue to assign from all unassigned issues\n",
" if(anIssue != null){\n",
" worker.assign(day, anIssue)\n",
" }\n",
" }\n",
" }\n",
" }\n",
" return WorkResult(issues, numWorkers, days)\n",
"}\n",
"\n",
"fun WorkResult.computeLoss(): Double = issues.sumOf { \n",
" ((it.dayResolved ?: days) - it.dayCreated).toDouble()*it.severity.penalty \n",
"} \/ days \/ workers \/ issues.size"
],
"execution_count":34,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"q53dZKxH2jHdS3Tpd8KuXw",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"# Implementations"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"d0pwXmjTuPEru3uKTIDDAj",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"import kotlin.random.Random\n",
"import kotlin.math.pow\n",
"\n",
"\/**\n",
"* Generate [issuesPerDay] random issues per day\n",
"*\/\n",
"class RandomIssueGenerator(seed: Long, val issuesPerDay: Int = 4 ) : IssueGenerator{\n",
" private val random = Random(seed)\n",
" override fun generate(day: Int): List<Issue>{\n",
" return List(issuesPerDay){\n",
" val severity = Severity.values()[random.nextInt(3)]\n",
" val complexity = random.nextInt(15)\n",
" Issue(\"${day}_${it}\", day, severity, complexity)\n",
" }\n",
" }\n",
"}\n",
"\n",
"object TakeOldest: Strategy{\n",
" override fun selectIssue(day: Int, issues: List<Issue>): Issue?{\n",
" return issues.minByOrNull { it.dayCreated }\n",
" }\n",
"}\n",
"\n",
"class TakeRandom(seed: Long): Strategy{\n",
" private val random = Random(seed)\n",
" override fun selectIssue(day: Int, issues: List<Issue>): Issue?{\n",
" if(issues.isEmpty()) return null\n",
" return issues.random(random)\n",
" }\n",
"}\n",
"\n",
"object TakeMostLoss: Strategy{\n",
" override fun selectIssue(day: Int, issues: List<Issue>): Issue?{\n",
" return issues.maxByOrNull { it.severity.penalty*(day - it.dayCreated) }\n",
" }\n",
"}\n"
],
"execution_count":35,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"dgQ1fKp1c6bgUw9JX1KJdU",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"object TakeCritical: Strategy{\n",
" override fun selectIssue(day: Int, issues: List<Issue>): Issue?{\n",
" val maxSeverity = issues.maxByOrNull{it.severity}?.severity ?: return null\n",
" return issues.filter{it.severity == maxSeverity}.random()\n",
" }\n",
"}"
],
"execution_count":36,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"xEy6jukH4Dr3fpL9756qbD",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"# Simulate lossseverity"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"Svw7Q0DAlz7Ry91ALvqtqH",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"val seed = 892L\n",
"val days = 100\n",
"val workers = 40"
],
"execution_count":44,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"0PbQxiuKSnPJJriVmhsa1w",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"## Take oldest"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"Y1k1bR1RBHGwZHYktqsSOy",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"val result = simulate(RandomIssueGenerator(seed, workers),TakeOldest, days = days)\n",
"\/\/result.issues.forEach { println(it)}\n",
"result.computeLoss()"
],
"execution_count":45,
"outputs":[
{
"data":{
"text\/plain":[
"0.09711974999999999"
]
},
"metadata":{
},
"output_type":"display_data"
}
],
"metadata":{
"datalore":{
"node_id":"6DIBRaZJ7DvfwfxWg79r7V",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"## Take random"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"1o8tU8B0YxPnuIBpafb1tW",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"simulate(RandomIssueGenerator(seed, workers),TakeRandom(seed), days = days).computeLoss()"
],
"execution_count":46,
"outputs":[
{
"data":{
"text\/plain":[
"0.0966175"
]
},
"metadata":{
},
"output_type":"display_data"
}
],
"metadata":{
"datalore":{
"node_id":"oFJRRpzc0yJUrQR6zjZGkN",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"markdown",
"source":[
"## Take critical"
],
"attachments":{
},
"metadata":{
"datalore":{
"node_id":"02plMVcMwARYP50RTPRMQo",
"type":"MD",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"simulate(RandomIssueGenerator(seed, workers), TakeCritical, days = days).computeLoss()"
],
"execution_count":47,
"outputs":[
{
"data":{
"text\/plain":[
"0.09535774999999999"
]
},
"metadata":{
},
"output_type":"display_data"
}
],
"metadata":{
"datalore":{
"node_id":"OlWpg3tpsUL34DwfzdGOaf",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
"val seeds = List(1000){Random.nextLong()}\n",
"\n",
"Plotly.plot{\n",
" trace{\n",
" x.numbers = seeds.map{ seed -> simulate(RandomIssueGenerator(seed, workers), TakeOldest, days = days).computeLoss()}\n",
" name = \"oldest\"\n",
" type = TraceType.histogram\n",
" }\n",
" trace{\n",
" x.numbers = seeds.map{ seed -> simulate(RandomIssueGenerator(seed, workers), TakeRandom(seed), days = days).computeLoss()}\n",
" name = \"random\"\n",
" type = TraceType.histogram\n",
" }\n",
" trace{\n",
" x.numbers = seeds.map{ seed -> simulate(RandomIssueGenerator(seed, workers), TakeCritical, days = days).computeLoss()}\n",
" name = \"critical\"\n",
" type = TraceType.histogram\n",
" }\n",
" trace{\n",
" x.numbers = seeds.map{ seed -> simulate(RandomIssueGenerator(seed, workers), TakeMostLoss, days = days).computeLoss()}\n",
" name = \"mostLoss\"\n",
" type = TraceType.histogram\n",
" }\n",
" layout{\n",
" title = \"Loss distribtution\"\n",
" xaxis {\n",
" title = \"Loss\"\n",
" }\n",
" }\n",
"}"
],
"execution_count":48,
"outputs":[
{
"data":{
"text\/html":[
"<html>\n",
" <head>\n",
" <meta charset=\"utf-8\">\n",
" <title>Plotly.kt<\/title>\n",
" <script type=\"text\/javascript\" src=\"https:\/\/cdn.plot.ly\/plotly-1.54.6.min.js\"><\/script>\n",
" <\/head>\n",
" <body>\n",
" <div id=\"space.kscience.plotly.Plot@57f2a52d\">\n",
" <script>if(typeof Plotly !== \"undefined\"){\n",
" Plotly.react(\n",
" 'space.kscience.plotly.Plot@57f2a52d',\n",
" [{\"x\":[0.097238,0.09768075,0.09758599999999999,0.09709999999999999,0.09803624999999999,0.0981315,0.0972505,0.09704349999999999,0.09744225000000001,0.098973,0.0979935,0.099235,0.09705275000000001,0.098345,0.09628274999999999,0.09770750000000002,0.0975145,0.096536,0.097873,0.09619275000000001,0.09800350000000001,0.09729375,0.0977855,0.0978875,0.09697825,0.09807099999999999,0.09793474999999999,0.09575399999999999,0.09745925,0.09642125,0.09770349999999998,0.09661449999999999,0.09582825,0.0994555,0.09634225,0.09843424999999999,0.0972435,0.098655,0.098951,0.09753999999999999,0.09774225,0.09765875,0.09824875,0.09705075,0.09654974999999999,0.09808225,0.0978485,0.098979,0.09710025,0.09868775,0.09868175,0.09800750000000001,0.097865,0.09767825000000001,0.0970325,0.09731725000000001,0.09791825,0.09832300000000001,0.0976815,0.097729,0.09776875,0.09709425000000001,0.098158,0.09704675,0.09746375,0.09828425,0.09861475,0.09771175,0.0975795,0.0963995,0.09766,0.09802625000000001,0.09794625,0.09683925,0.09597425,0.0967735,0.09725700000000001,0.09783675,0.09676825,0.09861625,0.097739,0.098488,0.09640075000000001,0.09731624999999999,0.098628,0.096857,0.09708675,0.0969845,0.09686224999999998,0.09744199999999999,0.0971195,0.09812275,0.09764275,0.09709225,0.09676475000000001,0.0966115,0.09751275000000001,0.09783125,0.09799625000000001,0.09724424999999999,0.09902925,0.09981749999999999,0.09675825,0.096745,0.09828425,0.0975895,0.098267,0.09814275,0.09747225,0.097443,0.097381,0.09716875,0.09860675,0.09909024999999999,0.09871150000000001,0.09711125,0.0988465,0.09872125,0.09723949999999999,0.09716475,0.09720025000000002,0.09683525,0.09756275,0.098544,0.0970995,0.09819425,0.09632075000000001,0.09702424999999999,0.09755924999999999,0.09823825000000001,0.09662649999999999,0.09797225,0.097182,0.09723575,0.0975125,0.09710825,0.09785025,0.09736199999999999,0.098771,0.09835425,0.0960915,0.09762775,0.09688025,0.09728925000000001,0.098235,0.0973605,0.09698375000000001,0.09689199999999999,0.09717350000000001,0.097898,0.0964425,0.098194,0.09741675000000001,0.099253,0.09661725,0.09792474999999999,0.09796849999999999,0.09699575,0.09751225,0.09876824999999999,0.09801875,0.09920125,0.09669425,0.09665399999999999,0.0980955,0.09788825,0.09832849999999999,0.098167,0.09843025,0.0973605,0.0982045,0.09726525000000001,0.09767175,0.0999255,0.09848625,0.09776699999999999,0.09809400000000001,0.09726625,0.096794,0.097857,0.09880549999999999,0.09894800000000001,0.09970775,0.097505,0.0974475,0.09793424999999999,0.09812050000000001,0.09890075,0.097387,0.09650700000000001,0.09822125,0.097197,0.09765425,0.098579,0.09664425,0.09666499999999999,0.10014674999999999,0.0970885,0.096568,0.09786725,0.09719025,0.09788849999999999,0.0978675,0.0966055,0.0972795,0.0973035,0.09679249999999999,0.09844199999999999,0.09690800000000001,0.09781050000000001,0.09780875,0.0967155,0.09774550000000001,0.0990275,0.09912424999999998,0.09750825,0.0966405,0.09703875000000001,0.09830924999999999,0.09692775000000001,0.09744825,0.09832225,0.098084,0.09782,0.09742175,0.09718075000000001,0.0979785,0.0974515,0.1006025,0.09702474999999999,0.09611725,0.09605275,0.0984375,0.0976625,0.09753700000000001,0.09801075000000001,0.098107,0.09721725,0.09710200000000001,0.09584825,0.09790025,0.098486,0.09730575,0.09689375,0.097768,0.097463,0.097003,0.097898,0.095546,0.0981835,0.09779425,0.0981905,0.09820125,0.097082,0.0981315,0.0982245,0.09641124999999999,0.0967825,0.09766725,0.09707125,0.0977655,0.09625974999999999,0.09742949999999999,0.09721774999999999,0.0988125,0.09772225,0.098089,0.09891575,0.09787075,0.09657675,0.09754299999999999,0.09698400000000001,0.09732675,0.096367,0.0976245,0.09697275,0.0980115,0.097297,0.09910675000000001,0.0979635,0.09718125000000001,0.0978415,0.096388,0.09762675,0.09867625000000001,0.09889674999999999,0.09786375000000001,0.09815625,0.097608,0.09802675000000001,0.098292,0.0981775,0.09718175,0.09646625,0.09681425,0.09803674999999999,0.097326,0.09670375,0.09806075,0.09832225,0.09835325,0.09717925,0.09637100000000001,0.09791225,0.09777549999999999,0.09806575000000001,0.09822
" {\"title\":{\"text\":\"Loss distribtution\"},\"xaxis\":{\"title\":\"Loss\"}},\n",
" {}\n",
" ); \n",
"} else {\n",
" console.error(\"Plotly not loaded\")\n",
"}<\/script>\n",
" <\/div>\n",
" <\/body>\n",
"<\/html>\n"
]
},
"metadata":{
},
"output_type":"display_data"
}
],
"metadata":{
"datalore":{
"node_id":"8YBi6EGLGMTut3UJnXFwje",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
],
"execution_count":null,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"BJh0bE0pe74i5TNd5tbHYB",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
},
{
"cell_type":"code",
"source":[
],
"execution_count":null,
"outputs":[
],
"metadata":{
"datalore":{
"node_id":"VjkEU5JZvKtD1phWDOW0q3",
"type":"CODE",
"hide_input_from_viewers":true,
"hide_output_from_viewers":true
}
}
}
],
"metadata":{
"kernelspec":{
"display_name":"Kotlin",
"language":"kotlin",
"name":"kotlin"
},
"datalore":{
"computation_mode":"JUPYTER",
"packages":[
],
"report_row_ids":[
],
"version":3
}
},
"nbformat":4,
"nbformat_minor":4
}