Frege, Machine Learning, and Logic: Fun with Python
Tags: python, machinelearning, ai, beginners
Published: 2024-06-03 04:05:55

As a philosophy graduate and as someone who has a newfound interest in machine learning and AI, Frege's work on logic and language has crossed my mind on more than one occasion. Indeed, Frege's contributions are seen as foundational in the field of philosophy. Translating _some of it_ into modern English and demonstrating its principles using Python can make these concepts more accessible and practical for contemporary audiences. By constructing Frege’s logic from the ground up using basic axioms and principles, we can create a valuable resource for learning and applying logic. For the most part, we will be relying on my new translation of Frege's Über Sinn und Bedeutung. I must confess, however, that the technicalities of a philosophically rich article such as Über Sinn und Bedeutung required more than what the transformer used for the translation could offer (heat-tip to GPT4o for assisting).

Well, without further ado, this is how I spent my free time this Sunday. I apologize if my musings are fragmented at times, I am a terse writer.
 
### Using Python to Explore Frege's Logic
The gist of my train of thought went something like this-before I realized that I was out of my depth--haha!

1. **Translation and Explanation**:
   - Translate key articles of Frege's work into modern English.
   - Provide clear explanations and context for each translated section.

2. **Implementing Logic in Python**:
   - Translate Frege's logical principles into Python code.
   - Use libraries such as `sympy` for symbolic mathematics and logic operations.
   - Demonstrate the implementation of basic logical operations, axioms, and theorems.

3. **Constructing Logical Structures**:
   - Build up from basic logical operations to more complex structures.
   - Show how to derive conclusions from premises using Frege's methods.
   - Create examples and exercises to illustrate each concept.

4. **Practical Applications**:
   - Apply these logical structures to solve real-world problems.
   - Show how logic can be used in programming, artificial intelligence, and data science.

### Example Outline:

#### Part 1: Introduction to Frege's Logic
- Overview of Frege's Contributions
- Key Concepts: Sense, Reference, Function, Concept, Relation

#### Part 2: Translating and Explaining Frege's Texts
- **Detailed Translations of Key Sections**
- **Explanations and Context**

#### Part 3: Implementing Basic Logic in Python
- Logical Operations: AND, OR, NOT
- Truth Tables and Logical Equivalences

#### Part 4: Constructing Logical Proofs
- Using Axioms and Inference Rules
- Proof Strategies and Techniques

#### Part 5: Practical Applications of Logic
- Programming with Logic
- Logic in AI and Data Science

### Example Code Snippet
Here’s a simple example to get started with implementing Huggigface transformers and OpenAI to help translate Frege's article into more contemporary prose:
```python
from transformers import MarianMTModel, MarianTokenizer
from pdfminer.high_level import extract_text
import torch
import nltk

# Download NLTK data
nltk.download('punkt')

# Path to the PDF file
file_path = "C:/Users/sefer/OneDrive/Desktop/Frege - Sinn & Bedeutung.pdf"

# Extract text from the PDF
text = extract_text(file_path)

# Function to nest sentences within a limit
def nest_sentences(document):
    nested = []
    sent = []
    length = 0
    for sentence in nltk.sent_tokenize(document):
        length += len(sentence)
        if length < 1024:  # Ensure the length stays within the limit
            sent.append(sentence)
        else:
            nested.append(" ".join(sent))
            sent = [sentence]
            length = len(sentence)
    if sent:
        nested.append(" ".join(sent))
    return nested

# Load the model and tokenizer
model_name = 'Helsinki-NLP/opus-mt-de-en'  # Example: German to English translation model
tokenizer = MarianTokenizer.from_pretrained(model_name)
model = MarianMTModel.from_pretrained(model_name)

# Move model to GPU if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

# Function to translate text
def translate(text, model, tokenizer):
    # Tokenize the input text
    inputs = tokenizer.encode(text, return_tensors="pt", truncation=True).to(device)
    # Generate translation
    translated = model.generate(inputs, max_length=512)
    # Decode the translation
    translated_text = tokenizer.decode(translated[0], skip_special_tokens=True)
    return translated_text

# Nest the sentences
nested_sentences = nest_sentences(text)

# Translate nested sentences and combine them
translated_texts = [translate(sentence, model, tokenizer) for sentence in nested_sentences]
full_translated_text = " ".join(translated_texts)

# Print the translated text
print(full_translated_text)
```
And next here’s a simple example to get started with implementing basic logic in Python using `sympy`:

```python
import sympy as sp

# Define logical variables
A, B = sp.symbols('A B')

# Logical operations
AND = sp.And(A, B)
OR = sp.Or(A, B)
NOT_A = sp.Not(A)

# Print truth tables
print("Truth Table for AND:")
print(sp.simplify_logic(AND, form='dnf'))

print("Truth Table for OR:")
print(sp.simplify_logic(OR, form='dnf'))

print("Truth Table for NOT A:")
print(sp.simplify_logic(NOT_A, form='dnf'))

# Define an implication
implication = sp.Implies(A, B)
print("Implication A => B:")
print(sp.simplify_logic(implication, form='dnf'))
```

### Translating and Explaining a Key Concept

**Original Text**:
> Die Bedeutung eines Eigennamens ist der Gegenstand selbst, den wir damit bezeichnen; die Vorstellung, welche wir dabei haben, ist ganz subjektiv; dazwischen liegt der Sinn, der zwar nicht mehr subjektiv wie die Vorstellung, aber doch auch nicht der Gegenstand selbst ist.

**Translated Text**:
> The meaning of a proper name is the object itself that we designate with it; the idea we have in mind is entirely subjective; between them lies the sense, which is not as subjective as the idea, but is also not the object itself.

**Explanation**:
Frege distinguishes between three elements:
- **Meaning (Bedeutung)**: The actual object referred to by the name.
- **Idea (Vorstellung)**: The subjective mental image or concept of the object.
- **Sense (Sinn)**: The way the object is presented, which is shared among speakers of the language but is not the object itself.

Combining the translation of Frege's philosophical texts with practical implementations in Python can create an educational and insightful project. It bridges historical philosophical concepts with modern computational applications, making the abstract principles of logic tangible and useful in various fields.

### Next Steps:

1. **Identify Key Logical Concepts**:
   - Start with basic logical operations and concepts such as propositions, logical connectives (AND, OR, NOT), and truth values.

2. **Implement Basic Logic in Python**:
   - Create functions to represent and manipulate these concepts.
   - Use `sympy` for symbolic mathematics and logical operations.

3. **Expand to More Complex Logical Structures**:
   - Implement more advanced logical operations and proof techniques.
   - Demonstrate how to derive conclusions from premises.

### Example Code to Implement Basic Logic:

Here’s an example of how to start implementing basic logical operations in Python:

```python
import sympy as sp

# Define logical variables
A, B = sp.symbols('A B')

# Logical operations
AND = sp.And(A, B)
OR = sp.Or(A, B)
NOT_A = sp.Not(A)

# Print truth tables
print("Truth Table for AND:")
print(sp.simplify_logic(AND, form='dnf'))

print("Truth Table for OR:")
print(sp.simplify_logic(OR, form='dnf'))

print("Truth Table for NOT A:")
print(sp.simplify_logic(NOT_A, form='dnf'))

# Define an implication
implication = sp.Implies(A, B)
print("Implication A => B:")
print(sp.simplify_logic(implication, form='dnf'))
```
This code demonstrates the basic logical operations and prints out their truth tables. We can build upon this foundation by adding more complex logical structures and exploring how to construct proofs and logical arguments in the spirit of Frege's work.

***
 
### Frege’s Views on Logic and Underlying Assumptions

1. **Concept and Object**:
   - **Concept (Begriff)**: A function that returns a truth value when applied to an object. In modern terms, this is akin to a predicate.
   - **Object (Gegenstand)**: An entity that a concept can be applied to.

2. **Function and Argument**:
   - **Function (Funktion)**: Frege viewed functions as mappings from arguments to values. For Frege, functions in logic are similar to mathematical functions.
   - **Argument (Argument)**: The input to a function, which when applied to the function yields a value.

3. **Quantifiers**:
   - **Universal Quantifier (∀)**: Indicates that a property holds for all elements in a domain.
   - **Existential Quantifier (∃)**: Indicates that there is at least one element in the domain for which a property holds.

4. **Logical Form and Syntax**:
   - Frege introduced a formal language with a strict syntax to represent logical relations clearly.
   - He used a two-dimensional notation for functions and quantifiers, which is quite different from our linear text-based programming languages.

***

### Mapping Frege’s Logic to Python Functions

#### 1. Concepts as Predicates
In Python, we can represent concepts (predicates) as functions that return boolean values.

```python
def is_even(n):
    return n % 2 == 0
```

#### 2. Functions and Arguments
Functions in Python can directly represent Frege’s idea of mapping arguments to values.

```python
def add(x, y):
    return x + y
```

#### 3. Universal and Existential Quantifiers
Quantifiers can be represented using functions that iterate over a domain.

- **Universal Quantifier (∀)**:

```python
def for_all(domain, predicate):
    return all(predicate(x) for x in domain)

# Example usage:
numbers = [2, 4, 6, 8]
print(for_all(numbers, is_even))  # True, because all numbers are even
```

- **Existential Quantifier (∃)**:

```python
def there_exists(domain, predicate):
    return any(predicate(x) for x in domain)

# Example usage:
numbers = [1, 3, 4, 7]
print(there_exists(numbers, is_even))  # True, because there exists at least one even number
```

### Example: Translating a Logical Expression

Frege might express a logical statement like "For all x, if x is a human, then x is mortal" as:

for all x (Human(x) > Mortal(x))

In Python, assuming we have predicates `is_human` and `is_mortal`, we can translate this as:

```python
def is_human(x):
    # Placeholder implementation
    return x in ["Socrates", "Plato", "Aristotle"]

def is_mortal(x):
    # Placeholder implementation
    return x in ["Socrates", "Plato", "Aristotle"]

def implies(p, q):
    return not p or q

def for_all_humans_implies_mortal(domain):
    return for_all(domain, lambda x: implies(is_human(x), is_mortal(x)))

# Example usage:
entities = ["Socrates", "Plato", "Aristotle", "Zeus"]
print(for_all_humans_implies_mortal(entities))  # True, assuming Zeus is not human and others are humans and mortal
```
***

Eventually after a few hours, OpenAI's finest and I had a project folder filled with the basic logical laws spelled out, including some more idiosyncratic contributions by the likes of Abalard, Ockham, Frege, and etc., etc. Long story short, things are harder up close:

This is what the philosophica_logic module looks like so far:
```python
# philosophical_logic/__init__.py

from .aristotelian_logic import *
from .frege import *
from .logic import *
from .logical_laws import *
from .modal import *
from .propositional import *
```
```python
# philosophical_logic/logic.py

def implies(p, q):
    return not p or q

def and_op(p, q):
    return p and q

def or_op(p, q):
    return p or q

def not_op(p):
    return not p

def for_all(domain, predicate):
    return all(predicate(x) for x in domain)

def there_exists(domain, predicate):
    return any(predicate(x) for x in domain)

def possibly(p):
    return p

def necessarily(p):
    return p
```
```python
# philosophical_logic/logical_laws.py

def involution(p):
    return p

def de_morgan_conjunction(p, q):
    return not (p and q) == (not p or not q)

def de_morgan_disjunction(p, q):
    return not (p or q) == (not p and not q)

def commutativity_and(p, q):
    return p and q == q and p

def commutativity_or(p, q):
    return p or q == q or p

def associativity_and(p, q, r):
    return (p and (q and r)) == ((p and q) and r)

def associativity_or(p, q, r):
    return (p or (q or r)) == ((p or q) or r)

def distributivity_and_or(p, q, r):
    return (p and (q or r)) == ((p and q) or (p and r))

def distributivity_or_and(p, q, r):
    return (p or (q and r)) == ((p or q) and (p or r))

def law_of_excluded_middle(p):
    return p or not p

def law_of_non_contradiction(p):
    return not (p and not p)

def idempotence_and(p):
    return p and p == p

def idempotence_or(p):
    return p or p == p

def identity_laws(p):
    return p == p

def transposition_laws(p, q):
    return (p implies q) == (not q implies not p)

def definition_of_conditional(p, q):
    return (p implies q) == (not p or q)

def definition_of_biconditional(p, q):
    return (p == q) == ((p implies q) and (q implies p))

def modus_ponens(p, q):
    return (p implies q) and p

def modus_tollens(p, q):
    return (p implies q) and not q

def syllogism(p, q, r):
    return (p implies q) and (q implies r) and (p implies r)

def disjunctive_syllogism(p, q):
    return (p or q) and not p

def transitivity(p, q, r):
    return (p implies q) and (q implies r) and (p implies r)

def simplification(p, q):
    return p and q

def addition(p, q):
    return p or q

def constructive_dilemma(p, q, r):
    return (p or q) and (p implies r) and (q implies r)

def second_law_constructive_dilemma(p, q, r, s):
    return (p implies q) and (r implies s) and (p or r) and (q or s)

def destructive_dilemma(p, q, r, s):
    return (p implies q) and (r implies s) and (not q or not s) and (not p or not r)

def law_of_contrapositive(p, q):
    return (p implies q) and (not p implies not q)

def exportation(p, q, r):
    return (p and q implies r) == (p implies (q and r))

def negation_of_conditional(p, q):
    return not (p implies q) == (p and not q)

def absorption_laws_and(p, q):
    return p and (p or q) == p

def absorption_laws_or(p, q):
    return p or (p and q) == p

def permutation_law(p, q, r):
    return (p implies (q implies r)) == (q implies (p implies r))

def expansion_laws_or(p, q):
    return (p implies q) == ((p or q) == q)

def expansion_laws_and(p, q):
    return (p implies q) == ((p and q) == p)

def known_true(p):
    return True or p == True

def known_false(p):
    return False or p == p

def true_and(p):
    return True and p == p

def false_and(p):
    return False and p == False

def contradiction(p):
    return p and not p == False

def tautology(p):
    return p or not p == True
```
```python
# philosophical_logic/modal.py

def possibly(p):
    return p

def necessarily(p):
    return p

# Placeholder for more modal logic functions
```
```python
# philosophical_logic/propositional.py

def implies(p, q):
    return not p or q

def and_op(p, q):
    return p and q

def or_op(p, q):
    return p or q

def not_op(p):
    return not p

def for_all(domain, predicate):
    return all(predicate(x) for x in domain)

def there_exists(domain, predicate):
    return any(predicate(x) for x in domain)

# Placeholder for more propositional logic functions
```
```python
# stoic_logic.py

# Define logical connectives
def implies(p, q):
    return not p or q

def and_op(p, q):
    return p and q

def or_op(p, q):
    return p or q

def not_op(p):
    return not p

# Indemonstrable Arguments
def modus_ponens(p, q):
    """
    If p, then q. p. Therefore, q.
    """
    return implies(p, q) and p

def modus_tollens(p, q):
    """
    If p, then q. Not q. Therefore, not p.
    """
    return implies(p, q) and not_op(q)

def modus_ponendo_tollens(p, q):
    """
    Not both p and q. p. Therefore, not q.
    """
    return not and_op(p, q) and p

def strong_modus_tollendo_ponens(p, q):
    """
    Either p or q. Not p. Therefore, q.
    """
    return or_op(p, q) and not_op(p)

def strong_modus_ponendo_tollens(p, q):
    """
    Either p or q. p. Therefore, not q.
    """
    return or_op(p, q) and p

# Example usage
if __name__ == "__main__":
    p = True
    q = False

    print("Modus Ponens:", modus_ponens(p, q))
    print("Modus Tollens:", modus_tollens(p, q))
    print("Modus Ponendo Tollens:", modus_ponendo_tollens(p, q))
    print("Strong Modus Tollendo Ponens:", strong_modus_tollendo_ponens(p, q))
    print("Strong Modus Ponendo Tollens:", strong_modus_ponendo_tollens(p, q))
```
```python
# philosophical_logic/frege.py

class Term:
    def __init__(self, sense, reference):
        self.sense = sense  # Sinn
        self.reference = reference  # Bedeutung

    def __str__(self):
        return f"Term(sense={self.sense}, reference={self.reference})"

# Example terms
morning_star = Term("The star seen in the morning", "Venus")
evening_star = Term("The star seen in the evening", "Venus")

def compare_terms(term1, term2):
    same_reference = term1.reference == term2.reference
    same_sense = term1.sense == term2.sense
    return same_reference, same_sense

# Logical functions applied to terms
def implies(term1, term2):
    return not term1.reference or term2.reference

def and_op(term1, term2):
    return term1.reference and term2.reference

def not_op(term):
    return not term.reference
```
```python
# abelard_logic.py

# Define logical connectives in a truth-functional manner
def negation(p):
    """
    Abelard's definition of negation: not-p is false/true if and only if p is true/false.
    """
    return not p

def conjunction(p, q):
    """
    Conjunction: p and q.
    """
    return p and q

def disjunction(p, q):
    """
    Disjunction: p or q.
    """
    return p or q

def implication(p, q):
    """
    Implication: if p then q.
    """
    return not p or q

def biconditional(p, q):
    """
    Biconditional: p if and only if q.
    """
    return p == q

# Define entailment (inferentia)
def entailment(p, q):
    """
    Abelard's entailment: The conclusion (q) is required by the sense of the preceding statement (p).
    """
    return implication(p, q)

# Examples and testing
if __name__ == "__main__":
    p = True
    q = False

    print("Negation of p:", negation(p))
    print("Conjunction of p and q:", conjunction(p, q))
    print("Disjunction of p and q:", disjunction(p, q))
    print("Implication (p implies q):", implication(p, q))
    print("Biconditional (p if and only if q):", biconditional(p, q))
    print("Entailment (p entails q):", entailment(p, q))
```
```python
# philosophical_logic/aristotelian_logic.py

def categorical_syllogism(major_premise, minor_premise, conclusion):
    return (major_premise == 'All M are P' and minor_premise == 'All S are M' and conclusion == 'All S are P')

def hypothetical_syllogism(major_premise, minor_premise, conclusion):
    return (major_premise == 'If P then Q' and minor_premise == 'If Q then R' and conclusion == 'If P then R')

def disjunctive_syllogism(major_premise, minor_premise, conclusion):
    return (major_premise == 'P or Q' and minor_premise == 'Not P' and conclusion == 'Therefore Q') or \
           (major_premise == 'P or Q' and minor_premise == 'Not Q' and conclusion == 'Therefore P')

def modus_ponens(major_premise, minor_premise, conclusion):
    return (major_premise == 'If P then Q' and minor_premise == 'P' and conclusion == 'Therefore Q')

def modus_tollens(major_premise, minor_premise, conclusion):
    return (major_premise == 'If P then Q' and minor_premise == 'Not Q' and conclusion == 'Therefore Not P')

def validate_syllogism(syllogism_type, major_premise, minor_premise, conclusion):
    if syllogism_type == 'categorical':
        return categorical_syllogism(major_premise, minor_premise, conclusion)
    elif syllogism_type == 'hypothetical':
        return hypothetical_syllogism(major_premise, minor_premise, conclusion)
    elif syllogism_type == 'disjunctive':
        return disjunctive_syllogism(major_premise, minor_premise, conclusion)
    elif syllogism_type == 'modus_ponens':
        return modus_ponens(major_premise, minor_premise, conclusion)
    elif syllogism_type == 'modus_tollens':
        return modus_tollens(major_premise, minor_premise, conclusion)
    else:
        raise ValueError("Invalid syllogism type")
```

### Conclusion

Frege’s approach to logic involves precise definitions of concepts, functions, and quantifiers. Translating his ideas into Python involves creating functions that represent predicates, mappings, and quantifiers. By understanding these fundamental concepts, we can design Python functions that reflect Frege’s logical structure accurately. We can continue by developing more complex logical constructs and ensuring that our Python implementations align closely with Frege’s logical framework. This will allow us to leverage the power of Python to explore and demonstrate Fregean logic in a practical and computational context. Indeed, the first 1800 years of philosophy and its reliance on Aristotelian logic were farily easy to code for, thanks to the help of easily accessible data and my trusty AI sidekicks. The more recent forms of logic, however, are a bit more...technical. 

Anyhow, I hope to share more of my musings with the DEV family.

Best,

Roomal