Introduction of using the theodias package#

The theodias package provides different classes and methods to apply the theory of dialectical structures (as introduced in Betz (2010) and Betz (2013)).

The provided functionalities are described by two abstract classes. Implementations of DialecticalStructure can be used to create, manipulate and calculate properties of dialectical structures. Implementations of Position represent positions on a sentence pool.

The theodias package includes different implementations of these abstract classes, which differ w.r.t. their runtime performance:

  • DAG (directed acyclic graphs) based dialectical structures: All important properties of the stucture are calculated and stored during instantiation. This implementation is fast for small sentence pools (\(n<15\)).

    • We recommend to use the following standard implementation: DAGDialecticalStructure.

  • BDD (binary decision diagramm) based dialectical structures: Important properties of the structure are calculated by using binary decision trees. This representation is comparably fast for most properties of the graph even if the sentence pool is larger (\(n>10\)). However, for larger sentence pools it will become more difficult to calculate all dialectically consistent positions, axiomatic bases (without a confining source) and minimal positions.

    • We recommend to use the following standard implementation: BDDDialecticalStructure.

How to run this notebook#

There are several possibilities to execute this notebook. You can, for instance,

  1. execute this notebook on Colab: Open In Colab, or

  2. execute this notebook locally in, for instance, JupyterLab. You can download this notebook from here.

Installing libraries#

[ ]:
%pip install theodias
[1]:
# Are we on Colab?
on_colab = 'google.colab' in str(get_ipython())
print(f"Are we on Colab? {'Yes' if on_colab else 'No'}!")
Are we on Colab? No!

Instantiation of dialectical structures and positions#

A dialectical structure is a tupel \(\left<S,A\right>\) of a sentence pool \(S = \{ s_1, s_2, \dots, s_n, \neg s_1, \neg s_2, \dots, \neg s_n \}\) and a set \(A\) of arguments.

An argument \(a=(P_a, c_a)\) is defined as a pair consisting of premises \(P_a \subset S\) and a conclusion \(c_a \in S\).

A dialectical structure is instantiated by specifying the size \(n\) of the sentence pool and a list of arguments, in which sententences are represented numerals. The minus sign is used to indicate the negation of a sentence. For instance, the list [1,3,-4] represents an argument with the premsises \(P_a=\{s_1,s_3\}\) and the conclusion \(\neg s_4\).

[2]:
from theodias import DAGDialecticalStructure

# size of the sentencepool
n = 7
# a list of arguments
arguments = [[1, 3],[1, 4],[1, 5],[1, -6], [2, -4],[2, 5],[2, 6],[2, 7]]
# instantiation of a dialectical structure tau
tau = DAGDialecticalStructure.from_arguments(arguments, n)

A position is a binary belief state that is represented by a subset \(\mathcal{A}\subset S\) over the sentence pool \(S\). The internal representation depends on the implementation. However, independent of their implementation, positions can be instantiated by specifying the set of sentences the agent believes:

[3]:
from theodias import StandardPosition

# believing s_3, s_4 and s_5
belief_state_a = StandardPosition.from_set({3, 4, 5}, n)

# believing s_2 and believing that s_4 is false
belief_state_b = StandardPosition.from_set({2,-4}, n)

The theodias package provides several methods to manipulate dialectical structures and to determine properties of positions with regard to a given dialectical structure. For instance:

[4]:
# implications of a believed sentences w.r.t. the
# inferential relationships encoded in tau
print(f"Implications of {belief_state_a}: {tau.closure(belief_state_a)}")
print(f"Implications of {belief_state_b}: {tau.closure(belief_state_b)}")

# checking whether a position is consistent w.r.t. tau
print(f"Is {belief_state_a} consistent? {tau.is_consistent(belief_state_a)}")
print(f"Is {belief_state_b} consistent? {tau.is_consistent(belief_state_b)}")

# checking whether two positions are consistent with each other (w.r.t tau)
print(f"Are {belief_state_a} and {belief_state_b} consistent with each other?" +
      f" {tau.are_compatible(belief_state_a,belief_state_b)}")
Implications of {3, 4, 5}: {3, 4, 5, -2}
Implications of {2, -4}: {2, 5, 6, 7, -4, -1}
Is {3, 4, 5} consistent? True
Is {2, -4} consistent? True
Are {3, 4, 5} and {2, -4} consistent with each other? False

Important notes#

You can operate with position in the usual set-theoretic fashion:

[5]:
pos1 = StandardPosition.from_set({3, 4, 5}, n)
pos2 = StandardPosition.from_set({4, 5, 6}, n)
# set-theoretic union
print(pos1 | pos2)
# set-theoretic intersection
print(pos1 & pos2)
# set-theoretic difference
print(pos1 - pos2)
{3, 4, 5, 6}
{4, 5}
{3}

Positions are immutable—that is, you cannot change them once instantiated. If you want to change, for instance, the underlying sentence pool, you can reinstantiate the position:

[6]:
# instantiating {3, 4, 5} with a sentence pool of size 5
pos_n5 = StandardPosition.from_set({3, 4, 5}, 5)
# changing the underlying sentence to a larger sentence-pool size.
pos_n6 = StandardPosition.from_set(pos_n5.as_set(), 6)

Since positions are immutable, you can use them as keys in, e.g., dictionaries and form sets of positions.

[7]:
set_of_positions = {pos_n5, pos_n6}

Identity criteria: A position is determined by its sentences and the corresponding sentence pool. Consequently, whether two numerically distinct positions are considered the same (as in :code:pos1 == pos2) does not depend on the implementation.

[8]:
from theodias import SetBasedPosition, BitarrayPosition, NumpyPosition
# instantiating {3, 4, 5} with a sentence pool of size 5 with different implementations

pos_setbased = SetBasedPosition.from_set({3, 4, 5}, 5)
pos_bitarray = BitarrayPosition.from_set({3, 4, 5}, 5)
pos_np = NumpyPosition.from_set({3, 4, 5}, 5)

print(pos_setbased == pos_bitarray)
print(pos_setbased == pos_np)
print(pos_np == pos_bitarray)
True
True
True

A position is bound to a specific sentence pool. If you pass positions to functions that have non-matching sentence pools, the function will raise a ValueError.

[10]:
# size of the sentencepool
n = 3
# a list of arguments
arguments = [[1, 3],[1, 2]]
# instantiation of a dialectical structure tau
tau = DAGDialecticalStructure.from_arguments(arguments, n)

print(tau.is_complete(StandardPosition.from_set({1, 3}, n=3)))
# calling a function with the "same" position based on another sentence pool: Raises a ValueError
print(tau.is_complete(StandardPosition.from_set({1, 3}, n=4)))

Export to JSON#

Serializing theodias objects#

You can serialize theodias positions and dialectical structures and any compounds thereof, as long as the json python module can handle them (e.g., lists, dictionaries).

For instance, the following code will serialize a position:

[12]:
from theodias import StandardPosition
from theodias.util import tau_dumps, tau_dump
from os import getcwd, path

# serializing a position as JSON String
pos_json_str = tau_dumps(StandardPosition.from_set({1,2},4),
                         indent=4)
print(pos_json_str)

# serializing a list of positions
pos_list = [StandardPosition.from_set({1,2},4),
            StandardPosition.from_set({1,3},4),
            StandardPosition.from_set(set(),0)]
print(tau_dumps(pos_list, indent=4))

# serializing a list of position into a file
# only if we are not on colab
if not on_colab:
    output_file_path = path.join(getcwd(),'positions.json')
    with open(file=output_file_path, mode='w') as output_file:
        tau_dump(pos_list, output_file, indent=4)
{
    "n_unnegated_sentence_pool": 4,
    "position": [
        1,
        2
    ]
}
[
    {
        "n_unnegated_sentence_pool": 4,
        "position": [
            1,
            2
        ]
    },
    {
        "n_unnegated_sentence_pool": 4,
        "position": [
            1,
            3
        ]
    },
    {
        "n_unnegated_sentence_pool": 0,
        "position": []
    }
]

If important, you can save the implementation details (module and class name), which can be considered later to deserialize the objects:

[13]:
from theodias import SetBasedPosition
from theodias.util import tau_dumps

# serializing a position as JSON String
pos_json_str = tau_dumps(StandardPosition.from_set({1,2},4),
                         indent=4,
                         serialize_implementation=True)
print(pos_json_str)
{
    "n_unnegated_sentence_pool": 4,
    "position": [
        1,
        2
    ],
    "module_name": "theodias.numpy_implementation",
    "class_name": "NumpyPosition"
}

Deserializing theodias objects#

The deserialization of objects is similarly simple. The implementation details can either be taken from the json file or can be explicitly given via parameters.

[15]:
from theodias import SetBasedPosition, BitarrayPosition
from theodias.util import tau_dumps, tau_loads, tau_load
from os import getcwd, path

# serializing a position as JSON String
pos_json_str = tau_dumps(SetBasedPosition.from_set({1,2},4),
                         indent=4,
                         serialize_implementation=True)
# deserializing it
position = tau_loads(pos_json_str, use_json_specified_type = True )
print(f"{position} of type {type(position)}")

# deserializing it and using another implementation
position = tau_loads(pos_json_str,
                     position_module = 'theodias',
                     position_class = 'BitarrayPosition' )
print(f"{position} of type {type(position)}")

# deserializing tau objects from a file (only if we are not on colab)
if not on_colab:
    input_file_path = path.join(getcwd(),'positions.json')
    with open(file=input_file_path, mode='r') as input_file:
        obj = tau_load(input_file)
    print(obj)
{1, 2} of type <class 'theodias.set_implementation.SetBasedPosition'>
{1, 2} of type <class 'theodias.bitarray_implementation.BitarrayPosition'>
[{1, 2}, {1, 3}, set()]