<jupyter_start><jupyter_text>Generating stimuli for A/B choice experiment, given_subgoal experiment Purpose of this notebook is:* to create a set of towers* for each tower, create a tree of branching subgoal choices, which each subgoal on each turn being either the cheapest or the most expensive one meeting a certain condition.    * ensuring that each node has a path to the goal (can we do that?)* visualize the different choices* create a list of **subgoals** with an associated world state, not necessarily the best and worst sequence.* save that out to a pickled dataframe for the upload notebook in the `_human_expperiment` repoRequires:*See also:*   Setup<jupyter_code># set up imports
import os
import sys
__file__ = os.getcwd()
proj_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(proj_dir)
utils_dir = os.path.join(proj_dir, 'utils')
sys.path.append(utils_dir)
analysis_dir = os.path.join(proj_dir, 'analysis')
analysis_utils_dir = os.path.join(analysis_dir, 'utils')
sys.path.append(analysis_utils_dir)
agent_dir = os.path.join(proj_dir, 'model')
sys.path.append(agent_dir)
agent_util_dir = os.path.join(agent_dir, 'utils')
sys.path.append(agent_util_dir)
experiments_dir = os.path.join(proj_dir, 'experiments')
sys.path.append(experiments_dir)
df_dir = os.path.join(proj_dir, 'results/dataframes')
stim_dir = os.path.join(proj_dir, 'stimuli')
import tower_generator

from tqdm import tqdm
import p_tqdm

import pickle

import math

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

import scipy.stats as stats
from scipy.stats import sem as sem

from utils.blockworld_library import *
from utils.blockworld import *

from model.BFS_Lookahead_Agent import BFS_Lookahead_Agent
from model.BFS_Agent import BFS_Agent
from model.Astar_Agent import Astar_Agent
from model.Best_First_Search_Agent import Best_First_Search_Agent
from model.Subgoal_Planning_Agent import Subgoal_Planning_Agent

from model.utils.decomposition_functions import *
import utils.blockworld_library as bl


# show all columns in dataframe
pd.set_option('display.max_columns', None)<jupyter_output><empty_output><jupyter_text>Generating towers<jupyter_code>block_library = bl_nonoverlapping_simple
generator = tower_generator.TowerGenerator(8, 8,
                                           block_library=block_library,
                                           seed=42,
                                           padding=(2, 0),
                                           num_blocks=lambda: random.randint(4, 10), #  flat random interval of tower sizes (inclusive)
                                           )

NUM_TOWERS  = 64
towers = []
for i in tqdm(range(NUM_TOWERS)):
    towers.append(generator.generate())
worlds = [Blockworld(silhouette=t['bitmap'], block_library=bl.bl_nonoverlapping_simple) for t in towers]<jupyter_output><empty_output><jupyter_text>Visualize the generated towers<jupyter_code># look at towers
def visualize_towers(towers, text_parameters=None):
    fig,axes = plt.subplots(math.ceil(len(towers)/5),5,figsize=(20,15*math.ceil(len(towers)/20)))
    for axis, tower in zip(axes.flatten(),towers):
        axis.imshow(tower['bitmap']*1.0)
        if text_parameters is not None:
            if type(text_parameters) is not list:
                text_parameters = [text_parameters]
            for y_offset,text_parameter in enumerate(text_parameters):
                axis.text(0,y_offset*1.,str(text_parameter+": "+str(tower[text_parameter])),color='gray',fontsize=20)
    plt.tight_layout()
    plt.show()
visualize_towers(towers)<jupyter_output><empty_output><jupyter_text>Score towers for basic difficultyFor each tower, compute the cost of solving it using a planning agent. Here, we use Best First Search without lookahead or subgoals.<jupyter_code>lower_agent = Best_First_Search_Agent(random_seed=42)
def get_tower_cost(agent,world):
    cost = 0
    agent.set_world(world)
    world.reset()
    while world.status()[0] == 'Ongoing':
        _,step_info = agent.act()
        cost += step_info['states_evaluated']
    return cost,world.status()
costs = []
statusses = []
for world in tqdm(worlds):
    cost,status = get_tower_cost(lower_agent,world)
    costs.append(cost)
    statusses.append(status)<jupyter_output><empty_output><jupyter_text>Split the basic costs into three percentiles: easy, medium, hard.<jupyter_code>difficulty_percentiles = [np.percentile(costs, i)
               for i in [33, 66, 99]]

percentiles = [None] * len(costs)
for i, cost in enumerate(costs):
    if cost < difficulty_percentiles[0]:
        percentiles[i] = 'easy'
    elif cost < difficulty_percentiles[1]:
        percentiles[i] = 'medium'
    else:
        percentiles[i] = 'hard'<jupyter_output><empty_output><jupyter_text>Find best and worst subgoalsWe compute the full subgoal tree for each tower and extract the best and worst sequence.Note: for the planned studies, we will use individual states and subgoals, not sequences of subgoals.<jupyter_code>decomposer = Rectangular_Keyholes(
    sequence_length=3,
    necessary_conditions=[
        Area_larger_than(area=1),
        Area_smaller_than(area=21),
        No_edge_rows_or_columns(),
    ],
    necessary_sequence_conditions=[
        Complete(),
        No_overlap(),
        Supported(),
    ]
)

sg_agent = Subgoal_Planning_Agent(lower_agent=lower_agent,
    random_seed=42,
    decomposer=decomposer)<jupyter_output><empty_output><jupyter_text>Calculate the subgoal tree for each tower.Sadly, the sockets seem to make this hard to parallelize.<jupyter_code># # # parallelized
# def get_subgoal_tree_from_tower(agent, world):
#     world.reset()
#     agent.set_world(world)
#     return agent.get_subgoal_tree(only_solved_sequences=True)

# agents = [copy.deepcopy(a) for a in [sg_agent]*len(worlds)]

# # remove process connection before handoff to the threads
# for world in worlds:
#     world.physics_provider.kill_server(force=True)

# trees = p_tqdm.p_map(get_subgoal_tree_from_tower, agents, worlds)
# sequential version
trees = []
for world in tqdm(worlds):
    world.reset()
    sg_agent.set_world(world)
    trees.append(sg_agent.get_subgoal_tree(only_solved_sequences=True))<jupyter_output><empty_output><jupyter_text>Select most divergent subgoalsFor each tower, select n state/subgoal combinations that are maximally divergent.<jupyter_code>NUM_SGs_PER_TOWER = 6 # how many subgoals do we want to choose?
best_worst_subgoals = []
for tree in trees:
    best_worst_subgoals.append(tree.get_most_divergent_matching_pairs_of_subgoals(NUM_SGs_PER_TOWER))
results = [{'world':world,'world name':index, 'subgoal tree':tree,'cost':cost,'percentile':percentile, 'best_worst_subgoals':best_worst_subgoal} for world,index,tree,cost,percentile,best_worst_subgoal in zip(worlds,range(len(towers)), trees,costs,percentiles,best_worst_subgoals)]<jupyter_output><empty_output><jupyter_text>Add into df<jupyter_code>df = pd.DataFrame(columns=['world name','world','best subgoal', 'worst subgoal', 'subgoal cost delta', 'tower percentile', 'state', 'blocks'])
i = 0

for r in results:
    for best_subgoal,worst_subgoal in r['best_worst_subgoals']:
        # fill in subgoal cost delta
        delta = worst_subgoal.subgoal.solution_cost - best_subgoal.subgoal.solution_cost
        # fill in state from the best subgoal
        state = best_subgoal.subgoal.past_world.current_state
        # insert into dataframe
        df.loc[i] = [r['world name'],r['world'],best_subgoal,worst_subgoal,delta,r['percentile'],state,state.blocks]
        i += 1<jupyter_output><empty_output><jupyter_text>What is the distribution over the subgoal deltas?<jupyter_code>df['subgoal cost delta'].plot()<jupyter_output><empty_output><jupyter_text>Let's display some of the subgoals<jupyter_code>for i,row in df.sort_values('subgoal cost delta',ascending=False).head(5).iterrows():
    print(i,row['subgoal cost delta'])
    # construct dummy sequence
    sequence = Subgoal_sequence([row['best subgoal'], row['worst subgoal']])
    sequence.visual_display()<jupyter_output><empty_output><jupyter_text>Let's save the dataframe to disk. This will serve as the basis for the `given_subgoal` human experiment.<jupyter_code>df.to_pickle("most divergent subgoals.pkl")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09948211489822956, 'fraction_numerical': 0.005660604600746718, 'mean_word_length': 4.778705636743215, 'pattern_counts': {'":': 1, '<': 51, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '180811', 'license': 'MIT', 'n_tokens_mistral': 2782, 'n_tokens_neox': 2654, 'n_words': 622, 'path': 'stimuli/AB_choice_experiment_stim_generation.ipynb', 'repo_name': 'cogtoolslab/projection_block_construction', 'size': '8303'}