Skip to content

GPU autoencoder ignores target_true_p#85

Open
drmckay wants to merge 2 commits into
cair:mainfrom
drmckay:fix/gpu-autoencoder-target-true-p
Open

GPU autoencoder ignores target_true_p#85
drmckay wants to merge 2 commits into
cair:mainfrom
drmckay:fix/gpu-autoencoder-target-true-p

Conversation

@drmckay

@drmckay drmckay commented Jul 1, 2026

Copy link
Copy Markdown

Fix GPU autoencoder: honor target_true_p; drop stray encoded_X arg

Relation to #81: that PR ("quick workaround to make the autoencoder run on GPU") addresses the
same two symptoms but keeps the semantic bug: it swallows target_true_p via **kwargs and draws
np.random.choice(2) — an unbiased 50/50 from an unseeded global RNG, diverging from the CPU
path — and works around the stray encoded_X element instead of removing it. This PR honors
target_true_p with the clause bank's seeded RNG (CPU parity) and drops the stray tuple element
at the source. Verified end-to-end on an RTX 3090 (results below).

Symptom

TMAutoEncoder(platform='GPU').fit(...) raises:

TypeError: produce_autoencoder_example() got an unexpected keyword argument 'target_true_p'

The GPU autoencoder cannot run at all.

Root cause

TMAutoEncoder.fit() (and clause_precision/clause_recall) call:

self.clause_bank.produce_autoencoder_example(encoded_X=, target=, target_true_p=, accumulation=)
  • CPU ClauseBank.produce_autoencoder_example(self, encoded_X, target, target_true_p, accumulation)
    draws target_value = self.rng.random() <= target_true_p (biased by the feature's true
    probability) and passes it to the C kernel.
  • GPU ClauseBankCudaDevice.produce_autoencoder_example(self, encoded_X, target, accumulation)
    has no target_true_p parameter, so the call fails. Even the body was wrong:
    target_value = self.rng_gen.choice(2) — an unbiased 50/50 that ignores target_true_p, and
    .choice does not exist on pycuda's XORWOWRandomNumberGenerator (it appears nowhere in
    curandom.py); that line never executed only because the TypeError fired first.

So the GPU path was both call-incompatible and semantically different from the CPU path.

Fix (commit 1)

Mirror the CPU path. Add target_true_p to the GPU method (and the ClauseBankCuda dispatcher),
and draw the polarity host-side with the clause bank's seeded RNG:

target_value = int(self.coordinator.host.rng.random() <= target_true_p)

The kernel already takes int target_value, so no .cu change is needed. target_value is a single
host-side branch bit per call — the CPU decides it host-side too; the in-kernel per-row sampling keeps
using the device curand RNG (self.rng_gen.state), unchanged.

A second, latent defect exposed by this fix (commit 2)

Once the target_true_p TypeError was gone, the kernel launch failed with
pack requires exactly 13 arguments: prepare_X_autoencoder returned the host active_output
numpy array as a stray 2nd tuple element, which produce_autoencoder_example splats into the
launch (*encoded_X) — 14 args for the 13-arg kernel (prepare("PPiPPiPPiPiii")). The host array
is not indexed anywhere on the CUDA path (only encoded_X[-1] and the splat are used), so commit 2
drops it; only the device copy active_output_gpu is a kernel argument. Both commits are needed for
the GPU autoencoder to run at all.

Verification (RTX 3090)

Small synthetic-corpus run — 400 docs × 40 binary features (co-occurrence "topics"), 20 clauses,
T=15, s=5, accumulation=10, 500 examples, seed 42; mean clause_precision/clause_recall over
4 output targets, CPU vs GPU:

  • main, platform='GPU'TypeError: ... unexpected keyword argument 'target_true_p'.

  • this branch:

    fit mean clause precision recall
    CPU 0.14 s 1.000 0.358
    GPU 2.75 s 1.000 0.436

    The GPU autoencoder now runs and tracks the CPU reference (non-degenerate, same precision,
    comparable recall; the exact GPU recall varies run-to-run because the device curand generator is
    created unseeded — a pre-existing issue). GPU wall-clock is slower on this tiny workload because
    produce_autoencoder_example launches a full grid but works on a single thread — addressed in a
    follow-up PR that parallelises the kernel's init.

drmckay added 2 commits July 1, 2026 15:49
The CUDA method and dispatcher lacked the target_true_p parameter that
TMAutoEncoder.fit() passes, so GPU fit() raised TypeError; the body also called a
non-existent .choice on the pycuda RNG. Draw target_value host-side biased by
target_true_p, matching the CPU path.
prepare_X_autoencoder left the host active_output array in the tuple that
produce_autoencoder_example splats into the launch, giving 14 args for a 13-arg kernel.
Only active_output_gpu is a kernel argument; drop the host copy.
@drmckay drmckay changed the title git@github.com:drmckay/tmu.gitgit@github.com:drmckay/tmu.git GPU autoencoder ignores target_true_p Jul 1, 2026
@drmckay drmckay changed the title GPU autoencoder ignores target_true_p GPU autoencoder ignores target_true_p and CUDA kernel leaves padding bits uninitialised Jul 1, 2026
@drmckay drmckay marked this pull request as ready for review July 1, 2026 20:49
@drmckay drmckay changed the title GPU autoencoder ignores target_true_p and CUDA kernel leaves padding bits uninitialised GPU autoencoder ignores target_true_p Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant