Current scope: arg2p| all classes
|
it.unibo.tuprolog.argumentation.core.primitives.labelling.argument
Coverage Summary for Class: GroundedFixpoint (it.unibo.tuprolog.argumentation.core.primitives.labelling.argument)
| Class | Method, % | Branch, % | Line, % | Instruction, % |
|---|---|---|---|---|
| GroundedFixpoint | 100% (5/5) | 90% (27/30) | 100% (39/39) | 99.5% (381/383) |
| GroundedFixpoint$solve$lambda$0$label$$inlined$sortedBy$1 | 0% (0/1) | |||
| Total | 83.3% (5/6) | 90% (27/30) | 100% (39/39) | 99.5% (381/383) |
package it.unibo.tuprolog.argumentation.core.primitives.labelling.argument
import it.unibo.tuprolog.argumentation.core.dsl.arg2pScope
import it.unibo.tuprolog.argumentation.core.primitives.graph.attackGraph
import it.unibo.tuprolog.argumentation.core.primitives.graph.labelArguments
import it.unibo.tuprolog.argumentation.core.primitives.graph.labelledIds
import it.unibo.tuprolog.argumentation.core.primitives.graph.storedArguments
import it.unibo.tuprolog.argumentation.core.primitives.ids
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.Signature
import it.unibo.tuprolog.solve.primitive.Primitive
import it.unibo.tuprolog.solve.primitive.Solve
/**
* groundedFixpoint: extends the IN/OUT labelling of the active context to the grounded fixpoint, labelling UND
* the arguments left. Each round labels IN the arguments whose attackers are all OUT, then OUT the arguments
* attacked by an IN one; the context is read once and only the new labels are asserted.
*
* Each argument keeps the number of its attackers not OUT: a round only looks at the arguments the previous one
* changed (those attacked by the arguments just labelled), rather than at all the unlabelled ones, which made a
* chain of attacks quadratic. The labels of a round are asserted in the order of the arguments.
*/
internal object GroundedFixpoint : Primitive {
val signature: Signature = Signature("groundedFixpoint", 0)
override fun solve(request: Solve.Request<ExecutionContext>): Sequence<Solve.Response> {
arg2pScope {
val arguments = request.storedArguments()
val position = arguments.withIndex().associate { (index, argument) -> argument.first to index }
val graph = request.attackGraph()
val inIds = request.labelledIds("in").toMutableSet()
val outIds = request.labelledIds("out").toMutableSet()
// an argument of the graph not labelled yet
fun unlabelled(id: Term) = id in position && id !in inIds && id !in outIds
// the attackers not OUT of each unlabelled argument
val pending = HashMap<Term, Int>()
arguments.forEach { (id, _) ->
if (unlabelled(id)) pending[id] = graph.attackers[id].orEmpty().count { it !in outIds }
}
fun label(
ids: Collection<Term>,
name: String,
): List<Term> {
val sorted = ids.sortedBy { position.getValue(it) }
if (sorted.isEmpty()) return sorted
request.labelArguments(listOf(Pair(name, sorted.map { id -> arguments[position.getValue(id)] })))
return sorted
}
// the arguments to label IN, and those to label OUT, in the next round
var candidatesIn: Collection<Term> = pending.filterValues { it == 0 }.keys
var candidatesOut: Collection<Term> =
arguments.map { it.first }.filter { id ->
unlabelled(id) &&
graph.attackers[id].orEmpty().any { it in inIds }
}
do {
val newIn = label(candidatesIn.filter { unlabelled(it) && pending[it] == 0 }.toSet(), "in")
inIds += newIn
val attackedByIn = newIn.flatMap { graph.attacked[it].orEmpty() }
val newOut = label((candidatesOut + attackedByIn).filter { unlabelled(it) }.toSet(), "out")
outIds += newOut
val released = LinkedHashSet<Term>()
newOut.forEach { out ->
graph.attacked[out].orEmpty().forEach { target ->
val count = pending[target] ?: return@forEach
pending[target] = count - 1
if (count == 1) released += target
}
}
candidatesIn = released
candidatesOut = emptyList()
} while (newIn.isNotEmpty() || newOut.isNotEmpty())
label(arguments.map { it.first }.filter { unlabelled(it) }, "und")
}
return sequenceOf(request.replyWith(true))
}
}