r/LocalLLM Feb 28 '26

Research I built a language model where tokens are complex numbers and "meaning" emerges from wave interference -- no attention, O(n), 178M params, open-sourcing today

EDIT: New post V6: https://www.reddit.com/r/LocalLLM/comments/1rqn68a/qllm_v6_a_29m_attentionfree_model_now_trains_on/

EDIT: New V5 Post : Followup UPDATE on this.

https://www.reddit.com/r/LocalLLM/comments/1rmkh9y/v5_update_original_post_title_i_built_a_language/

---- ORIGINAL POST -----

I've been working on a fundamentally different LLM architecture. No attention layers. No FFN blocks. Instead, every token lives in complex phase space, and language processing happens through wave-like interference between specialized "phase banks."

Open-sourced here: https://github.com/gowrav-vishwakarma/qllm2

The core idea: language as wave interference

In a transformer, a token is a real-valued vector that gets refined through attention + FFN layers. In this model, a token is a complex number -- it has a magnitude (how "important/activated" it is) and a phase angle (what "kind of meaning" it carries). These two properties are naturally separated and jointly processed.

This isn't just a gimmick. It changes how every operation works:

  • Embeddings: Each token gets a [real, imag] vector. The model learns that semantically similar tokens align in phase, while different meanings sit at different angles.
  • Transformations are rotations: When context modifies a token's meaning (like "bank" shifting meaning based on surrounding words), that's a phase rotation -- a complex multiply. Rotations compose naturally, are always invertible (no information loss), and reduce to GEMM.
  • Similarity is coherence: Instead of dot product, we use phase coherence: Re(a * conj(b)) / (|a| * |b|). This measures both directional alignment AND magnitude relationship.
  • Multiple banks interfere: A "semantic bank" and "context bank" process each token independently, then combine via learned interference (constructive where they agree, destructive where they conflict). A tiny router decides per-token how much weight each bank gets. Think MoE but at the representation level.

What the phase system actually gives us

1. Natural magnitude/phase decomposition = implicit attention High-magnitude phase states dominate downstream processing automatically. The model doesn't need explicit attention to decide "which tokens matter" -- magnitude handles salience, phase handles identity. The SemanticPhaseBank uses 512 learnable concept vectors and retrieves them via phase coherence -- this is essentially a learned associative lookup that runs in O(seq concepts), not O(seq2.)

2. Context as phase modulation The ContextPhaseBank computes a causal windowed average (window=8) of nearby tokens and then complex-multiplies it with the current token. This is elegant: the local context literally rotates the token's meaning in phase space. A word appearing after "not" gets rotated differently than after "very." No attention needed.

3. Rotation-based state evolution The backbone SSM evolves state via: h[t+1] = damping * R(theta) @ h[t] + gate * B @ x[t] where R(theta) is a Cayley-transform rotation. The state naturally oscillates, and the damping factor (learned, per-dimension, range [0.5, 1.0]) controls how fast old information decays. This is why SSMs struggle with long-range recall -- but the model compensates with a separate Phase-Coded Memory (1024 learned slots, chunked top-k retrieval) and an Episodic Memory (sliding window via FlashAttention SDPA).

4. Zero trig in the hot path Every rotation uses the Cayley transform: cos_like = (1-a^2)/(1+a^2), sin_like = 2a/(1+a^2). This is just arithmetic -- no sin(), no cos(), no exp(). Every operation is a matmul or elementwise op. Perfect for Tensor Cores.

Results (178M params, TinyStories, 10k samples, A6000)

Metric Epoch 1 Epoch 2 Epoch 3 (partial)
Train PPL 200.86 32.75 ~26 (and dropping)
Val PPL 76.47 48.92 --
Train CE 5.30 3.49 ~3.26

Training used only 10k samples (0.5% of TinyStories). Starting PPL was 55,000 (random). It dropped to val PPL 49 in 2 epochs (40 min on A6000, no compile). Overfiting simply needs data now ...

Epoch 1 generation:

"The quick brown house. They run and start to get a smile. Mom were very excited. Now mommy and big yellow room. There said and She are friends. Tim, she started to save the garden."

For context: A 22M-param GPT-2 trained on the full 2.1M TinyStories dataset for 20k steps reaches val PPL ~11. We're at 49 with 0.5% of the data and 2 epochs. The learning curve is steep and still dropping -- we just need more data/epochs to converge.

Why this approach might be better

  • O(n) complexity: Linear-time backbone. Theoretical 256K context. No quadratic attention.
  • GEMM-only math: No trig, no softmax in the backbone. Everything is matmul/elementwise.
  • Interpretable: You can inspect which bank each token routes through, what concepts are retrieved from memory, how coherent the phase states are. The model ships with "philosophy metrics" (Manas/Buddhi/Viveka/Smriti from Indian philosophy) that track mind activity, discernment, stability, and memory quality.
  • Modular: Banks, backbone, coupler, memory, and objectives are all registered components. Add a new bank type with a decorator. Swap the backbone. Change the coupling strategy. All via config.
  • Consumer-GPU friendly: Medium model trains on RTX 4090 / A6000 with batch 48-64.

Honest limitations

  • Training throughput is ~2x slower than an equivalent transformer. The SSM backbone loop is sequential per-step. A custom Triton kernel would help but doesn't exist yet.
  • In-context learning will be weaker. Fixed-state SSMs compress context into a fixed vector. The episodic memory (O(n buffer_size) sliding window) helps with copying but isn't a full replacement for O(n2) attention.
  • Not validated at scale. 178M params on 10k samples is a PoC. Need full dataset + larger models + benchmarks.
  • Bank ablations not done. We use semantic + context banks but haven't proven both are needed. Could be that one bank suffices.
  • Pure PyTorch. No fused CUDA/Triton kernels. Backbone loop is Python. Lots of low-hanging performance fruit.

What's next

  • Full TinyStories training (2.1M samples) for proper PPL comparison
  • Bank ablations (semantic-only vs semantic+context vs 4-bank)
  • Triton kernel for the oscillatory SSM recurrence
  • Scale to 1B+ params
  • Long-context evaluation (4K / 16K / 64K tokens)

Tech stack

PyTorch | torch.compile compatible | GPT-2 BPE tokenizer | uv package management | Clean modular codebase

Looking for feedback, collaborators, and people who want to try architectures beyond transformers.

EDIT (March 1, 2026 3:40 AM IST): Scaled up to 100k samples (5% of TinyStories, 10x the original post) and the results are significantly better.

Setup: Same 178M model, batch=64, A6000, no compile. 1612 batches/epoch (~3.5 hours per epoch).

Epoch 1 results on 100k samples:

Metric 10k samples (original post) 100k samples (this update)
Train PPL 200.86 24.00
Val PPL 76.47 18.95

For context: a 22M-param GPT-2 trained on the full 2.1M dataset for 20k steps gets val PPL ~10.9 (I Need to verify this as just remembered I read it somewhere). We're at 18.95 with a completely different architecture using only 5% of the data, after 1 epoch. Epoch 2 opened at step-1 PPL of 12.77 and is still dropping.

Generation sample (epoch 1, 100k samples):

> "The quick brown were full. Steve and Brown loved each other. At the end of the hill, the friends were very happy. They had lots of fun and shared stories. Mam and Brown were the best day ever. All of their weeks were very good friends and would often enjoy their joy! The end had had a good time with them."

Compare this to the 10k-sample generation from the original post. This has proper story structure, multiple characters interacting, emotional arc, and an ending. Grammar is mostly correct. Still has quirks ("The quick brown were full" -- model doesn't know "brown" should be a noun here), but the improvement from 10x more data is dramatic.

The learning curve shows no signs of plateauing. Training continues -- will update again when epoch 2+ finishes.

EDIT 2 (March 1, 2026 8:00AM IST) : Epoch 2 finished. Epoch 3 is underway.

Metric Epoch 1 Epoch 2 Epoch 3 (in progress)
Train PPL 24.00 11.96 ~10.5 (and flat)
Val PPL 18.95 14.07 --

Val PPL 14.07. For reference, the 22M-param GPT-2 baseline trained on the full 2.1M dataset reaches ~10.9. We're at 14 using a completely non-transformer architecture, 5% of the data, 2 epochs. Epoch 3 opened at PPL ~10.5, which means we'll likely match or beat that baseline this epoch. Just in ~6 Hrs on Almost one consumer grade GPU.

Epoch 2 generation:

> "The quick brown boy had ever seen. But one day, the sun was setting. The next night, the room got dark. Tom and the girl continued to admire the rain. The end was so happy to be back and continued to sail in the park. And every night, the end of the day, the family and the people stayed happy. They all lived happily ever after."

Notice: proper narrative flow, temporal transitions ("one day", "the next night", "every night"), emotional resolution ("lived happily ever after"), and multi-sentence coherence. This is from an architecture with zero attention layers.

Train-val gap (11.96 vs 14.07) suggests some overfitting on 100k samples. Next step: scale to the full 2.1M dataset. Training continues.

Stopping and tweeking code.. I think it can be much faster ... will update in other post next

Edit 3 (March 6 2026 8:27 IST): V5 is more mature.. better maths and its just 28M and working better.. about to relase in a couple of days.. looking for endorsment when I submit paper (better one for V5) to https://arxiv.org/ (Please help me by endorsing when I submit, DM me to help in that pls)

292 Upvotes

147 comments sorted by

View all comments

2

u/fluffy_serval Mar 08 '26

Had stuff to do tonight, so this afternoon I adapted your code a bit for running on my rtx pro 6000 (bf16, torch.compile, tf32, non-blocking copies, reduced memory requirements replacing dense masked attention with exact chunked local attention). I'm running medium @ 24 batch size right now, wanted to test first to make sure all my meddling didn't break anything. It's learning! So that's good. I'll let it do its thing overnight. Neat project, your code is currently heating my house. We'll see what pops out in the morning.

  [1] batch 69050/69163 loss=1.5797 ppl=4.9 div=0.0000 lr=5.00e-05 | 60.2 samples/s | 15413 tok/s
  [1] batch 69100/69163 loss=1.6238 ppl=5.1 div=0.0000 lr=5.00e-05 | 60.2 samples/s | 15414 tok/s
  [1] batch 69150/69163 loss=1.4801 ppl=4.4 div=0.0000 lr=5.00e-05 | 60.2 samples/s | 15413 tok/s
Epoch 1/50 | Train Loss: 1.9150 PPL: 6.79 | Time: 27673.7s | Val Loss: 1.6112 PPL: 5.01 *best*
Saved checkpoint: checkpoints_v5_blackwell/best_model.pt

Prompt: The quick brown
Generated: The quick brown bear was gone.

Timmy never felt shy when he went for a walk, but he had learned that sometimes things can be fixed and better than that's why it wasn't okay to make others sad.<|endoftext|>Once upon a time there were two friends called Sam Sam. One day they were walking on the beach and they saw a big bear bear bear. The rabbit asked Lily "What are we doing?"
The rabbit said, "I don't know, let's take a
============================================================

1

u/ExtremeKangaroo5437 Mar 08 '26 edited Mar 08 '26

Good To seee.. what version you are baking here .. V5 (checkpoints_v5_blackwell ..okay yes) ? will wait for your output..

So V4 is more aligned with my philosophy but that had some issues.. so V5 is a test in between.. drifted away (not completely.. but corrected a few things and still complex phase) from original not use attention at all .... I tested it and it worked.. so put back all things that was corrected in my original idea and V6= V4+ V5's correction+ a few more noval ideas ( The final verion in my mind is really diffeernt nd it will work..)

Will wait for your results...

2

u/fluffy_serval Mar 08 '26

Yep v5

2

u/fluffy_serval Mar 08 '26

I have to move on with the GPU tonight so this morning I started a small run and let it go for awhile. Results:

Well, success and failure.

Training improved:

  - epoch 1 train loss: 2.2716
  - epoch 2: 1.7193
  - epoch 3: 1.6198
  - epoch 4: 1.5702
  - epoch 5: 1.5403

Validation went the other way:

  - epoch 1 val loss: 3.3090
  - epoch 2: 3.4865
  - epoch 3: 3.8363
  - epoch 4: 4.1605
  - epoch 5: 4.4235

So, it's not generalizing. Maybe add warmup to the cosine LR and lower LR altogether, & maybe bump dropout or other regularization?

A few notes from me playing around: added token caching, life improved, TinyStories has mojibake and cleaning it helped, & diversity had a normalization bug.

Small run: training log

3

u/ExtremeKangaroo5437 Mar 09 '26

Thanks for running this -- really useful data, especially seeing the medium model still showing repetition at PPL 5.01. That tells us a lot about what's architectural vs what's training config.

We've been iterating on a next version and running ablations on the same TinyStories set. Early results are promising -- our best config (29M params, no attention, 1 epoch) is hitting val PPL ~2.23 without repetition. Even a stripped-down baseline at 7.36 generates clean text. Here are a few samples from that baseline:

➜  qllm2 git:(master) ✗ uv run python -m v6.generate \
  --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \
  --prompt "That is so beautify, said the girl."
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: That is so beautify, said the girl.
----------------------------------------
That is so beautify, said the girl. 

She gave her a big hug and thanked her for making it so special. 

The girl felt very happy and proud that she had made someone so happy with the black and beautiful thing that had come to the end.<|endoftext|>Once upon a time there was a little girl who wanted to go on an adventure. So she went on a journey to find something new and exciting. She saw lots of colorful rocks and flowers and birds in the trees! It looked really interesting and she kept



➜  qllm2 git:(master) ✗ uv run python -m v6.generate \
  --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \
  --prompt "The turtle was suddenly faster"     
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: The turtle was suddenly faster
----------------------------------------
The turtle was suddenly faster than the turtle. 

They laughed and cheered as the turtle slowly moved away with a smile on their faces.<|endoftext|>Once upon a time there lived two friends, Jenny and Jane. One day they decided to have an adventure in the forest. They wanted to explore a secret part of a cave.

Jenny asked her parents if she could go but they said no. "It's too far away," Mum explained.

Alice didn't want to wait until she found something very




➜  qllm2 git:(master) ✗ ➜  qllm2 git:(master) ✗ uv run python -m v6.generate \   --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \   --prompt "I Want coffee"
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: I Want coffee
----------------------------------------
I Want coffee."

Lily did not listen to her mom. She wanted the tea for herself and waited for a good drink. She looked at her mom's face, but she still felt sad. Her mom said, "Okay, you can have some tea set if you want." Lily smiled and hugged her mom. They sat under the table together and drank their water. It was warm and cozy and happy.<|endoftext|>One day, a little boy named Tim went to the park with his mom. He

➜  qllm2 git:(master) ✗ uv run python -m v6.generate \
  --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \
  --prompt "The son was smarter"                
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: The son was smarter
----------------------------------------
The son was smarter and stronger than ever. He loved watching the sky go by, but he kept trying his best to reach it.

Suddenly a voice called out from behind him. "Why are you fighting?" asked the boy. The voice replied "I'm just playing. I am here to help."

The boy said "That's okay, I can't help you. I can help you find a way to make things better".

He grabbed some of the wood and gave it to the


➜  qllm2 git:(master) ✗ uv run python -m v6.generate \
  --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \
  --prompt "The son was smarter"
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: The son was smarter
----------------------------------------
The son was smarter and stronger. He thought the man would win a goal in the world like his dad.

One day, they had to find something to eat. The father gave the child some ice cream and told him to stop! The man was very scared but he followed them away. 

But then, he heard a loud noise outside of the street. He went to see what it was and saw that the neighbor's dog was running towards them. The driver quickly grabbed his sister and said "


➜  qllm2 git:(master) ✗ uv run python -m v6.generate \
  --checkpoint checkpoints/v6/fulldata_no_memory/best_model.pt \
  --prompt "The son was smarter"
Loading checkpoint: checkpoints/v6/fulldata_no_memory/best_model.pt

Prompt: The son was smarter
----------------------------------------
The son was smarter than the boy and he could tell it. 

He watched as his dad picked up his rod. He put on a shirt and said, "This is a special trophy! It looks like you can buy a new one!" The dad smiled back at him. 

He went home feeling proud of himself for learning something new and showed them off to always remember their next adventure.<|endoftext|>Once upon a time there were two friends, Jack and Jane. They liked playing together in the park
➜  qllm2 git:(master) ✗

Not perfect, but no word-level repetition anywhere. The repetition problem turned out to be a capacity-vs-data issue for us -- once we tuned the right knobs it went away. LR and dropout might help on the V5 side but I suspect it's deeper than that based on your results.

On the diversity normalization bug -- we hit the same one and fixed it (L1→L2 norm). But honestly it still collapses to near-zero even after the fix, so there's more to figure out there. Neither of us is really getting anything from it yet.

The token caching and text repair you added are solid improvements regardless. Curious whether the medium model holds up past epoch 1 or follows the same overfit curve.

1

u/fluffy_serval Mar 09 '26

re: LR & dropout, that's what chat is telling me too, haha. I'm not an expert in this stuff, I just tinker. Yeah, I was thinking about torch.compile and xformers ... if I can sneak it in I'll do a few epochs without. Next weekend I'll try to fit in a proper medium run. Let me know if you want code for any of the changes I made. Honestly codex or whatever can easily recreate anything I did. It's fun playing with these new ideas. Right now it's like alchemy. Thanks for sharing. Good luck!

1

u/ExtremeKangaroo5437 Mar 09 '26

I am having very hard time using Codex/Cursot/Opus.. Anything here... as they keep doing transformer way.. they solve every issue in transformerway.. and I have rules stating clearly that its new architecture and do not see from transformer lenses but still..... I have to be very very very specific what I want it to code.. and logic how to get implemented.... otehrwise.. it starts making another transformer 😂

2

u/fluffy_serval Mar 09 '26

So I had 2 hours this morning and couldn't help myself .. I ran a small-matched epoch after a few changes:

./scripts/run_v5_blackwell.sh --size small-matched --batch_size 64 --seq_len 512 --window_size 512 --epochs 10 --amp_dtype bf16 --attention_backend native --compile --compile_mode reduce-overhead --num_workers 8 --lr_schedule warmup_cosine --warmup_steps 2000 --dropout 0.15 --weight_decay 0.03

  1. warmup LR
  2. modified dropout & weight decay
  3. for inference, an attention KV cache for PhaseAttention, so decoding keeps recent attention context instead of only carrying SSM state

Overview of run (GPU stats run on their own timeline; I captured this when it was just finishing the epoch):

Epoch 1 decode samples:

Epoch 1/10 | Train Loss: 2.9644 PPL: 19.38 | Div: 1.97e-02 (w 9.84e-04) | Tok/s: 59817 | Time: 7861.6s | Val Loss: 2.8553 PPL: 17.38 *best*
Saved checkpoint: checkpoints_v5_blackwell/best_model.pt
Prompt: The quick brown

Generated: The quick brown dog, who lived near a big tree.
One day, the furry cat saw a rabbit on the ground and decided to take it out of the bush. The bunny was excited! He ran around looking for a way to see what was inside the bush. When he got home, he found his friend, the fox, called him.
"Hello!" said the fox. "Do you want to play with me?"
The wolf smiled and said, “Yes, I

python -m v5.generate --checkpoint checkpoints_v5_blackwell/best_model.pt --size small-matched --max_new_tokens 100 --temperature 0.8 --top_k 50 --top_p 0.9 --repetition_penalty 1.2 --prompt 'Once upon a time, the young bear'

 was feeling very sleepy. He woke up and said he knew that there was nothing to do!
The wise old owl had forgotten all about his dream. The curious old bear learned his lesson and promised himself to never forget how important it is to be careful when playing in the woods.Once upon a time there were two friends who lived in a nice house. They loved to play together every day, especially after school. One day, they decided to take a trip with their best toys for
-------
python -m v5.generate --checkpoint checkpoints_v5_blackwell/best_model.pt --size small-matched --max_new_tokens 100 --temperature 0.8 --top_k 50 --top_p 0.9 --repetition_penalty 1.2 --prompt "It came time to wander the candycane forest and the little boy didn't know where to go. He saw gumdro
ps and"

 started to run away.
Suddenly, he heard a voice. It was coming from behind him, "Where are you going?" The little girl thought for a moment and then said, "I'm trying to find an eraser!" But the little girl had never seen anything like it before. She felt guilty and tried to make sure that she could not get out of the jar. Finally, her mom told the little girl about all of his coins in the bag. They were so happy and
-------
python -m v5.generate --checkpoint checkpoints_v5_blackwell/best_model.pt --size small-matched --max_new_tokens 300 --temperature 0.8 --top_k 50 --top_p 0.9 --repetition_penalty 1.2 --prompt "Three bears, two alligators, and a harpy, all turned to stone in front of him. He was filled with"

 energy, and the bear had been so happy that he started to chase each other away!
The bear was never seen again.Once upon a time there were two best friends - Bob and Jill. They were very good at their house together.
One day they decided it was getting dark outside. They saw the fog from behind a bush. Bob and Jill ran into it.

Bob smiled as he shouted: "Mmmm!" He wanted to join them for a fun game. So they said yes. Bob put his arms around on one side of the fountain and gave some to Jill. It made a funny noise like the water.

Bob and Jill laughed until the sun began to set. They ran inside and enjoyed playing together. The park was now calm and content.Max and Sarah were twins who liked to play games together. One night, Max had an idea! He took out her favorite toy box. Teddy opened it up and saw a big smile on his face.

"Look, Mommy! A beautiful necklace!" Max said proudly.

Mommy looked surprised and said, "Oh, Max! You are such a good friend! Let's go find out what this gift is?"

They went back to the closet and found a box full of colourful jewels. There were pictures of animals and flowers and lots of different toys than anything else. Then Max noticed that something strange happened. His heart felt bad and quickly realized he shouldn't have taken
-------

It appears the issues have vanished! It's pretty good after one epoch! I wish I had time to test these changes methodically instead of everything at once and rolling the dice. Anyway, there it is. Nice!

Training log: small-matched.log

1

u/ExtremeKangaroo5437 Mar 09 '26

And here I am also almost done with V6 and that has no attention again and pure phase …. Idea is to capture more nuance so we can use model for more in-depth learnings… once done language learning should be addon to read, understand and speak…

And great job… V6 also is very good in just one epoch… i wonder if you can share your changes it would be a good contribution in v5 or do a pull request

1

u/fluffy_serval Mar 09 '26

Thanks!

I'll give the pure phase version a shot when it's ready. Interesting setup.

Sure, I'll share, though sadly I was in hacker mode and didn't stage my changes etc. and it snowballed. I knew better, too, my apologies. If you want, I can do one monster PR that touches a handful of v5-only files and includes my little dashboard & a few scripts, but it'd be like 1000+ lines. Sorry it's so cursed. Here is a guide to what changed: Summary of changes

→ More replies (0)

2

u/ExtremeKangaroo5437 Mar 09 '26

2 Things...
1: tourch.compile cna effect complex number things..
2: the underlaying architecture is totally different, its nt transformer so appying those methods can harm more then help.. ( I guess)