{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":58266,"databundleVersionId":6641124,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport glob\nimport os\nimport pandas as pd\nfrom tqdm import tqdm\nfrom xgboost import XGBRegressor\nimport lightgbm as lgb\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import TensorDataset, DataLoader\n\n# =========================================================\n# 0. CONFIG\n# =========================================================\n\nSEED = 42\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\n\nOPCODE_DIM = 128\n\nTILE_MAX_FILES = 500\nLAYOUT_MAX_FILES = 500\nCONFIGS_PER_GRAPH = 200\n\nUSE_GRAPH_NORMALIZED_TARGET = True\nGRAPH_TARGET_MODE = \"log_center\"\n\n# Kaggle notebook paths — data is already here, no download needed\nDATA_ROOT = \"/kaggle/input/competitions/predict-ai-model-runtime\"\nOUTPUT_FILE = \"/kaggle/working/submission_hybrid_improved_v2.csv\"\n\n# =========================================================\n# VERIFY DATA STRUCTURE AND PATHS\n# =========================================================\n\n# Check all paths\npaths_to_check = {\n    \"tile/xla/train\":           f\"{DATA_ROOT}/npz_all/npz/tile/xla/train/*.npz\",\n    \"tile/xla/valid\":           f\"{DATA_ROOT}/npz_all/npz/tile/xla/valid/*.npz\",\n    \"tile/xla/test\":            f\"{DATA_ROOT}/npz_all/npz/tile/xla/test/*.npz\",\n    \"layout/xla/random/train\":  f\"{DATA_ROOT}/npz_all/npz/layout/xla/random/train/*.npz\",\n    \"layout/xla/random/valid\":  f\"{DATA_ROOT}/npz_all/npz/layout/xla/random/valid/*.npz\",\n    \"layout/xla/random/test\":   f\"{DATA_ROOT}/npz_all/npz/layout/xla/random/test/*.npz\",\n    \"layout/xla/default/train\": f\"{DATA_ROOT}/npz_all/npz/layout/xla/default/train/*.npz\",\n    \"layout/xla/default/valid\": f\"{DATA_ROOT}/npz_all/npz/layout/xla/default/valid/*.npz\",\n    \"layout/xla/default/test\":  f\"{DATA_ROOT}/npz_all/npz/layout/xla/default/test/*.npz\",\n    \"layout/nlp/random/train\":  f\"{DATA_ROOT}/npz_all/npz/layout/nlp/random/train/*.npz\",\n    \"layout/nlp/random/valid\":  f\"{DATA_ROOT}/npz_all/npz/layout/nlp/random/valid/*.npz\",\n    \"layout/nlp/random/test\":   f\"{DATA_ROOT}/npz_all/npz/layout/nlp/random/test/*.npz\",\n    \"layout/nlp/default/train\": f\"{DATA_ROOT}/npz_all/npz/layout/nlp/default/train/*.npz\",\n    \"layout/nlp/default/valid\": f\"{DATA_ROOT}/npz_all/npz/layout/nlp/default/valid/*.npz\",\n    \"layout/nlp/default/test\":  f\"{DATA_ROOT}/npz_all/npz/layout/nlp/default/test/*.npz\",\n}\n\nprint(\"=\" * 55)\nprint(f\"{'Path':<35} {'Count':>8}\")\nprint(\"=\" * 55)\nfor name, pattern in paths_to_check.items():\n    count = len(glob.glob(pattern))\n    status = \"✓\" if count > 0 else \"✗ MISSING\"\n    print(f\"{name:<35} {count:>6}  {status}\")\nprint(\"=\" * 55)\n\n# Check sample submission\ntry:\n    sample = pd.read_csv(f\"{DATA_ROOT}/sample_submission.csv\")\n    print(f\"\\nSample submission: {len(sample)} rows\")\n    print(sample.head(3))\nexcept Exception as e:\n    # Try alternate location\n    try:\n        sample = pd.read_csv(\"/kaggle/input/predict-ai-model-runtime/sample_submission.csv\")\n        print(f\"\\nSample submission found at alternate path: {len(sample)} rows\")\n    except:\n        print(f\"\\nSample submission not found: {e}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-12T04:08:08.983313Z","iopub.execute_input":"2026-04-12T04:08:08.983678Z","iopub.status.idle":"2026-04-12T04:08:09.648863Z","shell.execute_reply.started":"2026-04-12T04:08:08.983648Z","shell.execute_reply":"2026-04-12T04:08:09.648202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# =========================================================\n# 1. FEATURE FUNCTIONS\n# =========================================================\n\ndef extract_topology_features(edge_index, n_nodes):\n    if edge_index.ndim != 2:\n        return np.zeros(12, dtype=np.float32)\n\n    if edge_index.shape[0] == 2:\n        src = edge_index[0].astype(np.int64)\n        dst = edge_index[1].astype(np.int64)\n        n_edges = edge_index.shape[1]\n    else:\n        src = edge_index[:, 0].astype(np.int64)\n        dst = edge_index[:, 1].astype(np.int64)\n        n_edges = edge_index.shape[0]\n\n    out_deg = np.bincount(src, minlength=n_nodes).astype(np.float32)\n    in_deg = np.bincount(dst, minlength=n_nodes).astype(np.float32)\n    deg = in_deg + out_deg\n\n    density = n_edges / max(n_nodes * (n_nodes - 1), 1)\n    edge_node_ratio = n_edges / max(n_nodes, 1)\n    isolated_ratio = (deg == 0).mean().astype(np.float32) if len(deg) > 0 else 0.0\n    self_loops = (src == dst).mean().astype(np.float32) if len(src) > 0 else 0.0\n\n    topo = np.array([\n        out_deg.mean(), out_deg.std(), out_deg.max() if len(out_deg) else 0.0,\n        in_deg.mean(),  in_deg.std(),  in_deg.max() if len(in_deg) else 0.0,\n        deg.mean(),     deg.std(),     deg.max() if len(deg) else 0.0,\n        isolated_ratio,\n        edge_node_ratio,\n        density,\n        self_loops\n    ], dtype=np.float32)\n    return topo\n\ndef extract_graph_features(node_feat, edge_index, node_opcode, opcode_dim=OPCODE_DIM, normalize_hist=True):\n    f_mean = node_feat.mean(axis=0)\n    f_std = node_feat.std(axis=0)\n    f_max = node_feat.max(axis=0)\n    f_min = node_feat.min(axis=0)\n\n    n_nodes = node_feat.shape[0]\n\n    if edge_index.ndim == 2 and edge_index.shape[0] == 2:\n        n_edges = np.array([edge_index.shape[1]], dtype=np.float32)\n    else:\n        n_edges = np.array([edge_index.shape[0]], dtype=np.float32)\n\n    n_nodes_arr = np.array([n_nodes], dtype=np.float32)\n\n    hist = np.bincount(node_opcode.astype(np.int64), minlength=opcode_dim).astype(np.float32)\n    if normalize_hist:\n        hist = hist / max(len(node_opcode), 1)\n\n    topo_feat = extract_topology_features(edge_index, n_nodes)\n\n    g_feat = np.concatenate([\n        f_mean, f_std, f_max, f_min,\n        n_nodes_arr, n_edges,\n        topo_feat, hist\n    ]).astype(np.float32)\n\n    return g_feat, hist.astype(np.float32)\n\ndef build_features_with_interactions(g_feat, opcode_hist, c_feat, interaction_dim):\n    g_repeated = np.tile(g_feat, (len(c_feat), 1))\n\n    c_mean = c_feat.mean(axis=0, keepdims=True)\n    c_std = c_feat.std(axis=0, keepdims=True) + 1e-6\n    c_feat_rel = (c_feat - c_mean) / c_std\n\n    op_part = opcode_hist[:interaction_dim]\n    op_repeated = np.tile(op_part, (len(c_feat), 1))\n    c_part = c_feat[:, :interaction_dim]\n\n    interact_mul = op_repeated * c_part\n    interact_diff = op_repeated - c_part\n    interact_sum = op_repeated + c_part\n\n    X = np.concatenate([\n        g_repeated, c_feat, c_feat_rel,\n        interact_mul, interact_diff, interact_sum\n    ], axis=1).astype(np.float32)\n\n    return X\n\ndef normalize_runtime_within_graph(runtimes, mode=\"log_center\"):\n    runtimes = runtimes.astype(np.float32)\n    if mode == \"raw\":\n        return runtimes\n    if mode == \"log_center\":\n        r = np.log1p(runtimes)\n        return (r - r.mean()).astype(np.float32)\n    if mode == \"zscore\":\n        mu = runtimes.mean()\n        sd = runtimes.std() + 1e-6\n        return ((runtimes - mu) / sd).astype(np.float32)\n    if mode == \"rank\":\n        order = runtimes.argsort().argsort().astype(np.float32)\n        order = order / max(len(order) - 1, 1)\n        return order.astype(np.float32)\n    raise ValueError(f\"Unknown target mode: {mode}\")\n\ndef get_layout_config_features(node_config_feat):\n    node_cfg = node_config_feat.astype(np.float32)\n    c_mean = node_cfg.mean(axis=1)\n    c_std = node_cfg.std(axis=1)\n    c_max = node_cfg.max(axis=1)\n    c_min = node_cfg.min(axis=1)\n    return np.concatenate([c_mean, c_std, c_max, c_min], axis=1).astype(np.float32)\n\n# =========================================================\n# 2. DATA LOADING\n# =========================================================\n\n\ndef load_task_data_safe(task_type=\"tile\", split=\"train\", max_files=500, configs_per_graph=200, seed=42):\n    rng = np.random.default_rng(seed)\n\n    if task_type == \"tile\":\n        files = sorted(glob.glob(f\"{DATA_ROOT}/npz_all/npz/tile/xla/{split}/*.npz\"))[:max_files]\n        interaction_dim = 24\n    else:\n        files  = sorted(glob.glob(f\"{DATA_ROOT}/npz_all/npz/layout/xla/random/{split}/*.npz\"))[:max_files // 4]\n        files += sorted(glob.glob(f\"{DATA_ROOT}/npz_all/npz/layout/xla/default/{split}/*.npz\"))[:max_files // 4]\n        files += sorted(glob.glob(f\"{DATA_ROOT}/npz_all/npz/layout/nlp/random/{split}/*.npz\"))[:max_files // 4]\n        files += sorted(glob.glob(f\"{DATA_ROOT}/npz_all/npz/layout/nlp/default/{split}/*.npz\"))[:max_files // 4]\n        interaction_dim = 18\n\n    X_list, y_list = [], []\n    print(f\"Loading {task_type} {split} data ({len(files)} files)...\")\n\n    for f in tqdm(files):\n        d = np.load(f)\n        g_feat, opcode_hist = extract_graph_features(\n            d[\"node_feat\"], d[\"edge_index\"], d[\"node_opcode\"]\n        )\n        if task_type == \"tile\":\n            c_feat = d[\"config_feat\"].astype(np.float32)\n            runtimes = d[\"config_runtime\"].astype(np.float32)\n        else:\n            c_feat = get_layout_config_features(d[\"node_config_feat\"])\n            runtimes = d[\"config_runtime\"].astype(np.float32)\n\n        if len(c_feat) > configs_per_graph:\n            idx = rng.choice(len(c_feat), size=configs_per_graph, replace=False)\n            c_feat = c_feat[idx]\n            runtimes = runtimes[idx]\n\n        X = build_features_with_interactions(g_feat, opcode_hist, c_feat, interaction_dim)\n\n        if USE_GRAPH_NORMALIZED_TARGET:\n            y = normalize_runtime_within_graph(runtimes, mode=GRAPH_TARGET_MODE)\n        else:\n            y = runtimes.astype(np.float32)\n\n        X_list.append(X)\n        y_list.append(y)\n\n    X = np.vstack(X_list).astype(np.float32)\n    y = np.concatenate(y_list).astype(np.float32)\n    print(f\"{task_type}: X={X.shape}, y={y.shape}\")\n    return X, y\n\n\n# =========================================================\n# 3. TREE ENSEMBLES\n# =========================================================\n\ndef train_models(task_type, X, y):\n    device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n    models = []\n\n    xgb1 = XGBRegressor(\n        n_estimators=700, learning_rate=0.03, max_depth=6,\n        min_child_weight=3, subsample=0.8, colsample_bytree=0.7,\n        reg_alpha=0.1, reg_lambda=1.0, objective=\"reg:squarederror\",\n        tree_method=\"hist\", device=device, random_state=42\n    )\n    xgb1.fit(X, y)\n    models.append((\"xgb1\", xgb1))\n\n    xgb2 = XGBRegressor(\n        n_estimators=900, learning_rate=0.025, max_depth=10,\n        min_child_weight=1, subsample=0.9, colsample_bytree=0.9,\n        reg_alpha=0.0, reg_lambda=1.5, objective=\"reg:squarederror\",\n        tree_method=\"hist\", device=device, random_state=2024\n    )\n    xgb2.fit(X, y)\n    models.append((\"xgb2\", xgb2))\n\n    lgb1 = lgb.LGBMRegressor(\n        n_estimators=900, learning_rate=0.025, num_leaves=63,\n        max_depth=-1, min_child_samples=20, subsample=0.85,\n        colsample_bytree=0.85, reg_alpha=0.1, reg_lambda=1.0,\n        random_state=42, device=\"cpu\", verbose=-1  # ← FIXED\n    )\n    lgb1.fit(X, y)\n    models.append((\"lgb1\", lgb1))\n\n    return models\n\n# =========================================================\n# 4. MLP\n# =========================================================\n\nclass RuntimeMLP(nn.Module):\n    def __init__(self, input_dim):\n        super().__init__()\n        self.layers = nn.Sequential(\n            nn.Linear(input_dim, 512),\n            nn.BatchNorm1d(512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(256, 128),\n            nn.ReLU(),\n            nn.Linear(128, 1)\n        )\n\n    def forward(self, x):\n        return self.layers(x)\n\ndef standardize_features(X_train, X_test=None):\n    mean = X_train.mean(axis=0, keepdims=True)\n    std = X_train.std(axis=0, keepdims=True) + 1e-6\n    X_train_std = (X_train - mean) / std\n    if X_test is None:\n        return X_train_std.astype(np.float32), mean.astype(np.float32), std.astype(np.float32)\n    X_test_std = (X_test - mean) / std\n    return X_train_std.astype(np.float32), X_test_std.astype(np.float32), mean.astype(np.float32), std.astype(np.float32)\n\ndef train_mlp(X_train, y_train, epochs=25, batch_size=512):\n    device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n    X_train_std, mean, std = standardize_features(X_train)\n\n    if USE_GRAPH_NORMALIZED_TARGET and GRAPH_TARGET_MODE != \"raw\":\n        y_target = y_train.astype(np.float32)\n    else:\n        y_target = np.log1p(y_train).astype(np.float32)\n\n    X_tensor = torch.tensor(X_train_std, dtype=torch.float32)\n    y_tensor = torch.tensor(y_target.reshape(-1, 1), dtype=torch.float32)\n\n    dataset = TensorDataset(X_tensor, y_tensor)\n    loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n\n    model = RuntimeMLP(X_train.shape[1]).to(device)\n    optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-5)\n    criterion = nn.SmoothL1Loss()\n\n    epoch_losses = []\n    model.train()\n    for epoch in range(epochs):\n        total_loss = 0.0\n        for batch_X, batch_y in loader:\n            batch_X, batch_y = batch_X.to(device), batch_y.to(device)\n            optimizer.zero_grad()\n            preds = model(batch_X)\n            loss = criterion(preds, batch_y)\n            loss.backward()\n            optimizer.step()\n            total_loss += loss.item()\n\n        avg_loss = total_loss / len(loader)\n        epoch_losses.append(avg_loss)\n        if (epoch + 1) % 5 == 0:\n            print(f\"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.6f}\")\n\n    return model, mean, std, epoch_losses\n\ndef predict_mlp(model, X, mean, std):\n    device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n    X_std = ((X - mean) / std).astype(np.float32)\n    X_tensor = torch.tensor(X_std, dtype=torch.float32).to(device)\n    model.eval()\n    with torch.no_grad():\n        pred = model(X_tensor).cpu().numpy().flatten()\n    if USE_GRAPH_NORMALIZED_TARGET and GRAPH_TARGET_MODE != \"raw\":\n        return pred.astype(np.float32)\n    return np.expm1(pred).astype(np.float32)\n\n# =========================================================\n# 5. LOAD TRAINING DATA\n# =========================================================\n\nX_tile, y_tile = load_task_data_safe(\n    task_type=\"tile\", split=\"train\",\n    max_files=TILE_MAX_FILES, configs_per_graph=CONFIGS_PER_GRAPH, seed=SEED\n)\n\nX_layout, y_layout = load_task_data_safe(\n    task_type=\"layout\", split=\"train\",\n    max_files=LAYOUT_MAX_FILES, configs_per_graph=CONFIGS_PER_GRAPH, seed=SEED\n)\n\n# =========================================================\n# 6. TRAIN MODELS\n# =========================================================\n\nprint(\"Training tree ensembles...\")\ntile_models = train_models(\"tile\", X_tile, y_tile)\nlayout_models = train_models(\"layout\", X_layout, y_layout)\n\nprint(\"Training Tile MLP...\")\nmlp_tile, tile_mean, tile_std, tile_losses = train_mlp(X_tile, y_tile, epochs=25, batch_size=512)\n\nprint(\"Training Layout MLP...\")\nmlp_layout, layout_mean, layout_std, layout_losses = train_mlp(X_layout, y_layout, epochs=25, batch_size=512)\n\n# =========================================================\n# 7. ENSEMBLE WEIGHTS\n# =========================================================\n\ndef rank_predictions(preds):\n    return preds.argsort().argsort().astype(np.float32)\n\ndef weighted_rank_average(pred_list, weights):\n    rank_list = [rank_predictions(preds) for preds in pred_list]\n    rank_mat = np.stack(rank_list, axis=0)\n    weights = np.array(weights, dtype=np.float32).reshape(-1, 1)\n    blended = (rank_mat * weights).sum(axis=0) / weights.sum()\n    return blended\n\nTILE_WEIGHTS =   [0.40, 0.40, 0.20, 0.00]  # xgb1, xgb2, lgb1, mlp (MLP dropped)\nLAYOUT_WEIGHTS = [0.20, 0.35, 0.45, 0.00]  # xgb1, xgb2, lgb1, mlp (MLP dropped)\n\n# =========================================================\n# 8. GENERATE SUBMISSION\n# =========================================================\n\ndef generate_hybrid_submission(output_file=OUTPUT_FILE):\n    task_patterns = {\n        \"tile:xla\":           f\"{DATA_ROOT}/npz_all/npz/tile/xla/test/*.npz\",           # ← FIXED\n        \"layout:xla:random\":  f\"{DATA_ROOT}/npz_all/npz/layout/xla/random/test/*.npz\",  # ← FIXED\n        \"layout:xla:default\": f\"{DATA_ROOT}/npz_all/npz/layout/xla/default/test/*.npz\", # ← FIXED\n        \"layout:nlp:random\":  f\"{DATA_ROOT}/npz_all/npz/layout/nlp/random/test/*.npz\",  # ← FIXED\n        \"layout:nlp:default\": f\"{DATA_ROOT}/npz_all/npz/layout/nlp/default/test/*.npz\", # ← FIXED\n    }\n\n    results = []\n\n    for task_name, pattern in task_patterns.items():\n        files = sorted(glob.glob(pattern))\n        is_tile = \"tile\" in task_name\n\n        curr_tree_models = tile_models if is_tile else layout_models\n        curr_mlp         = mlp_tile if is_tile else mlp_layout\n        curr_mean        = tile_mean if is_tile else layout_mean\n        curr_std         = tile_std if is_tile else layout_std\n        curr_weights     = TILE_WEIGHTS if is_tile else LAYOUT_WEIGHTS\n        interaction_dim  = 24 if is_tile else 18\n\n        print(f\"Predicting {task_name}...\")\n\n        for f in tqdm(files):\n            d = np.load(f)\n            g_feat, op_hist = extract_graph_features(\n                d[\"node_feat\"], d[\"edge_index\"], d[\"node_opcode\"]\n            )\n\n            if is_tile:\n                c_feat = d[\"config_feat\"].astype(np.float32)\n            else:\n                c_feat = get_layout_config_features(d[\"node_config_feat\"])\n\n            X = build_features_with_interactions(g_feat, op_hist, c_feat, interaction_dim)\n\n            tree_preds = [model.predict(X).astype(np.float32) for _, model in curr_tree_models]\n            mlp_preds  = predict_mlp(curr_mlp, X, curr_mean, curr_std)\n\n            all_preds = tree_preds + [mlp_preds]\n            avg_rank  = weighted_rank_average(all_preds, curr_weights)\n            ranked    = np.argsort(avg_rank).tolist()\n\n            results.append({\n                \"ID\": f\"{task_name}:{os.path.basename(f)[:-4]}\",\n                \"TopConfigs\": \";\".join(map(str, ranked))\n            })\n\n    pd.DataFrame(results).to_csv(output_file, index=False)\n    print(f\"Saved to {output_file}\")\n\n\ngenerate_hybrid_submission(OUTPUT_FILE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T04:13:39.578147Z","iopub.execute_input":"2026-04-12T04:13:39.578507Z","iopub.status.idle":"2026-04-12T04:43:56.672129Z","shell.execute_reply.started":"2026-04-12T04:13:39.578478Z","shell.execute_reply":"2026-04-12T04:43:56.67128Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\nif os.path.exists(\"/kaggle/working/submission_hybrid_improved_v2.csv\"):\n    df = pd.read_csv(\"/kaggle/working/submission_hybrid_improved_v2.csv\")\n    print(f\"File exists! Rows: {len(df)} (expected 894)\")\n    print(df.head(3))\nelse:\n    print(\"File not found. Files in working directory:\")\n    print(os.listdir(\"/kaggle/working/\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T04:56:33.946328Z","iopub.execute_input":"2026-04-12T04:56:33.946654Z","iopub.status.idle":"2026-04-12T04:56:34.029633Z","shell.execute_reply.started":"2026-04-12T04:56:33.946625Z","shell.execute_reply":"2026-04-12T04:56:34.029035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check ALL files in working directory\nimport os\nfor f in os.listdir('/kaggle/working/'):\n    size = os.path.getsize(f'/kaggle/working/{f}')\n    print(f\"{f} — {size/1024:.1f} KB\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T04:58:26.152405Z","iopub.execute_input":"2026-04-12T04:58:26.152709Z","iopub.status.idle":"2026-04-12T04:58:26.158096Z","shell.execute_reply.started":"2026-04-12T04:58:26.152683Z","shell.execute_reply":"2026-04-12T04:58:26.15739Z"}},"outputs":[],"execution_count":null}]}