-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdemo_actions.py
More file actions
173 lines (137 loc) · 5.59 KB
/
demo_actions.py
File metadata and controls
173 lines (137 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
"""
Action inference demo.
Shows how the same magnitude produces different actions depending
on the DIRECTION in valence space. And how trajectory projection
triggers preemptive action.
Run: python -m vScore.demo_actions
"""
from vScore.core.valence import ValenceVector, ValenceTrajectory
from vScore.core.action_space import build_survival_actions, build_fire_actions
def print_state(label, v, actions, conflict):
scores_str = " ".join(f"{s:.0f}" for s in v.scores)
mag = sum(s * s for s in v.scores) ** 0.5
print(f"\n {label}")
print(f" vector: [{scores_str}] magnitude: {mag:.1f} conflict: {conflict:.2f}")
for name, strength in actions[:3]:
bar = "#" * int(strength * 30)
print(f" {name:>15s} {strength:.2f} {bar}")
def survival_demo():
print("=" * 65)
print("SURVIVAL: Same magnitude, different directions, different actions")
print("=" * 65)
print(" axes: seeking rage fear lust care panic play")
af = build_survival_actions(
["seeking", "rage", "fear", "lust", "care", "panic", "play"]
)
scenarios = [
("Predator spotted (fear dominant)",
[0, 0, 8, 0, 0, 0, 0]),
("Territory invaded (rage dominant)",
[0, 8, 0, 0, 0, 0, 0]),
("Cornered (fear + rage = FREEZE)",
[0, 7, 7, 0, 0, 0, 0]),
("Mother with cub, predator near (care + fear = PROTECT)",
[0, 0, 8, 0, 8, 0, 0]),
("Safe meadow (seeking + play)",
[6, 0, 0, 0, 0, 0, 5]),
("Lost offspring (panic dominant)",
[0, 0, 0, 0, 3, 8, 0]),
("Sleeping (homeostasis)",
[0, 0, 0, 0, 0, 0, 0]),
]
for label, scores in scenarios:
v = ValenceVector("Survival", scores)
actions = af.evaluate(v)
conflict = af.conflict_level(v)
print_state(label, v, actions, conflict)
def fire_demo():
print("\n\n" + "=" * 65)
print("FIRE: Trajectory changes action BEFORE the crisis arrives")
print("=" * 65)
print(" axes: spread prox intens contain escape struct smoke")
af = build_fire_actions(
["spread_rate", "proximity", "intensity", "containment",
"escape_route", "structural_risk", "smoke_toxicity"]
)
# A fire that deteriorates over time
trajectory = ValenceTrajectory(domain_name="Fire")
# spread prox int contain escape struct smoke
timeline = [
(0.0, "Initial assessment",
[2.0, 2.0, 2.0, 7.0, 1.0, 1.0, 1.0]),
(1.0, "Spreading, still contained",
[3.5, 2.5, 3.0, 6.0, 1.5, 1.5, 2.0]),
(2.0, "Containment breaking down",
[5.0, 3.5, 4.5, 4.0, 2.5, 2.5, 3.5]),
(3.0, "Losing control",
[6.5, 5.0, 6.0, 2.5, 4.0, 4.0, 5.0]),
(4.0, "Structure compromised",
[7.5, 6.5, 7.5, 1.5, 6.0, 6.5, 7.0]),
(5.0, "Collapse imminent",
[8.5, 8.0, 9.0, 0.5, 8.0, 8.5, 8.5]),
]
for t, label, scores in timeline:
v = ValenceVector("Fire", scores, timestamp=t)
trajectory.append(v)
current_actions = af.evaluate(v)
conflict = af.conflict_level(v)
result = af.evaluate_trajectory(trajectory, horizon=2.0)
print(f"\n t={t:.0f} {label}")
scores_str = " ".join(f"{s:.1f}" for s in scores)
print(f" vector: [{scores_str}]")
print(f" NOW: ", end="")
for name, strength in result["current"][:2]:
print(f"{name}({strength:.2f}) ", end="")
print()
print(f" t+2: ", end="")
for name, strength in result["projected"][:2]:
print(f"{name}({strength:.2f}) ", end="")
print()
if result["preempt"]:
print(f" >>> PREEMPT: {result['preempt_reason']}")
print(f" conflict: {conflict:.2f}", end="")
if conflict > 0.7:
print(" [HIGH — competing actions, hesitation risk]", end="")
print()
def geometry_demo():
print("\n\n" + "=" * 65)
print("GEOMETRY: Why direction matters more than magnitude")
print("=" * 65)
af = build_survival_actions(
["seeking", "rage", "fear", "lust", "care", "panic", "play"]
)
# Three vectors with IDENTICAL magnitude but different actions
import math
vectors = [
("Pure fear", [0, 0, 10, 0, 0, 0, 0]),
("Pure rage", [0, 10, 0, 0, 0, 0, 0]),
("Pure care", [0, 0, 0, 0, 10, 0, 0]),
("Distributed low", [3.8, 3.8, 3.8, 3.8, 3.8, 3.8, 3.8]), # ~10.0 magnitude
]
print(f"\n All vectors have magnitude ~10.0")
print(f" Same energy, completely different behavior:\n")
for label, scores in vectors:
v = ValenceVector("Survival", scores)
mag = sum(s*s for s in scores) ** 0.5
actions = af.evaluate(v)
conflict = af.conflict_level(v)
top_action = actions[0][0] if actions else "REST"
top_strength = actions[0][1] if actions else 0
print(f" {label:>20s} mag={mag:5.1f} → {top_action:<15s} "
f"({top_strength:.2f}) conflict={conflict:.2f}")
print(f"""
The classifier says: "high activation" for all four.
The valence scorer says: flee / fight / nurture / anxious vigilance.
Same energy. Four completely different organisms.
Magnitude is arousal. Direction is meaning.
A 10-dimensional classifier needs 10 thresholds.
A 10-dimensional valence space has infinite directions —
each one a different way of being activated, a different
reason to act, a different future to project.
""")
def main():
survival_demo()
fire_demo()
geometry_demo()
if __name__ == "__main__":
main()