Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

AINS-M6001 — Notebook 1: First Marketing World

Course: Personal Attention & Influence Systems (Mag.AI-Marketing)
Theme: Minimal executable model: attention, credibility, clarity → signal reach.

PTAH-style layers: entities, rules, discrete ticks. Plain Python (dataclasses, numpy, matplotlib); no external PTAH runtime required.

Setup

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Callable, List, Protocol

import matplotlib.pyplot as plt
import numpy as np

plt.style.use("dark_background")
plt.rcParams["figure.figsize"] = (10, 5)
plt.rcParams["axes.grid"] = True
plt.rcParams["grid.alpha"] = 0.25

Marketer entity

Fields are intentionally small; extend in later notebooks (blocks, message vectors, shocks).

@dataclass
class Marketer:
    attention_hours_day: float = 4.0
    credibility: float = 0.5  # 0..1
    message_clarity: float = 0.6  # 0..1 how tight the niche/message is
    name: str = "you"


marketer = Marketer()
marketer

PTAH-like API: World, Rule, Simulation

Same pattern as AIN-B6001 Business notebooks: rules update state each tick.

class Entity(Protocol):
    name: str


@dataclass
class Rule:
    name: str
    apply: Callable[[dict[str, Any]], None]


@dataclass
class World:
    name: str
    entities: dict[str, Any]
    rules: List[Rule] = field(default_factory=list)

    def register_rule(self, rule: Rule) -> None:
        self.rules.append(rule)


@dataclass
class Simulation:
    world: World
    state: dict[str, Any]
    day: int = 0

    def step(self) -> None:
        self.day += 1
        self.state["day"] = self.day
        for rule in self.world.rules:
            rule.apply(self.state)

    def run(self, n_days: int) -> None:
        for _ in range(n_days):
            self.step()

Reach rule (toy)

signal_reach increases with attention, credibility, clarity; diminishing returns via square-root.

[ \text{reach}_t = 100 \cdot \sqrt{a_t \cdot c_t \cdot m_t} ]

DAYS = 30


def reach(attention: float, credibility: float, clarity: float) -> float:
    x = max(attention, 0.0) * max(min(credibility, 1.0), 0.0) * max(min(clarity, 1.0), 0.0)
    return 100.0 * np.sqrt(x)


history: list[list[float]] = []


def daily_update(st: dict[str, Any]) -> None:
    a = float(st["attention_hours_day"])
    cr = float(st["credibility"])
    cl = float(st["message_clarity"])
    r = reach(a, cr, cl)
    st["daily_reach"] = r
    st["cumulative_reach"] = float(st.get("cumulative_reach", 0.0)) + r
    history.append([int(st["day"]), r, float(st["cumulative_reach"])])


w = World(
    name="personal_marketing_world",
    entities={"marketer": marketer},
    rules=[Rule(name="daily_reach", apply=daily_update)],
)
st0: dict[str, Any] = {
    "day": 0,
    "attention_hours_day": marketer.attention_hours_day,
    "credibility": marketer.credibility,
    "message_clarity": marketer.message_clarity,
    "cumulative_reach": 0.0,
}
sim = Simulation(world=w, state=st0, day=0)
sim.run(DAYS)
hist = np.array(history)
hist[:5], hist[-3:]
fig, ax = plt.subplots()
ax.plot(hist[:, 0], hist[:, 1], label="daily reach")
ax.set_xlabel("day")
ax.set_ylabel("reach (arb.)")
ax.legend()
plt.show()