Skip to content

Port Ideogram 4.0 to JAX / MaxDiffusion#442

Open
mbohlool wants to merge 1 commit into
mainfrom
ideogram_jax
Open

Port Ideogram 4.0 to JAX / MaxDiffusion#442
mbohlool wants to merge 1 commit into
mainfrom
ideogram_jax

Conversation

@mbohlool

@mbohlool mbohlool commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Title: Port Ideogram 4.0 to JAX / MaxDiffusion

Summary:
This PR ports the Ideogram 4.0 pipeline to JAX. It introduces the native Flax NNX Ideogram4Transformer, integrates the TorchaxQwen3VLTextEncoder for complex prompt processing, and implements the required asymmetric CFG denoising loop. It also includes caption key re-ordering in the pipeline to enforce Ideogram's strict JSON-based prompt schema (high_level_description, style_description, compositional_deconstruction).

Generated Examples:

Prompt:

{
  "high_level_description": "A sleek red sports car driving through a futuristic cyberpunk city in the rain, with neon reflections on the wet asphalt.",
  "style_description": {
    "aesthetics": "cyberpunk, futuristic, moody, dynamic",
    "lighting": "neon lights, cinematic lighting, reflections",
    "photo": "photorealistic, motion blur on background",
    "medium": "digital photography",
    "color_palette": ["#E74C3C", "#3498DB", "#9B59B6", "#1ABC9C", "#2C3E50"]
  },
  "compositional_deconstruction": {
    "background": "A bustling cyberpunk cityscape at night, towering skyscrapers, heavy rain.",
    "elements": [
      {
        "type": "obj",
        "bbox": [400, 100, 900, 900],
        "desc": "A highly detailed, sleek red futuristic sports car speeding forward."
      },
      {
        "type": "obj",
        "bbox": [800, 0, 1000, 1000],
        "desc": "Wet asphalt road reflecting vibrant pink and blue neon lights."
      }
    ]
  }
}
pr_magic_car_ideogram_output_42_0

Prompt:

{
  "high_level_description": "A ginger cat wearing a tiny wizard hat reading a glowing spellbook.",
  "style_description": {
    "aesthetics": "magical, fantasy, whimsical",
    "lighting": "warm glowing light, magical illumination from the book",
    "photo": "highly detailed, macro photography, shallow depth of field",
    "medium": "digital photography, fantasy illustration",
    "color_palette": ["#E67E22", "#8E44AD", "#F1C40F", "#2C3E50", "#D35400"]
  },
  "compositional_deconstruction": {
    "background": "A dimly lit, dusty wizard's study filled with old scrolls and potions.",
    "elements": [
      {
        "type": "obj",
        "bbox": [100, 200, 800, 800],
        "desc": "A fluffy ginger cat wearing a detailed purple wizard hat with gold stars."
      },
      {
        "type": "obj",
        "bbox": [700, 200, 1000, 800],
        "desc": "An ancient, open spellbook glowing with magical yellow light."
      }
    ]
  }
}
pr_magic_cat_ideogram_output_42_0

Prompt:

{
  "high_level_description": "A stunning cinematic shot of a futuristic city at night with flying cars and bright neon lights.",
  "style_description": {
    "aesthetics": "cinematic, futuristic, sci-fi",
    "lighting": "neon lighting, night time, glowing cityscapes",
    "photo": "photorealistic, hyper-detailed, wide angle",
    "medium": "digital photography",
    "color_palette": ["#00FFFF", "#FF00FF", "#000080", "#FFD700", "#191970"]
  },
  "compositional_deconstruction": {
    "background": "A dark night sky overlooking a sprawling futuristic megalopolis.",
    "elements": [
      {
        "type": "obj",
        "bbox": [200, 0, 1000, 1000],
        "desc": "Dense cityscape with towering skyscrapers illuminated by intricate neon signs."
      },
      {
        "type": "obj",
        "bbox": [100, 200, 400, 800],
        "desc": "Several futuristic flying cars with glowing thrusters streaking across the sky."
      }
    ]
  }
}
pr_magic_city_ideogram_output_42_0

@mbohlool
mbohlool requested a review from entrpn as a code owner July 8, 2026 21:37
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

@mbohlool
mbohlool force-pushed the ideogram_jax branch 8 times, most recently from 83fcf30 to 23fa1c2 Compare July 8, 2026 23:27
…r and Qwen text encoder

- Implements `Ideogram4Transformer` using `flax.nnx`
- Ports `TorchaxQwen3VLTextEncoder` to handle Ideogram's LLM prompt conditions
- Adds strict JSON schema caption key re-ordering in `IdeogramPipeline`
- Adds full asymmetric CFG denoising loop
@mbohlool

mbohlool commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

/gemini review

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Hi @Perseus14, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 I'm sorry @Perseus14, but I was unable to process your request. Please see the logs for more details.

@mbohlool

mbohlool commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

/gemini review

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Hi @mbohlool, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 I'm sorry @mbohlool, but I was unable to process your request. Please see the logs for more details.

@eltsai
eltsai self-requested a review July 14, 2026 21:50
@mbohlool

Copy link
Copy Markdown
Collaborator Author

/gemini review

# 1. Warmup Compilation
config.get_keys()["enable_profiler"] = False
config.get_keys()["enable_ml_diagnostics"] = False
config.get_keys()["num_inference_steps"] = 2 # lower for warmup

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have z, _, _ = jax.lax.fori_loop(0, num_steps, denoise_step, init_val) in src/maxdiffusion/pipelines/ideogram/ideogram_pipeline.py, setting the warmup step as 2 means that the later 50 step inference time includes compile

I think this is the reason why we see a 50 sec generation time. We could either:

  1. go with what WAN does: only compile one step and do a plain for loop
  2. warmup with 50 steps

I am not sure which would be faster, it's worth testing

@@ -0,0 +1,138 @@
import jax

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file and some other files are missing the license header, this would incur errors when doing copy-bara

@@ -0,0 +1,138 @@
import jax

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this file is named for torchax but there's no torchax here (I see device=torch.device("cpu"))

self.bias = None

def forward(self, x: torch.Tensor) -> torch.Tensor:
w = self.weight.to(x.dtype) * self.weight_scale.to(x.dtype).unsqueeze(1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line dequantizes the full weight tensor on every forward. We should dequantize once at load and cache

q, k = _apply_rotary_pos_emb(q, k, cos, sin)

# Block-diagonal mask from segment ids
attn_mask = jnp.expand_dims(segment_ids, axis=2) == jnp.expand_dims(segment_ids, axis=1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure why we need masks to do this...we can leave this for future optimization


# JAX scaled dot product attention
scale = 1.0 / math.sqrt(self.head_dim)
attn_weights = jnp.einsum("bhqd,bhkd->bhqk", q, k) * scale

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in base_ideogram.yml we have attention: "flash" but here it uses a naive attention, can be optimized later


class Ideogram4Attention(nnx.Module):

def __init__(self, rngs: nnx.Rngs, hidden_size: int, num_heads: int, eps: float = 1e-5, dtype=jnp.float32):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtype=jnp.float32 default is never overridden, so weights_dtype/activations_dtype: bfloat16 in the config are inert.

schedule_fn = get_schedule_for_resolution((height, width), known_mean=0.5)
step_intervals = make_step_intervals(num_steps)
# Compute schedule evaluated at all intervals. schedule_fn returns a continuous scalar mapping.
sigmas = jnp.array(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be vectorized

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants