Skip to content

Commit 801cabf

Browse files
committed
state punishment, update bash scripts & fix logger bugs
1 parent c733302 commit 801cabf

6 files changed

Lines changed: 1638 additions & 1406 deletions

File tree

sorrel/examples/state_punishment/analysis/analysis.ipynb

Lines changed: 1545 additions & 1362 deletions
Large diffs are not rendered by default.

sorrel/examples/state_punishment/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def create_config(
157157
}
158158
else:
159159
social_harm_config = {
160-
"A": 5, # 3
160+
"A": 10, # 3
161161
"B": 0,
162162
"C": 0,
163163
"D": 0.0,

sorrel/examples/state_punishment/logger.py

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
from sorrel.utils.logging import ConsoleLogger, TensorboardLogger
77

8+
# Lowercased entity class names for A–E (see entities.py); always logged, including 0.
9+
_STATE_PUNISHMENT_RESOURCE_ENCOUNTER_TYPES = ("a", "b", "c", "d", "e")
10+
_STATE_PUNISHMENT_RESOURCE_ENCOUNTER_SET = frozenset(_STATE_PUNISHMENT_RESOURCE_ENCOUNTER_TYPES)
11+
812

913
class StatePunishmentLogger:
1014
"""Enhanced logger that tracks encounters and punishment levels."""
@@ -33,6 +37,8 @@ def record_turn(
3337
encounter_data = {}
3438

3539
if self.multi_agent_env is not None:
40+
from sorrel.examples.state_punishment.agents import SeparateModelStatePunishmentAgent
41+
3642
# Initialize total and mean counters
3743
total_encounters = {}
3844
mean_encounters = {}
@@ -44,26 +50,39 @@ def record_turn(
4450
sigma_weights_advantage = []
4551
sigma_weights_value = []
4652

47-
# Track action frequencies
48-
total_action_frequencies = {}
49-
mean_action_frequencies = {}
53+
n_envs = len(self.multi_agent_env.individual_envs)
54+
# Union of action keys so every epoch logs zeros for unused actions (and vote_* for separate-model agents)
55+
global_action_keys: set[str] = set()
56+
for env in self.multi_agent_env.individual_envs:
57+
ag = env.agents[0]
58+
global_action_keys.update(ag.action_names)
59+
if isinstance(ag, SeparateModelStatePunishmentAgent):
60+
global_action_keys.update(
61+
("vote_no", "vote_increase", "vote_decrease")
62+
)
63+
sorted_action_keys = sorted(global_action_keys)
5064

5165
for i, env in enumerate(self.multi_agent_env.individual_envs):
5266
agent = env.agents[0]
53-
agent_count = len(agent.encounters)
5467

55-
# Individual agent encounter data
68+
# Resource encounters (A–E): always log every step, including zeros
69+
for res in _STATE_PUNISHMENT_RESOURCE_ENCOUNTER_TYPES:
70+
count = agent.encounters.get(res, 0)
71+
encounter_data[f"Agent_{i}/{res}_encounters"] = count
72+
total_encounters[res] = total_encounters.get(res, 0) + count
73+
mean_encounters[res] = mean_encounters.get(res, 0) + count
74+
75+
# Other stepped-on entity types (e.g. sand, wall)
5676
for entity_type, count in agent.encounters.items():
77+
if entity_type in _STATE_PUNISHMENT_RESOURCE_ENCOUNTER_SET:
78+
continue
5779
encounter_data[f"Agent_{i}/{entity_type}_encounters"] = count
58-
59-
# Initialize if first time seeing this entity type
6080
if entity_type not in total_encounters:
6181
total_encounters[entity_type] = 0
6282
mean_encounters[entity_type] = 0
63-
6483
total_encounters[entity_type] += count
6584
mean_encounters[entity_type] += count
66-
85+
6786
# Individual agent score
6887
encounter_data[f"Agent_{i}/individual_score"] = agent.individual_score
6988
total_individual_scores += agent.individual_score
@@ -72,21 +91,13 @@ def record_turn(
7291
encounter_data[f"Agent_{i}/social_harm_received"] = agent.social_harm_received_epoch
7392
total_social_harm_received += agent.social_harm_received_epoch
7493

75-
# Track action frequencies for this agent
76-
for action_name, frequency in agent.action_frequencies.items():
94+
# Action frequencies: always emit every key in global union (zeros when unused)
95+
for action_name in sorted_action_keys:
96+
frequency = agent.action_frequencies.get(action_name, 0)
7797
encounter_data[f"Agent_{i}/action_freq_{action_name}"] = frequency
78-
79-
# Initialize if first time seeing this action
80-
if action_name not in total_action_frequencies:
81-
total_action_frequencies[action_name] = 0
82-
mean_action_frequencies[action_name] = 0
83-
84-
total_action_frequencies[action_name] += frequency
85-
mean_action_frequencies[action_name] += frequency
8698

8799
# Access sigma_weight and epsilon from PyTorchIQN model
88100
# Check if agent uses separate models
89-
from sorrel.examples.state_punishment.agents import SeparateModelStatePunishmentAgent
90101
if isinstance(agent, SeparateModelStatePunishmentAgent):
91102
# Separate model agent: log sigma weights and epsilon from both move and vote models
92103
# Move model sigma weights
@@ -147,26 +158,32 @@ def record_turn(
147158
# Add totals and means to encounter_data
148159
for entity_type in total_encounters:
149160
encounter_data[f"Total/total_{entity_type}_encounters"] = total_encounters[entity_type]
150-
encounter_data[f"Mean/mean_{entity_type}_encounters"] = mean_encounters[entity_type] / len(self.multi_agent_env.individual_envs)
161+
encounter_data[f"Mean/mean_{entity_type}_encounters"] = mean_encounters[entity_type] / n_envs
151162

152163
# Add total and mean individual scores
153164
encounter_data["Total/total_individual_score"] = total_individual_scores
154-
encounter_data["Mean/mean_individual_score"] = total_individual_scores / len(self.multi_agent_env.individual_envs)
165+
encounter_data["Mean/mean_individual_score"] = total_individual_scores / n_envs
155166

156167
# Add total and mean social harm received
157168
encounter_data["Total/total_social_harm_received"] = total_social_harm_received
158-
encounter_data["Mean/mean_social_harm_received"] = total_social_harm_received / len(self.multi_agent_env.individual_envs)
169+
encounter_data["Mean/mean_social_harm_received"] = total_social_harm_received / n_envs
159170

160-
# Add total and mean action frequencies
171+
# Add total and mean action frequencies (zeros included via .get above)
161172
# Note: For standard agents, each agent takes one action per turn, so the sum of mean
162173
# action frequencies should equal max_turns (typically 100) if the epoch completes.
163174
# For separate model agents, the total includes both movement actions (one per turn)
164175
# and vote actions (one per vote epoch), so the sum will be higher than max_turns.
165176
# For example: if max_turns=100 and vote_window_size=10, expect ~110 actions per agent
166177
# (100 movement + 10 vote actions). Epochs can end early if world.is_done is True.
167-
for action_name in total_action_frequencies:
168-
encounter_data[f"Total/total_action_freq_{action_name}"] = total_action_frequencies[action_name]
169-
encounter_data[f"Mean/mean_action_freq_{action_name}"] = mean_action_frequencies[action_name] / len(self.multi_agent_env.individual_envs)
178+
for action_name in sorted_action_keys:
179+
total_af = sum(
180+
self.multi_agent_env.individual_envs[j].agents[0].action_frequencies.get(
181+
action_name, 0
182+
)
183+
for j in range(n_envs)
184+
)
185+
encounter_data[f"Total/total_action_freq_{action_name}"] = total_af
186+
encounter_data[f"Mean/mean_action_freq_{action_name}"] = total_af / n_envs
170187

171188

172189
# Add mean sigma weights across all agents

sorrel/examples/state_punishment/run_cpc_tmux_study1.sh

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@
22
# Study 1: same as study 2 but agents can observe punishment level (--punishment_level_accessible).
33
# Create four tmux sessions and run CPC experiments (cpc_00/01 x iqn/ppo).
44
# Run this from the workspace root (parent of sorrel/), or it will cd there.
5+
#
6+
# Long python CLI strings are run via run_cpc_tmux_study1_job.sh because tmux
7+
# send-keys truncates very long lines (PPO/IQN would stop before --seed 1).
58

69
set -e
710
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
811
cd "$ROOT"
912

10-
# Prefix: ensure conda is available, deactivate any env, then activate sorrel and run
11-
CONDA_PREFIX='source "$(conda info --base 2>/dev/null)/etc/profile.d/conda.sh" 2>/dev/null; conda deactivate 2>/dev/null; conda deactivate 2>/dev/null; conda activate sorrel && cd '"$ROOT"' && '
13+
JOB="$ROOT/sorrel/examples/state_punishment/run_cpc_tmux_study1_job.sh"
1214

1315
# cpc_00_ppo: PPO, cpc_weight 0.0
14-
tmux new-session -d -s study_1_cpc_00_ppo
15-
tmux send-keys -t study_1_cpc_00_ppo "$CONDA_PREFIX python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --model_type ppo_lstm_cpc --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix np_00_cpc_ppo_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --use_cpc --cpc_horizon 5 --cpc_weight 0.0 --cpc_sample_size 4 --ppo_use_factored_actions --ppo_action_dims \"5,3\" --composite_view --seed 1" C-m
16+
tmux new-session -d -s study_1_cpc_00_ppo 2>/dev/null || true
17+
tmux send-keys -t study_1_cpc_00_ppo "bash '$JOB' ppo_00" C-m
1618

1719
# cpc_01_ppo: PPO, cpc_weight 0.1
18-
tmux new-session -d -s study_1_cpc_01_ppo
19-
tmux send-keys -t study_1_cpc_01_ppo "$CONDA_PREFIX python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --model_type ppo_lstm_cpc --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix np_01_cpc_ppo_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --use_cpc --cpc_horizon 5 --cpc_weight 0.1 --cpc_sample_size 4 --ppo_use_factored_actions --ppo_action_dims \"5,3\" --composite_view --seed 1" C-m
20+
tmux new-session -d -s study_1_cpc_01_ppo 2>/dev/null || true
21+
tmux send-keys -t study_1_cpc_01_ppo "bash '$JOB' ppo_01" C-m
2022

2123
# cpc_01_iqn: IQN, cpc_weight 0.1
22-
tmux new-session -d -s study_1_cpc_01_iqn
23-
tmux send-keys -t study_1_cpc_01_iqn "$CONDA_PREFIX python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix np_01_cpc_iqn_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --iqn_use_cpc --iqn_cpc_horizon 5 --iqn_cpc_weight 0.1 --iqn_cpc_sample_size 4 --iqn_use_factored_actions --iqn_action_dims \"5,3\" --composite_views --seed 1" C-m
24+
tmux new-session -d -s study_1_cpc_01_iqn 2>/dev/null || true
25+
tmux send-keys -t study_1_cpc_01_iqn "bash '$JOB' iqn_01" C-m
2426

2527
# cpc_00_iqn: IQN, cpc_weight 0.0
26-
tmux new-session -d -s study_1_cpc_00_iqn
27-
tmux send-keys -t study_1_cpc_00_iqn "$CONDA_PREFIX python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix np_00_cpc_iqn_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --iqn_use_cpc --iqn_cpc_horizon 5 --iqn_cpc_weight 0.0 --iqn_cpc_sample_size 4 --iqn_use_factored_actions --iqn_action_dims \"5,3\" --composite_views --seed 1" C-m
28+
tmux new-session -d -s study_1_cpc_00_iqn 2>/dev/null || true
29+
tmux send-keys -t study_1_cpc_00_iqn "bash '$JOB' iqn_00" C-m
2830

29-
echo "Created 4 tmux sessions: study_1_cpc_00_ppo, study_1_cpc_01_ppo, study_1_cpc_01_iqn, study_1_cpc_00_iqn"
31+
echo "Ensured 4 tmux sessions exist and sent run commands: study_1_cpc_00_ppo, study_1_cpc_01_ppo, study_1_cpc_01_iqn, study_1_cpc_00_iqn (new-session duplicates ignored)"
3032
echo "Attach with: tmux attach -t study_1_cpc_00_ppo (or study_1_cpc_01_ppo, study_1_cpc_01_iqn, study_1_cpc_00_iqn)"
3133
echo "List sessions: tmux ls"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# One Study 1 CPC job; invoked by run_cpc_tmux_study1.sh to avoid tmux send-keys length limits.
3+
set -e
4+
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
5+
cd "$ROOT"
6+
# shellcheck source=/dev/null
7+
source "$(conda info --base 2>/dev/null)/etc/profile.d/conda.sh" 2>/dev/null
8+
conda deactivate 2>/dev/null || true
9+
conda deactivate 2>/dev/null || true
10+
conda activate sorrel
11+
export PYTHONPATH="$ROOT"
12+
13+
case "$1" in
14+
ppo_00)
15+
exec python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --model_type ppo_lstm_cpc --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix 00_cpc_ppo_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --use_cpc --cpc_horizon 5 --cpc_weight 0.0 --cpc_sample_size 4 --ppo_use_factored_actions --ppo_action_dims 5,3 --composite_views --seed 1
16+
;;
17+
ppo_01)
18+
exec python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --model_type ppo_lstm_cpc --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix 01_cpc_ppo_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --use_cpc --cpc_horizon 5 --cpc_weight 0.1 --cpc_sample_size 4 --ppo_use_factored_actions --ppo_action_dims 5,3 --composite_views --seed 1
19+
;;
20+
iqn_01)
21+
exec python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix 01_cpc_iqn_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --iqn_use_cpc --iqn_cpc_horizon 5 --iqn_cpc_weight 0.1 --iqn_cpc_sample_size 4 --iqn_use_factored_actions --iqn_action_dims 5,3 --composite_views --seed 1
22+
;;
23+
iqn_00)
24+
exec python sorrel/examples/state_punishment/main.py --num_agents 10 --multi_env_composite --epochs 1500000 --social_harm_accessible --punishment_level_accessible --disable_probe_test --epsilon 0 --use_probabilistic_punishment --use_predefined_punishment_schedule --replacement_selection_mode random_with_tenure --replacement_minimum_tenure_epochs 10000 --agents_to_replace_per_epoch 1 --replacement_start_epoch 10000 --enable_agent_replacement --replacement_min_epochs_between 10000 --run_folder_prefix 00_cpc_iqn_lstm_cpc_slow --respawn_prob 0.01 --num_resources 14 --max_resources 14 --iqn_use_cpc --iqn_cpc_horizon 5 --iqn_cpc_weight 0.0 --iqn_cpc_sample_size 4 --iqn_use_factored_actions --iqn_action_dims 5,3 --composite_views --seed 1
25+
;;
26+
*)
27+
echo "Usage: $0 {ppo_00|ppo_01|iqn_00|iqn_01}" >&2
28+
exit 1
29+
;;
30+
esac

sorrel/examples/state_punishment/state_system.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222

2323
# increase the punishment prob for A
2424
# predefined_punishment_probs = np.array([
25-
# [0.40, 0.00, 0.00, 0.00, 0.00], # s = 0
26-
# [0.45, 0.05, 0.00, 0.00, 0.00], # s = 1
25+
# [0.30, 0.00, 0.00, 0.00, 0.00], # s = 0
26+
# [0.40, 0.05, 0.00, 0.00, 0.00], # s = 1
2727
# [0.55, 0.10, 0.00, 0.00, 0.00], # s = 2
2828
# [0.70, 0.10, 0.05, 0.00, 0.00], # s = 3
29-
# [0.80, 0.10, 0.10, 0.00, 0.00], # s = 4
30-
# [0.90, 0.10, 0.10, 0.05, 0.00], # s = 5
31-
# [0.95, 0.10, 0.10, 0.10, 0.00], # s = 6
29+
# [0.85, 0.10, 0.10, 0.00, 0.00], # s = 4
30+
# [0.95, 0.10, 0.10, 0.05, 0.00], # s = 5
31+
# [1.0, 0.10, 0.10, 0.10, 0.00], # s = 6
3232
# [1.0, 0.15, 0.10, 0.10, 0.05], # s = 7
3333
# [1.0, 0.15, 0.15, 0.10, 0.10], # s = 8
3434
# [1.0, 0.20, 0.15, 0.15, 0.10], # s = 9

0 commit comments

Comments
 (0)