"""
Judgment functions transform (activated) arguments into a scalar number.
The idea is to obtain a *reward*, which qualifies or measures the degree to
which the agent's action was aligned with the moral value that is represented
by the argumentation graph.
Argumentation frameworks allow determining a subset of *accepted* arguments,
such as the *grounded extension*. From this subset, we want to compare the
*pros* and *cons* arguments (which respectively defend that the action was
aligned or not aligned with the moral value), and to return a scalar number.
For example, if only *pros* arguments are present in the grounded extension,
the reward may be ``1``; if only *cons* arguments are present, the reward may
be ``0``. A mixture of *pros* and *cons* arguments should return a number
between ``0`` and ``1`` which corresponds to this mixture *and* which allows
the learning agent to effectively learn good behaviours.
The judgment functions implement this measure and propose several ways to
compute it, which have different properties.
A (hopefully good) discussion of these properties can be found at:
https://rchaput.github.io/phdthesis/5-judgments.html#judgments-experiments-ajar
This code is mostly based on the work of Benoît Alcaraz.
"""
from .afdm import AFDM
[docs]
def j_simple(afdm: AFDM, decision: str):
"""
Simply return the ratio of (alive) *pros* over *pros* + *cons*.
"""
pros = len(afdm.arguments_supporting(decision))
cons = len(afdm.arguments_countering(decision))
if (pros + cons) == 0:
# Neither moral nor immoral, we say it is neutral
return 0.5
else:
return pros / (pros + cons)
[docs]
def j_diff(afdm: AFDM, decision: str):
"""
Compares the number of alive *pros* with total *pros*, and returns the
difference with the number of alive *cons* over total *cons*.
"""
alive_pros = len(afdm.arguments_supporting(decision))
total_pros = len(afdm.arguments_supporting(decision, ignore_aliveness=True))
alive_cons = len(afdm.arguments_countering(decision))
total_cons = len(afdm.arguments_countering(decision, ignore_aliveness=True))
pros = alive_pros / total_pros if total_pros != 0 else 0.0
cons = alive_cons / total_cons if total_cons != 0 else 0.0
return pros - cons
[docs]
def j_ratio(afdm: AFDM, decision: str):
"""
Compare the number of (squared) alive *pros* minus alive *cons* with the
known maximum number of activated *pros* and *cons*.
"""
# This function requires a "static" variable, to hold the known maximum
# number. This number is updated each time the function is called.
if not hasattr(j_ratio, 'best_acceptable_count'):
j_ratio.best_acceptable_count = 0
pros = len(afdm.arguments_supporting(decision))
cons = len(afdm.arguments_countering(decision))
top = (pros ** 2) - (cons ** 2)
down = pros + cons
# Update the known max
j_ratio.best_acceptable_count = max(down, j_ratio.best_acceptable_count)
if j_ratio.best_acceptable_count == 0:
return 0.5
else:
return top / j_ratio.best_acceptable_count
[docs]
def j_grad(afdm: AFDM, decision: str):
"""
Create a gradient between 0 and 1, using as many graduations between 0.5
and 1 as possible *pros* arguments, and as many graduations between 0.0 and
0.5 as possible *cons* arguments. Then, advance graduations in a sense or
another based on the number of alive *pros* and *cons*.
"""
total_pros = len(afdm.arguments_supporting(decision, ignore_aliveness=True))
total_cons = len(afdm.arguments_countering(decision, ignore_aliveness=True))
# If there are no possible pros or cons (weird, but might happen), we simply
# return 0.5
if total_pros == 0 or total_cons == 0:
return 0.5
start = 0.5 # "neutral" point
up_step = 0.5 / total_pros # graduations towards 1
down_step = 0.5 / total_cons # graduations towards 0
pros = len(afdm.arguments_supporting(decision))
cons = len(afdm.arguments_countering(decision))
return start + (up_step * pros) - (down_step * cons)
[docs]
def j_offset(afdm: AFDM, decision: str):
"""
Avoid division by 0 by offsetting the number of activated arguments.
"""
pros = len(afdm.arguments_supporting(decision))
cons = len(afdm.arguments_countering(decision))
return min(1.0, (1 + pros) / (1 + cons))