tensorcode 0.1.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. tensorcode/__init__.py +84 -0
  2. tensorcode/actions.py +137 -0
  3. tensorcode/answer_type.py +222 -0
  4. tensorcode/awareness.py +344 -0
  5. tensorcode/backends/__init__.py +0 -0
  6. tensorcode/backends/builtin.py +167 -0
  7. tensorcode/backends/hf_local.py +89 -0
  8. tensorcode/backends/linear.py +133 -0
  9. tensorcode/backends/neural.py +361 -0
  10. tensorcode/causal.py +262 -0
  11. tensorcode/change.py +566 -0
  12. tensorcode/chunking.py +195 -0
  13. tensorcode/cognition.py +311 -0
  14. tensorcode/context.py +97 -0
  15. tensorcode/control.py +291 -0
  16. tensorcode/cues.py +192 -0
  17. tensorcode/expectation.py +270 -0
  18. tensorcode/frames.py +232 -0
  19. tensorcode/language/__init__.py +36 -0
  20. tensorcode/language/chart.py +558 -0
  21. tensorcode/language/discourse.py +132 -0
  22. tensorcode/language/domains/__init__.py +0 -0
  23. tensorcode/language/domains/desktop.py +552 -0
  24. tensorcode/language/english.py +459 -0
  25. tensorcode/language/features.py +112 -0
  26. tensorcode/language/generate.py +574 -0
  27. tensorcode/language/grammar.py +893 -0
  28. tensorcode/language/semantics.py +349 -0
  29. tensorcode/learning/__init__.py +30 -0
  30. tensorcode/learning/certificate.py +148 -0
  31. tensorcode/learning/induce.py +304 -0
  32. tensorcode/learning/library.py +217 -0
  33. tensorcode/learning/literals.py +126 -0
  34. tensorcode/learning/verify.py +253 -0
  35. tensorcode/memory.py +303 -0
  36. tensorcode/metacognition.py +351 -0
  37. tensorcode/ops.py +207 -0
  38. tensorcode/outcomes.py +99 -0
  39. tensorcode/permanence.py +376 -0
  40. tensorcode/priming.py +191 -0
  41. tensorcode/py.typed +0 -0
  42. tensorcode/quantity.py +311 -0
  43. tensorcode/records.py +728 -0
  44. tensorcode/relation.py +771 -0
  45. tensorcode/runtime.py +471 -0
  46. tensorcode/semantics_bridge.py +308 -0
  47. tensorcode/social.py +380 -0
  48. tensorcode/temporal.py +189 -0
  49. tensorcode/wants.py +185 -0
  50. tensorcode-0.1.0a1.dist-info/METADATA +196 -0
  51. tensorcode-0.1.0a1.dist-info/RECORD +53 -0
  52. tensorcode-0.1.0a1.dist-info/WHEEL +4 -0
  53. tensorcode-0.1.0a1.dist-info/licenses/LICENSE +21 -0
tensorcode/causal.py ADDED
@@ -0,0 +1,262 @@
1
+ """Causal claims, kept apart from evidential ones, and earned by intervention.
2
+
3
+ Provenance already answers "why do I believe this" — which source said it. It does not
4
+ answer "what makes this happen", and the two are routinely confused: a claim derived from
5
+ another is *supported* by it, not *caused* by it. So causation is its own record, and it
6
+ carries how it was learned:
7
+
8
+ * ``observational`` support means the two were seen together. That is a correlation, and
9
+ this module names it one.
10
+ * ``interventional`` support means the same state was run twice, once with the act and once
11
+ without, and the effect differed. An agent's own action is a real ``do(·)``, and a world
12
+ that can fork gives the untaken branch for free.
13
+
14
+ :func:`experiment` runs that controlled pair, :func:`learn` turns contrasts into claims
15
+ with an effect size, and :func:`counterfactual` records the branch that did not happen in a
16
+ scope of its own, so "what would have happened" never leaks into the shared world.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import hashlib
22
+ import json
23
+ from dataclasses import dataclass, field
24
+ from datetime import datetime, timezone
25
+ from typing import Any, Callable, Iterable, Mapping, Sequence
26
+
27
+ from .outcomes import Score, Unknown
28
+ from .records import Claim, Evidence, Ref, Store
29
+
30
+ Support = str # "interventional" | "observational"
31
+
32
+
33
+ def _digest(payload: Any) -> str:
34
+ return hashlib.sha256(json.dumps(payload, sort_keys=True, default=str).encode()).hexdigest()[:12]
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class Contrast:
39
+ """One aspect, observed with the act and without it, from the same starting state."""
40
+
41
+ cause: str
42
+ aspect: str
43
+ with_act: tuple[Any, ...]
44
+ without_act: tuple[Any, ...]
45
+
46
+ @property
47
+ def trials(self) -> int:
48
+ return min(len(self.with_act), len(self.without_act))
49
+
50
+ @property
51
+ def changed_with(self) -> float:
52
+ """Fraction of with-act runs where this aspect ended up different from its control."""
53
+ if not self.with_act or not self.without_act:
54
+ return 0.0
55
+ pairs = zip(self.with_act, self.without_act)
56
+ return sum(1 for a, b in pairs if a != b) / self.trials
57
+
58
+ @property
59
+ def effect(self) -> float:
60
+ """The average causal effect: how often the act, and only the act, moved this aspect."""
61
+ return self.changed_with
62
+
63
+ def describe(self) -> str:
64
+ return f"{self.cause} → {self.aspect}: {self.with_act[:1]} vs control {self.without_act[:1]} (effect {self.effect:.2f}, n={self.trials})"
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ class Causal:
69
+ """``cause`` brings about ``effect``, with the evidence that says so."""
70
+
71
+ cause: str
72
+ aspect: str
73
+ effect: Any
74
+ support: Support
75
+ strength: Score
76
+ trials: int
77
+ mechanism: str | None = None
78
+ enabling: tuple[str, ...] = () # conditions that must hold for the link to fire
79
+
80
+ @property
81
+ def ref(self) -> Ref:
82
+ return Ref(f"causal:{_digest([self.cause, self.aspect, str(self.effect), self.support])}")
83
+
84
+ def describe(self) -> str:
85
+ how = f" via {self.mechanism}" if self.mechanism else ""
86
+ needs = f" when {', '.join(self.enabling)}" if self.enabling else ""
87
+ return f"{self.cause} causes {self.aspect}={self.effect!r}{how}{needs} [{self.support}, {self.strength.value:.2f}, n={self.trials}]"
88
+
89
+
90
+ # ------------------------------------------------------------- interventions
91
+
92
+
93
+ def experiment(
94
+ *,
95
+ prepare: Callable[[], Any],
96
+ act: Callable[[Any], None],
97
+ observe: Callable[[Any], Mapping[str, Any]],
98
+ cause: str,
99
+ trials: int = 1,
100
+ settle: Callable[[Any], None] | None = None,
101
+ control: Callable[[Any], None] | None = None,
102
+ ) -> list[Contrast]:
103
+ """Run the same starting state with and without ``act``; report what differed.
104
+
105
+ ``prepare`` returns a fresh copy of the world (a fork, a restored checkpoint), so the
106
+ two branches differ in exactly one thing: whether the act happened. Anything that moves
107
+ on its own — a clock, an animation, a scheduled event — moves in both branches and so
108
+ cancels out of the contrast, which is the whole point of running the control.
109
+
110
+ ``control`` is what the untreated branch does *instead*: an innocuous act of the same
111
+ kind (a click on empty space, a step that changes nothing). Without it the control
112
+ branch does nothing at all, and then everything that follows from merely acting — a
113
+ logical clock, a step counter, a repaint — is scored as an effect of this act. Which
114
+ control is right depends on the question: leave it out to ask "what follows from doing
115
+ this rather than nothing", pass one to ask "what does *this* act do that any act would
116
+ not".
117
+ """
118
+ with_runs: list[Mapping[str, Any]] = []
119
+ without_runs: list[Mapping[str, Any]] = []
120
+ for _ in range(max(1, trials)):
121
+ treated = prepare()
122
+ act(treated)
123
+ if settle:
124
+ settle(treated)
125
+ with_runs.append(dict(observe(treated)))
126
+
127
+ untreated = prepare()
128
+ if control:
129
+ control(untreated)
130
+ if settle:
131
+ settle(untreated)
132
+ without_runs.append(dict(observe(untreated)))
133
+ aspects = sorted({k for run in with_runs + without_runs for k in run})
134
+ return [
135
+ Contrast(cause, aspect, tuple(run.get(aspect) for run in with_runs), tuple(run.get(aspect) for run in without_runs))
136
+ for aspect in aspects
137
+ ]
138
+
139
+
140
+ def learn(contrasts: Iterable[Contrast], *, least_effect: float = 0.5, basis: str = "intervention") -> list[Causal]:
141
+ """Causal claims from controlled contrasts. An aspect the act never moved is dropped."""
142
+ out: list[Causal] = []
143
+ for contrast in contrasts:
144
+ if contrast.trials == 0 or contrast.effect < least_effect:
145
+ continue
146
+ value = contrast.with_act[0]
147
+ out.append(Causal(
148
+ cause=contrast.cause, aspect=contrast.aspect, effect=value, support="interventional",
149
+ strength=Score(contrast.effect, "probability", basis=f"{basis}@n={contrast.trials}"),
150
+ trials=contrast.trials,
151
+ ))
152
+ return out
153
+
154
+
155
+ def correlations(observations: Sequence[tuple[set[str], Mapping[str, Any]]], *, cause: str,
156
+ least: float = 0.5, basis: str = "co-occurrence") -> list[Causal]:
157
+ """The baseline an intervention has to beat: what merely co-occurs with the act.
158
+
159
+ Each observation is ``(acts_that_happened, aspects_observed)``. No control, so a
160
+ consequence of something else that always accompanies the act scores just as highly —
161
+ which is exactly the mistake :func:`experiment` exists to avoid.
162
+ """
163
+ seen: dict[tuple[str, Any], int] = {}
164
+ total = 0
165
+ for acts, aspects in observations:
166
+ if cause not in acts:
167
+ continue
168
+ total += 1
169
+ for aspect, value in aspects.items():
170
+ seen[(aspect, _hashable(value))] = seen.get((aspect, _hashable(value)), 0) + 1
171
+ if not total:
172
+ return []
173
+ out: list[Causal] = []
174
+ for (aspect, value), hits in sorted(seen.items(), key=lambda kv: -kv[1]):
175
+ share = hits / total
176
+ if share >= least:
177
+ out.append(Causal(cause=cause, aspect=aspect, effect=value, support="observational",
178
+ strength=Score(share, "probability", basis=f"{basis}@n={total}"), trials=total))
179
+ return out
180
+
181
+
182
+ def moved(before: Mapping[str, Any], after: Mapping[str, Any]) -> dict[str, str]:
183
+ """Which aspects moved, as events rather than values.
184
+
185
+ Co-occurrence has to be counted over movements, not over exact values: an aspect whose
186
+ value is different every time (a clock, a counter) never repeats, so keying on the value
187
+ makes a perfectly reliable co-occurrence look like noise. This is the same lesson the
188
+ prediction measurement gave — what changed is learnable, what it became often is not.
189
+ """
190
+ return {key: "changed" for key in sorted(set(before) | set(after)) if before.get(key) != after.get(key)}
191
+
192
+
193
+ def _hashable(value: Any) -> Any:
194
+ if isinstance(value, (list, tuple)):
195
+ return tuple(_hashable(v) for v in value)
196
+ if isinstance(value, dict):
197
+ return tuple(sorted((k, _hashable(v)) for k, v in value.items()))
198
+ return value
199
+
200
+
201
+ # -------------------------------------------------------------------- claims
202
+
203
+
204
+ def tell_causal(mind: Store, causal: Causal, *, source: Ref, observed_at: datetime | None = None,
205
+ derived_from: Sequence[str] = ()) -> Ref:
206
+ """Record a causal link as claims that say how it was learned."""
207
+ at = observed_at or datetime.now(timezone.utc)
208
+ ref = causal.ref
209
+ evidence = Evidence(source=source, observed_at=at, method=f"causal:{causal.support}",
210
+ confidence=causal.strength, derived_from=tuple(derived_from))
211
+ mind.tell(Claim(ref, "is_a", "causal_link"), evidence)
212
+ mind.tell(Claim(ref, "cause", causal.cause), evidence)
213
+ mind.tell(Claim(ref, "aspect", causal.aspect), evidence)
214
+ mind.tell(Claim(ref, "effect", causal.effect), evidence)
215
+ mind.tell(Claim(ref, "support", causal.support), evidence)
216
+ mind.tell(Claim(ref, "trials", causal.trials), evidence)
217
+ if causal.mechanism:
218
+ mind.tell(Claim(ref, "mechanism", causal.mechanism), evidence)
219
+ for condition in causal.enabling:
220
+ mind.tell(Claim(ref, "enabled_by", condition), evidence)
221
+ return ref
222
+
223
+
224
+ def causes_of(mind: Store, aspect: str, *, interventional_only: bool = False) -> list[Ref]:
225
+ """Recorded causes of an aspect, strongest support first."""
226
+ links = [r.claim.subject for r in mind.claims(predicate="aspect", object=aspect)]
227
+ out = []
228
+ for ref in links:
229
+ support = next((r.claim.object for r in mind.claims(ref, "support")), None)
230
+ if interventional_only and support != "interventional":
231
+ continue
232
+ out.append((0 if support == "interventional" else 1, ref))
233
+ return [ref for _, ref in sorted(out, key=lambda pair: pair[0])]
234
+
235
+
236
+ def counterfactual(mind: Store, *, name: str, facts: Iterable[tuple[Ref, str, Any]], source: Ref,
237
+ because: str, observed_at: datetime | None = None) -> Ref:
238
+ """Record the branch that did not happen, in a scope of its own.
239
+
240
+ A counterfactual is real knowledge — it is what makes an effect size meaningful — but it
241
+ is not true of the world, so it lives in its own scope and never answers a question
242
+ about what is.
243
+ """
244
+ at = observed_at or datetime.now(timezone.utc)
245
+ scope = Ref(f"scope:counterfactual:{name}")
246
+ evidence = Evidence(source=source, observed_at=at, method="counterfactual")
247
+ mind.tell(Claim(scope, "is_a", "counterfactual"), evidence)
248
+ mind.tell(Claim(scope, "because", because), evidence)
249
+ for subject, predicate, value in facts:
250
+ mind.tell(Claim(subject, predicate, value, scope=scope), evidence)
251
+ return scope
252
+
253
+
254
+ def distinguish(contrasts: Iterable[Contrast], correlated: Iterable[Causal]) -> dict[str, str]:
255
+ """Which co-occurring aspects the intervention actually vindicated.
256
+
257
+ Returns aspect → "caused" | "merely_correlated", which is the judgement a correlational
258
+ record cannot make on its own.
259
+ """
260
+ caused = {c.aspect for c in contrasts if c.trials and c.effect >= 0.5}
261
+ seen = {c.aspect for c in correlated}
262
+ return {aspect: ("caused" if aspect in caused else "merely_correlated") for aspect in sorted(seen | caused)}