""" Syllogistic convenience class.
"""
import numpy as np
from ..item import Item
from .task_encoder_syl import SyllogisticTaskEncoder
from .resp_encoder_syl import SyllogisticResponseEncoder
#: List of syllogistic task identifiers.
SYLLOGISMS = []
for _prem1 in ['A', 'I', 'E', 'O']:
for _prem2 in ['A', 'I', 'E', 'O']:
for _fig in ['1', '2', '3', '4']:
SYLLOGISMS.append(_prem1 + _prem2 + _fig)
#: List of syllogistic responses.
RESPONSES = []
for _quant in ['A', 'I', 'E', 'O']:
for _direction in ['ac', 'ca']:
RESPONSES.append(_quant + _direction)
RESPONSES.append('NVC')
#: List of valid syllogisms
VALID_SYLLOGISMS = [
'AA1', 'AA2', 'AA4', 'AE1', 'AE2', 'AE3', 'AE4', 'AI2', 'AI4', 'AO3', 'AO4', 'EA1', 'EA2',
'EA3', 'EA4', 'EI1', 'EI2', 'EI3', 'EI4', 'IA1', 'IA4', 'IE1', 'IE2', 'IE3', 'IE4', 'OA3',
'OA4'
]
#: List of invalid syllogisms
INVALID_SYLLOGISMS = [
'AA3', 'AI1', 'AI3', 'AO1', 'AO2', 'EE1', 'EE2', 'EE3', 'EE4', 'EO1', 'EO2', 'EO3', 'EO4',
'IA2', 'IA3', 'II1', 'II2', 'II3', 'II4', 'IO1', 'IO2', 'IO3', 'IO4', 'OA1', 'OA2', 'OE1',
'OE2', 'OE3', 'OE4', 'OI1', 'OI2', 'OI3', 'OI4', 'OO1', 'OO2', 'OO3', 'OO4'
]
#: Mapping of syllogisms to logically valid (first-order logics) conclusions
SYLLOGISTIC_FOL_RESPONSES = {
'AA1': ['Aac', 'Iac', 'Ica'],
'AA2': ['Aca', 'Iac', 'Ica'],
'AA3': ['NVC'],
'AA4': ['Iac', 'Ica'],
'AI1': ['NVC'],
'AI2': ['Iac', 'Ica'],
'AI3': ['NVC'],
'AI4': ['Iac', 'Ica'],
'AE1': ['Eac', 'Eca', 'Oac', 'Oca'],
'AE2': ['Oac'],
'AE3': ['Eac', 'Eca', 'Oac', 'Oca'],
'AE4': ['Oac'],
'AO1': ['NVC'],
'AO2': ['NVC'],
'AO3': ['Oca'],
'AO4': ['Oac'],
'IA1': ['Iac', 'Ica'],
'IA2': ['NVC'],
'IA3': ['NVC'],
'IA4': ['Iac', 'Ica'],
'II1': ['NVC'],
'II2': ['NVC'],
'II3': ['NVC'],
'II4': ['NVC'],
'IE1': ['Oac'],
'IE2': ['Oac'],
'IE3': ['Oac'],
'IE4': ['Oac'],
'IO1': ['NVC'],
'IO2': ['NVC'],
'IO3': ['NVC'],
'IO4': ['NVC'],
'EA1': ['Oca'],
'EA2': ['Eac', 'Eca', 'Oac', 'Oca'],
'EA3': ['Eac', 'Eca', 'Oac', 'Oca'],
'EA4': ['Oca'],
'EI1': ['Oca'],
'EI2': ['Oca'],
'EI3': ['Oca'],
'EI4': ['Oca'],
'EE1': ['NVC'],
'EE2': ['NVC'],
'EE3': ['NVC'],
'EE4': ['NVC'],
'EO1': ['NVC'],
'EO2': ['NVC'],
'EO3': ['NVC'],
'EO4': ['NVC'],
'OA1': ['NVC'],
'OA2': ['NVC'],
'OA3': ['Oac'],
'OA4': ['Oca'],
'OI1': ['NVC'],
'OI2': ['NVC'],
'OI3': ['NVC'],
'OI4': ['NVC'],
'OE1': ['NVC'],
'OE2': ['NVC'],
'OE3': ['NVC'],
'OE4': ['NVC'],
'OO1': ['NVC'],
'OO2': ['NVC'],
'OO3': ['NVC'],
'OO4': ['NVC']
}
[docs]def encode_task(task):
""" Encodes a syllogistic task.
Parameters
----------
task : list(list(str))
List representation of the syllogism (e.g., [['All', 'A', 'B'], ['Some', 'B', 'C']]).
Returns
-------
str
Syllogistic task encoding (e.g., 'AI1').
"""
return SyllogisticTaskEncoder.encode_task(task)
[docs]def encode_response(response, task):
""" Encodes a response to its syllogistic encoding.
Parameters
----------
response : list(str)
Syllogistc response in list representation (e.g., ['All', 'A', 'C'])
task : list(list(str))
Syllogistic task in list representation (e.g., [['All', 'A', 'B'], ['Some', 'B', 'C']]).
Returns
-------
str
Syllogistic response encoding (e.g., 'Aac').
"""
return SyllogisticResponseEncoder.encode_response(response, task)
[docs]def decode_response(enc_response, task):
""" Decodes an encoded syllogistic response by transforming it to the
corresponding tuple representation and inserting the appropriate terms.
Parameters
----------
enc_response : str
Encoded syllogistic response (e.g., 'Aac').
task : list(str)
Syllogistic task in the tuple list representation (e.g.,
[['Some', 'models', 'managers'], ['All', 'models', 'clerks']]).
Returns
-------
list
List representation of the response to decode.
"""
if enc_response == 'NVC':
return [['NVC']]
if enc_response == ['NVC']:
return [enc_response]
if enc_response == [['NVC']]:
return enc_response
obj_a = set(task[0][1:]) - set(task[1][1:])
obj_c = set(task[1][1:]) - set(task[0][1:])
quant = enc_response[0].replace('A', 'All').replace(
'I', 'Some').replace('O', 'Some not').replace('E', 'No')
if enc_response[1:] == 'ac':
return [[quant, list(obj_a)[0], list(obj_c)[0]]]
return [[quant, list(obj_c)[0], list(obj_a)[0]]]
def dataset_to_matrix(dataset):
""" Convert a training dataset (e.g., pre_train) into a corresponding matrix representation of
shape (576 x n_subj).
Parameters
----------
dataset : list(list(...))
Training dataset (e.g., pre_train) represented as a list of subjects represented as lists
of tasks and corresponding responses.
Returns
-------
mat : np.ndarray
Dataset matrix of shape (576 x n_subj). The first dimension reflects the different response
option to syllogistic problems (64 tasks x 9 responses = 576 options). The resulting
matrix is normalized so that each block of 9 options (i.e., tasks) is normalized to sum
up to 1. Each column therefore sums up to 64.
"""
# Check for valid arguments
if not isinstance(dataset, list):
raise ValueError('Invalid dataset. Must be of type list')
mat = np.zeros((576, len(dataset)))
for subj_idx, subj_data in enumerate(dataset):
subj_mat = np.zeros((64, 9))
for task_data in subj_data:
syllog = Syllogism(task_data['item'])
enc_resp = syllog.encode_response(task_data['response'])
syl_idx = SYLLOGISMS.index(syllog.encoded_task)
rsp_idx = RESPONSES.index(enc_resp)
subj_mat[syl_idx, rsp_idx] += 1
# Normalize subject response data and add to overall result matrix
subj_mat = subj_mat / subj_mat.sum(axis=1, keepdims=True)
mat[:, subj_idx] = subj_mat.reshape(-1)
return mat
[docs]class Syllogism():
""" Syllogistic helper class. Facilitates the extraction of premise
information as well as encoding and decoding of responses.
"""
def __init__(self, item):
""" Constructs the Syllogism based on a given task item.
Parameters
----------
item : ccobra.Item
CCOBRA task item container to base this Syllogism helper on.
"""
#: Instance of the item the Syllogism is constructed on. The instance
#: is copied in order to prevent reference mismatches from happening.
self.item = Item(
item.identifier,
item.domain,
item.task_str,
item.response_type,
item.choices_str,
item.sequence_number)
#: Reference to the task the Syllogism is constructed on.
self.task = self.item.task
#: String representation of the task
self.encoded_task = encode_task(self.task)
#: List representation of the first premise
self.p1 = self.task[0]
#: List representation of the second premise
self.p2 = self.task[1]
#: Quantifier of the first premise
self.quantifier_p1 = self.task[0][0]
#: Quantifier of the second premise
self.quantifier_p2 = self.task[1][0]
#: Figure of the syllogism
self.figure = int(self.encoded_task[-1])
# Figure out the figure and identify the terms
if self.figure == 1:
self.A, self.B, self.C = self.task[0][1], self.task[0][2], self.task[1][2]
elif self.figure == 2:
self.A, self.B, self.C = self.task[0][2], self.task[0][1], self.task[1][1]
elif self.figure == 3:
self.A, self.B, self.C = self.task[0][1], self.task[0][2], self.task[1][1]
elif self.figure == 4:
self.A, self.B, self.C = self.task[0][2], self.task[0][1], self.task[1][2]
[docs] def encode_response(self, response):
""" Encodes a given syllogistic response based on the information
contained in the premises.
Parameters
----------
response : list(str)
Syllogistic response in list representation (e.g.,
['All', 'clerks', 'managers']).
Returns
-------
str
String encoding of the response (e.g., 'Aac').
"""
return encode_response(response, self.item.task)
[docs] def decode_response(self, encoded_response):
""" Decodes a syllogistic response in string representation based on
the information stored in the syllogism's premises.
Parameters
----------
encoded_response : str
Encoded syllogistic response (e.g., 'Aac').
Returns
-------
list(str)
List representation of the encoded response (e.g.,
['All', 'clerks', 'managers']).
"""
return decode_response(encoded_response, self.item.task)
[docs] def is_valid_syllogism(self):
""" Returns true if syllogism is valid, i.e., has a logically valid conclusion.
Returns
-------
bool
True, if syllogism is valid, i.e., has a logically valid conclusion. False otherwise.
"""
return self.encoded_task in VALID_SYLLOGISMS
[docs] def logically_valid_conclusions(self):
""" Returns the list of logically valid (according to first-order logics) conclusions for
the syllogism.
Returns
-------
list(str)
List of logically valid conclusions.
"""
return SYLLOGISTIC_FOL_RESPONSES[self.encoded_task]
def __str__(self):
""" Constructs a string representation for the Syllogism object.
Returns
-------
str
String representation containing the premise, quantifier, figure,
and term information.
"""
rep = 'Syllogism:\n'
rep += '\ttask: {}\n'.format(self.task)
rep += '\tencoded_task: {}\n'.format(self.encoded_task)
rep += '\tp1: {}\n'.format(self.p1)
rep += '\tp2: {}\n'.format(self.p2)
rep += '\tquantifier_p1: {}\n'.format(self.quantifier_p1)
rep += '\tquantifier_p2: {}\n'.format(self.quantifier_p2)
rep += '\tfigure: {}\n'.format(self.figure)
rep += '\tTerms:\n'
rep += '\t\tA: {}\n'.format(self.A)
rep += '\t\tB: {}\n'.format(self.B)
rep += '\t\tC: {}\n'.format(self.C)
return rep