{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"},{"sourceId":55590013,"sourceType":"kernelVersion"},{"sourceId":97479937,"sourceType":"kernelVersion"}],"dockerImageVersionId":30886,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Like Paintings","metadata":{}},{"cell_type":"markdown","source":"### Environment Setup and Imports","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nprint(tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T17:59:47.363119Z","iopub.execute_input":"2025-04-02T17:59:47.363462Z","iopub.status.idle":"2025-04-02T17:59:47.367854Z","shell.execute_reply.started":"2025-04-02T17:59:47.363433Z","shell.execute_reply":"2025-04-02T17:59:47.366887Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pip install tfa-nightly","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:01:35.553632Z","iopub.execute_input":"2025-04-02T18:01:35.554033Z","iopub.status.idle":"2025-04-02T18:01:41.026199Z","shell.execute_reply.started":"2025-04-02T18:01:35.554000Z","shell.execute_reply":"2025-04-02T18:01:41.025218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, Model, initializers\n\n# --------------------------\n# 1. Self-Attention Layer (Fixed)\n# --------------------------\nclass SelfAttention(layers.Layer):\n    def __init__(self, filters):\n        \"\"\" Self-Attention Layer for capturing global dependencies. \"\"\"\n        super(SelfAttention, self).__init__()\n        self.filters = filters\n        self.query = layers.Conv2D(filters // 8, (1, 1), padding=\"same\")\n        self.key = layers.Conv2D(filters // 8, (1, 1), padding=\"same\")\n        self.value = layers.Conv2D(filters, (1, 1), padding=\"same\")\n        self.gamma = self.add_weight(shape=(1,), initializer=\"zeros\", trainable=True)\n\n    def call(self, x):\n        batch_size, height, width, channels = tf.shape(x)[0], tf.shape(x)[1], tf.shape(x)[2], tf.shape(x)[3]\n\n        q = tf.reshape(self.query(x), (batch_size, height * width, channels // 8))  # [B, HW, C/8]\n        k = tf.reshape(self.key(x), (batch_size, height * width, channels // 8))    # [B, HW, C/8]\n        v = tf.reshape(self.value(x), (batch_size, height * width, channels))       # [B, HW, C]\n\n        attention_map = tf.nn.softmax(tf.matmul(q, k, transpose_b=True))  # [B, HW, HW]\n        out = tf.matmul(attention_map, v)  # [B, HW, C]\n\n        out = tf.reshape(out, (batch_size, height, width, channels))  # Restore original shape\n        return self.gamma * out + x  # Weighted residual connection\n\n# --------------------------\n# 2. Adaptive Instance Normalization (AdaIN)\n# --------------------------\nclass AdaIN(layers.Layer):\n    def __init__(self, epsilon=1e-5):\n        super(AdaIN, self).__init__()\n        self.epsilon = epsilon\n\n    def call(self, x):\n        mean, var = tf.nn.moments(x, axes=[1, 2], keepdims=True)\n        std = tf.sqrt(var + self.epsilon)\n        return (x - mean) / std\n\n# --------------------------\n# 3. ResNet Block with AdaIN and Self-Attention\n# --------------------------\ndef resnet_block(filters):\n    def block(x):\n        res = layers.Conv2D(filters, (3, 3), padding=\"same\")(x)\n        res = AdaIN()(res)\n        res = layers.ReLU()(res)\n        res = SelfAttention(filters)(res)  # Add Self-Attention\n        res = layers.Conv2D(filters, (3, 3), padding=\"same\")(res)\n        res = AdaIN()(res)\n        return layers.Add()([x, res])  # Residual Connection\n    return block\n\n# --------------------------\n# 4. Generator Model (U-Net + ResNet)\n# --------------------------\ndef build_generator():\n    inputs = layers.Input(shape=(256, 256, 3))\n\n    # Encoder\n    x = layers.Conv2D(64, (7, 7), padding=\"same\", strides=1)(inputs)\n    x = AdaIN()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2D(128, (3, 3), padding=\"same\", strides=2)(x)\n    x = AdaIN()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2D(256, (3, 3), padding=\"same\", strides=2)(x)\n    x = AdaIN()(x)\n    x = layers.ReLU()(x)\n\n    # Residual Blocks\n    for _ in range(6):\n        x = resnet_block(256)(x)\n\n    # Decoder\n    x = layers.Conv2DTranspose(128, (3, 3), padding=\"same\", strides=2)(x)\n    x = AdaIN()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2DTranspose(64, (3, 3), padding=\"same\", strides=2)(x)\n    x = AdaIN()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2D(3, (7, 7), padding=\"same\", activation=\"tanh\")(x)\n\n    return Model(inputs, x, name=\"Generator\")\n\n# --------------------------\n# 5. Discriminator Model (PatchGAN)\n# --------------------------\ndef build_discriminator():\n    inputs = layers.Input(shape=(256, 256, 3))\n\n    x = layers.Conv2D(64, (4, 4), strides=2, padding=\"same\")(inputs)\n    x = layers.LeakyReLU(alpha=0.2)(x)\n\n    x = layers.Conv2D(128, (4, 4), strides=2, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(alpha=0.2)(x)\n\n    x = layers.Conv2D(256, (4, 4), strides=2, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(alpha=0.2)(x)\n\n    x = layers.Conv2D(512, (4, 4), strides=1, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.LeakyReLU(alpha=0.2)(x)\n\n    x = layers.Conv2D(1, (4, 4), strides=1, padding=\"same\", activation=\"sigmoid\")(x)\n\n    return Model(inputs, x, name=\"Discriminator\")\n\n# --------------------------\n# 6. Instantiate Models\n# --------------------------\ngenerator_G = build_generator()  # Photo → Monet\ngenerator_F = build_generator()  # Monet → Photo\ndiscriminator_X = build_discriminator()  # Discriminator for real vs fake photos\ndiscriminator_Y = build_discriminator()  # Discriminator for real vs fake Monet paintings\n\nprint(\"✅ Models initialized successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:03:34.500474Z","iopub.execute_input":"2025-04-02T18:03:34.500813Z","iopub.status.idle":"2025-04-02T18:03:36.357313Z","shell.execute_reply.started":"2025-04-02T18:03:34.500769Z","shell.execute_reply":"2025-04-02T18:03:36.356491Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import optimizers\n\n# --------------------------\n# 1. Adversarial Loss (Least Squares GAN)\n# --------------------------\nmse_loss = tf.keras.losses.MeanSquaredError()\n\ndef generator_loss(fake_output):\n    \"\"\" Adversarial loss for generators (LS-GAN). \"\"\"\n    return mse_loss(tf.ones_like(fake_output), fake_output)  # Try to fool discriminator\n\ndef discriminator_loss(real_output, fake_output):\n    \"\"\" Discriminator loss (LS-GAN). \"\"\"\n    real_loss = mse_loss(tf.ones_like(real_output), real_output)  # Real images should be 1\n    fake_loss = mse_loss(tf.zeros_like(fake_output), fake_output)  # Fake images should be 0\n    return (real_loss + fake_loss) * 0.5  # Average loss\n\n# --------------------------\n# 2. Cycle Consistency Loss (Content Preservation)\n# --------------------------\nmae_loss = tf.keras.losses.MeanAbsoluteError()\n\ndef cycle_loss(real_image, cycled_image, lambda_cycle=10):\n    \"\"\" Ensures translated image can be reconstructed back to original. \"\"\"\n    return lambda_cycle * mae_loss(real_image, cycled_image)\n\n# --------------------------\n# 3. Identity Loss (Style Preservation)\n# --------------------------\ndef identity_loss(real_image, same_image, lambda_identity=5):\n    \"\"\" Encourages generators to retain color composition. \"\"\"\n    return lambda_identity * mae_loss(real_image, same_image)\n\n# --------------------------\n# 4. Optimizers (Separate for each model)\n# --------------------------\ngenerator_G_optimizer = optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\ngenerator_F_optimizer = optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\ndiscriminator_X_optimizer = optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\ndiscriminator_Y_optimizer = optimizers.Adam(learning_rate=2e-4, beta_1=0.5)\n\nprint(\"✅ Loss functions & optimizers initialized!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:03:36.358466Z","iopub.execute_input":"2025-04-02T18:03:36.358820Z","iopub.status.idle":"2025-04-02T18:03:36.373512Z","shell.execute_reply.started":"2025-04-02T18:03:36.358763Z","shell.execute_reply":"2025-04-02T18:03:36.372827Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\n# --------------------------\n# 1. Training Step Function\n# --------------------------\n@tf.function\ndef train_step(real_x, real_y):\n    \"\"\"Performs one training step: forward pass, compute losses, and backpropagation.\"\"\"\n\n    with tf.GradientTape(persistent=True) as tape:\n        # -------------------------------\n        # Forward Pass - Generators\n        # -------------------------------\n        fake_y = generator_G(real_x, training=True)  # X → Y\n        cycled_x = generator_F(fake_y, training=True)  # Y' → X\n        \n        fake_x = generator_F(real_y, training=True)  # Y → X\n        cycled_y = generator_G(fake_x, training=True)  # X' → Y\n\n        # Identity Mapping (Preserve colors for images already in target domain)\n        same_y = generator_G(real_y, training=True)  # Y → Y\n        same_x = generator_F(real_x, training=True)  # X → X\n        \n        # -------------------------------\n        # Forward Pass - Discriminators\n        # -------------------------------\n        disc_real_x = discriminator_X(real_x, training=True)  # Real X\n        disc_fake_x = discriminator_X(fake_x, training=True)  # Fake X (generated)\n\n        disc_real_y = discriminator_Y(real_y, training=True)  # Real Y\n        disc_fake_y = discriminator_Y(fake_y, training=True)  # Fake Y (generated)\n\n        # -------------------------------\n        # Compute Losses\n        # -------------------------------\n        # Generator Adversarial Loss\n        gen_G_loss = generator_loss(disc_fake_y)\n        gen_F_loss = generator_loss(disc_fake_x)\n\n        # Cycle Consistency Loss\n        cycle_X_loss = cycle_loss(real_x, cycled_x)\n        cycle_Y_loss = cycle_loss(real_y, cycled_y)\n        total_cycle_loss = cycle_X_loss + cycle_Y_loss\n\n        # Identity Loss\n        id_X_loss = identity_loss(real_x, same_x)\n        id_Y_loss = identity_loss(real_y, same_y)\n        total_identity_loss = id_X_loss + id_Y_loss\n\n        # Total Generator Loss\n        total_gen_G_loss = gen_G_loss + total_cycle_loss + total_identity_loss\n        total_gen_F_loss = gen_F_loss + total_cycle_loss + total_identity_loss\n\n        # Discriminator Loss\n        disc_X_loss = discriminator_loss(disc_real_x, disc_fake_x)\n        disc_Y_loss = discriminator_loss(disc_real_y, disc_fake_y)\n\n    # --------------------------\n    # Compute Gradients\n    # --------------------------\n    generator_G_gradients = tape.gradient(total_gen_G_loss, generator_G.trainable_variables)\n    generator_F_gradients = tape.gradient(total_gen_F_loss, generator_F.trainable_variables)\n    discriminator_X_gradients = tape.gradient(disc_X_loss, discriminator_X.trainable_variables)\n    discriminator_Y_gradients = tape.gradient(disc_Y_loss, discriminator_Y.trainable_variables)\n\n    # --------------------------\n    # Apply Gradients (Backpropagation)\n    # --------------------------\n    generator_G_optimizer.apply_gradients(zip(generator_G_gradients, generator_G.trainable_variables))\n    generator_F_optimizer.apply_gradients(zip(generator_F_gradients, generator_F.trainable_variables))\n    discriminator_X_optimizer.apply_gradients(zip(discriminator_X_gradients, discriminator_X.trainable_variables))\n    discriminator_Y_optimizer.apply_gradients(zip(discriminator_Y_gradients, discriminator_Y.trainable_variables))\n\n    return {\n        \"G_G_loss\": total_gen_G_loss,\n        \"G_F_loss\": total_gen_F_loss,\n        \"D_X_loss\": disc_X_loss,\n        \"D_Y_loss\": disc_Y_loss\n    }\n\nprint(\"✅ Training step function initialized!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:03:36.374541Z","iopub.execute_input":"2025-04-02T18:03:36.374832Z","iopub.status.idle":"2025-04-02T18:03:36.387643Z","shell.execute_reply.started":"2025-04-02T18:03:36.374778Z","shell.execute_reply":"2025-04-02T18:03:36.386849Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow_addons as tfa\nimport glob\nimport random\nimport os\n\n# --------------------------\n# 1. Dataset Paths\n# --------------------------\nPHOTO_DIR = \"/kaggle/input/c/gan-getting-started/photo_jpg\"  \nMONET_DIR = \"/kaggle/input/c/gan-getting-started/monet_jpg\" \n\n# --------------------------\n# 2. Image Preprocessing Function\n# --------------------------\ndef preprocess_image(image):\n    \"\"\"Resizes image to 256x256 and normalizes pixel values to [-1, 1].\"\"\"\n    image = tf.image.resize(image, (256, 256))\n    image = (image - 127.5) / 127.5  # Normalize to [-1, 1]\n    return image\n\n# --------------------------\n# 3. Data Augmentation\n# --------------------------\ndef augment_image(image):\n    \"\"\"Applies random jitter and flip to the image.\"\"\"\n    image = tf.image.random_flip_left_right(image)  # Horizontal flip\n    image = tf.image.random_saturation(image, 0.8, 1.2)  # Random saturation\n    image = tf.image.random_brightness(image, 0.1)  # Random brightness\n    return image\n\n# --------------------------\n# 4. Load Dataset from Files\n# --------------------------\ndef load_image(filename):\n    \"\"\"Loads and preprocesses image from file.\"\"\"\n    image = tf.io.read_file(filename)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = preprocess_image(image)\n    return image\n\n# --------------------------\n# 5. Build TensorFlow Dataset Pipeline\n# --------------------------\ndef get_dataset(image_paths, batch_size=1, augment=False):\n    \"\"\"Creates a TensorFlow dataset pipeline with optional augmentation.\"\"\"\n    dataset = tf.data.Dataset.from_tensor_slices(image_paths)\n    dataset = dataset.map(load_image, num_parallel_calls=tf.data.AUTOTUNE)\n\n    if augment:\n        dataset = dataset.map(augment_image, num_parallel_calls=tf.data.AUTOTUNE)\n\n    dataset = dataset.shuffle(len(image_paths)).batch(batch_size).prefetch(tf.data.AUTOTUNE)\n    return dataset\n\n# --------------------------\n# 6. Load Datasets\n# --------------------------\nphoto_paths = glob.glob(os.path.join(PHOTO_DIR, \"*.jpg\"))\nmonet_paths = glob.glob(os.path.join(MONET_DIR, \"*.jpg\"))\n\nphoto_ds = get_dataset(photo_paths, batch_size=1, augment=True)\nmonet_ds = get_dataset(monet_paths, batch_size=1, augment=True)\n\nprint(f\"✅ Loaded {len(photo_paths)} photo images and {len(monet_paths)} Monet images.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:03:37.479121Z","iopub.execute_input":"2025-04-02T18:03:37.479485Z","iopub.status.idle":"2025-04-02T18:03:37.623004Z","shell.execute_reply.started":"2025-04-02T18:03:37.479453Z","shell.execute_reply":"2025-04-02T18:03:37.622211Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\n\n# --------------------------\n# 1. Loss Functions\n# --------------------------\nloss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n\ndef generator_loss(fake_output):\n    \"\"\"Generator loss encouraging fooling the discriminator.\"\"\"\n    return loss_object(tf.ones_like(fake_output), fake_output)\n\ndef discriminator_loss(real_output, fake_output):\n    \"\"\"Discriminator loss penalizing incorrect classification.\"\"\"\n    real_loss = loss_object(tf.ones_like(real_output), real_output)\n    fake_loss = loss_object(tf.zeros_like(fake_output), fake_output)\n    return (real_loss + fake_loss) * 0.5\n\ndef cycle_consistency_loss(real_image, cycled_image):\n    \"\"\"Cycle loss ensuring image consistency after transformations.\"\"\"\n    return tf.reduce_mean(tf.abs(real_image - cycled_image))\n\ndef identity_loss_fn(real_image, same_image):\n    \"\"\"Identity loss ensuring the generator does not change unchanged images.\"\"\"\n    return tf.reduce_mean(tf.abs(real_image - same_image))\n\n# Set loss weights\ncycle_loss_weight = 10.0\nidentity_loss_weight = 5.0\n\n# --------------------------\n# 2. Training Step Function\n# --------------------------\n@tf.function\ndef train_step(real_x, real_y):\n    \"\"\"Performs one training step for CycleGAN.\"\"\"\n    with tf.GradientTape(persistent=True) as tape:\n        # Forward Cycle\n        fake_y = generator_G(real_x, training=True)\n        cycled_x = generator_F(fake_y, training=True)\n\n        fake_x = generator_F(real_y, training=True)\n        cycled_y = generator_G(fake_x, training=True)\n\n        # Identity Mapping\n        same_x = generator_F(real_x, training=True)\n        same_y = generator_G(real_y, training=True)\n\n        # Discriminator Predictions\n        disc_real_x = discriminator_X(real_x, training=True)\n        disc_fake_x = discriminator_X(fake_x, training=True)\n\n        disc_real_y = discriminator_Y(real_y, training=True)\n        disc_fake_y = discriminator_Y(fake_y, training=True)\n\n        # Compute Losses\n        gen_G_loss = generator_loss(disc_fake_y)\n        gen_F_loss = generator_loss(disc_fake_x)\n\n        cycle_loss = cycle_consistency_loss(real_x, cycled_x) + cycle_consistency_loss(real_y, cycled_y)\n        identity_loss_value = identity_loss_weight * (identity_loss_fn(real_y, same_y) + identity_loss_fn(real_x, same_x))\n\n        total_gen_G_loss = gen_G_loss + cycle_loss_weight * cycle_loss + identity_loss_value\n        total_gen_F_loss = gen_F_loss + cycle_loss_weight * cycle_loss + identity_loss_value\n\n        disc_X_loss = discriminator_loss(disc_real_x, disc_fake_x)\n        disc_Y_loss = discriminator_loss(disc_real_y, disc_fake_y)\n\n    # Compute Gradients\n    gen_G_gradients = tape.gradient(total_gen_G_loss, generator_G.trainable_variables)\n    gen_F_gradients = tape.gradient(total_gen_F_loss, generator_F.trainable_variables)\n    disc_X_gradients = tape.gradient(disc_X_loss, discriminator_X.trainable_variables)\n    disc_Y_gradients = tape.gradient(disc_Y_loss, discriminator_Y.trainable_variables)\n\n    # Apply Gradients (Backprop)\n    generator_G_optimizer.apply_gradients(zip(gen_G_gradients, generator_G.trainable_variables))\n    generator_F_optimizer.apply_gradients(zip(gen_F_gradients, generator_F.trainable_variables))\n    discriminator_X_optimizer.apply_gradients(zip(disc_X_gradients, discriminator_X.trainable_variables))\n    discriminator_Y_optimizer.apply_gradients(zip(disc_Y_gradients, discriminator_Y.trainable_variables))\n\n    return total_gen_G_loss, total_gen_F_loss, disc_X_loss, disc_Y_loss\n\n# --------------------------\n# 3. Training Loop Execution\n# --------------------------\nEPOCHS = 50\nSAVE_INTERVAL = 5\n\nfor epoch in range(EPOCHS):\n    epoch_losses = {'G_G': [], 'G_F': [], 'D_X': [], 'D_Y': []}\n    \n    for image_x, image_y in tf.data.Dataset.zip((photo_ds, monet_ds)):\n        gen_G_loss, gen_F_loss, disc_X_loss, disc_Y_loss = train_step(image_x, image_y)\n        \n        epoch_losses['G_G'].append(gen_G_loss.numpy())\n        epoch_losses['G_F'].append(gen_F_loss.numpy())\n        epoch_losses['D_X'].append(disc_X_loss.numpy())\n        epoch_losses['D_Y'].append(disc_Y_loss.numpy())\n\n    # Print Epoch Statistics\n    print(f\"\\nEpoch {epoch + 1}/{EPOCHS}\")\n    print(f\"Generator G Loss: {np.mean(epoch_losses['G_G']):.4f}\")\n    print(f\"Generator F Loss: {np.mean(epoch_losses['G_F']):.4f}\")\n    print(f\"Discriminator X Loss: {np.mean(epoch_losses['D_X']):.4f}\")\n    print(f\"Discriminator Y Loss: {np.mean(epoch_losses['D_Y']):.4f}\")\n\n    # Save Checkpoint & Generate Sample Images\n    if (epoch + 1) % SAVE_INTERVAL == 0:\n        print(\"\\n🔹 Generating sample images...\")\n        \n        test_photo = next(iter(photo_ds))\n        test_monet = next(iter(monet_ds))\n        \n        plt.figure(figsize=(15, 5))\n        \n        # Photo → Monet\n        plt.subplot(1, 2, 1)\n        plt.imshow(test_photo[0] * 0.5 + 0.5)\n        plt.title(\"Input Photo\")\n        plt.axis('off')\n\n        plt.subplot(1, 2, 2)\n        generated_monet = generator_G(test_photo, training=False)\n        plt.imshow(generated_monet[0] * 0.5 + 0.5)\n        plt.title(\"Generated Monet\")\n        plt.axis('off')\n\n        plt.show()\n        \n        # Save Models\n        generator_G.save(f\"generator_G_epoch_{epoch+1}.h5\")\n        generator_F.save(f\"generator_F_epoch_{epoch+1}.h5\")\n        print(f\"✅ Models saved at epoch {epoch + 1}\")\n\nprint(\"🎉 Training Complete!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-02T18:04:57.408297Z","iopub.execute_input":"2025-04-02T18:04:57.408671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}