diff --git a/.github/workflows/config/spelling_allowlist.txt b/.github/workflows/config/spelling_allowlist.txt index 0b411b17bd0..6575f1474b7 100644 --- a/.github/workflows/config/spelling_allowlist.txt +++ b/.github/workflows/config/spelling_allowlist.txt @@ -8,6 +8,7 @@ AlmaLinux Anyon Asynchronous BFGS +BPE Bloch Braket CLA @@ -36,6 +37,8 @@ Fock Fortran Fourier GHCR +GPT +GPTs GPU GPUDirect GPUs @@ -67,6 +70,7 @@ MPI MPICH MPS MSB +Macbooks Mandel Max-Cut MyST @@ -79,11 +83,13 @@ OPX OQC ORCA OpenACC +OpenAI OpenMP OpenMPI OpenQASM OpenSSL OpenSUSE +OpenWebText Ou POSIX PSIRT @@ -131,9 +137,11 @@ UCCSD VQE Vazirani WSL +XA Zener accessor acknowledgement +acknowledgements adaptor adaptors adjoint @@ -172,6 +180,7 @@ comparator comparators composability composable +config constructible controlled coprocessing @@ -188,6 +197,8 @@ cuQuantum cuTensor cudaq dataflow +dataset +datasets ddots deallocate deallocated @@ -233,15 +244,20 @@ extensibility extremal fermionic fermioniq +finetune +finetuning fmt fourier frontend frontends +gpt grovers hadamard hamiltonian heisenberg homogenous +huggingface +hyperparameters iff increment incrementing @@ -267,17 +283,21 @@ lookup lookups lossy lvalue +macbook makefiles merchantability +minGPT mps multithreaded mutex namespace namespaces +nanoGPT natively normalization nullary observables +openwebtext optimizer optimizers pagerank @@ -292,10 +312,13 @@ photonics precompute precomputed prepend +preprocess preprocessor +pretrained probability programmatically pybind +pytorch qaoa qed quantize @@ -308,6 +331,7 @@ qumode qumodes reStructuredText reconfigurable +repro runtime runtimes rvalue @@ -315,6 +339,7 @@ scalability scalable selectable sexualized +shakespeare shifter shifters shors @@ -346,6 +371,7 @@ superpositions symplectic tablegen templated +todos toolchain toolchains toolset diff --git a/.gitmodules b/.gitmodules index 622993890c6..969107a43f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -48,3 +48,6 @@ [submodule "tpls/Stim"] path = tpls/Stim url = https://github.com/quantumlib/Stim +[submodule "docs/sphinx/applications/python/qaoa_gpt_src/FEATHER"] + path = docs/sphinx/applications/python/qaoa_gpt_src/FEATHER + url = https://github.com/benedekrozemberczki/FEATHER.git diff --git a/docs/sphinx/_static/app_title_images/qaoa_gpt_toc.png b/docs/sphinx/_static/app_title_images/qaoa_gpt_toc.png new file mode 100644 index 00000000000..5b6189fa772 Binary files /dev/null and b/docs/sphinx/_static/app_title_images/qaoa_gpt_toc.png differ diff --git a/docs/sphinx/applications/python/images/inference-qaoa-gpt.png b/docs/sphinx/applications/python/images/inference-qaoa-gpt.png new file mode 100644 index 00000000000..3911551e36d Binary files /dev/null and b/docs/sphinx/applications/python/images/inference-qaoa-gpt.png differ diff --git a/docs/sphinx/applications/python/images/qaoa-gpt-diag.png b/docs/sphinx/applications/python/images/qaoa-gpt-diag.png new file mode 100644 index 00000000000..427ce63d8a6 Binary files /dev/null and b/docs/sphinx/applications/python/images/qaoa-gpt-diag.png differ diff --git a/docs/sphinx/applications/python/nanoGPT/.gitattributes b/docs/sphinx/applications/python/nanoGPT/.gitattributes new file mode 100644 index 00000000000..a6096583cf0 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/.gitattributes @@ -0,0 +1,4 @@ +# Override jupyter in Github language stats for more accurate estimate of repo code languages +# reference: https://github.com/github/linguist/blob/master/docs/overrides.md#generated-code +*.ipynb linguist-generated +out-save_dir_3k.zip filter=lfs diff=lfs merge=lfs -text diff --git a/docs/sphinx/applications/python/nanoGPT/.gitignore b/docs/sphinx/applications/python/nanoGPT/.gitignore new file mode 100644 index 00000000000..f30deccd1ab --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +.idea +.ipynb_checkpoints/ +.vscode +__pycache__/ +*.bin +*.pkl +*.pt +*.pyc +input.txt +env/ +venv/ +*.npy +*.json \ No newline at end of file diff --git a/docs/sphinx/applications/python/nanoGPT/LICENSE b/docs/sphinx/applications/python/nanoGPT/LICENSE new file mode 100644 index 00000000000..329db5e3331 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Andrej Karpathy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/sphinx/applications/python/nanoGPT/README.md b/docs/sphinx/applications/python/nanoGPT/README.md new file mode 100644 index 00000000000..7138213c54c --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/README.md @@ -0,0 +1,338 @@ +# nanoGPT + +![nanoGPT](assets/nanogpt.jpg) + +The simplest, fastest repository for training/finetuning medium-sized GPTs. +It is a rewrite of [minGPT](https://github.com/karpathy/minGPT) that +prioritizes teeth over education. Still under active development, but currently +the file `train.py` reproduces GPT-2 (124M) on OpenWebText, running on a +single 8XA100 40GB node in about 4 days of training. The code itself is plain +and readable: `train.py` is a ~300-line boilerplate training loop and +`model.py` a ~300-line GPT model definition, which can optionally load the +GPT-2 weights from OpenAI. That's it. + +![`repro124m`](assets/gpt2_124M_loss.png) + +Because the code is so simple, it is very easy to hack to your needs, train new +models from scratch, or finetune pretrained checkpoints (e.g. biggest one +currently available as a starting point would be the GPT-2 1.3B model from +OpenAI). + +## install + +```sh +pip install torch numpy transformers datasets tiktoken wandb tqdm +``` + +Dependencies: + +- [`pytorch`](https://pytorch.org) <3 +- [`numpy`](https://numpy.org/install/) <3 +- `transformers` for huggingface transformers <3 (to load GPT-2 checkpoints) +- `datasets` for huggingface datasets <3 (if you want to download + preprocess OpenWebText) +- `tiktoken` for OpenAI's fast BPE code <3 +- `wandb` for optional logging <3 +- `tqdm` for progress bars <3 + +## quick start + +If you are not a deep learning professional and you just want to feel the magic +and get your feet wet, the fastest way to get started is to train a +character-level GPT on the works of Shakespeare. First, we download it as a +single (1MB) file and turn it from raw text into one large stream of integers: + +```sh +python data/shakespeare_char/prepare.py +``` + +This creates a `train.bin` and `val.bin` in that data directory. Now it is time +to train your GPT. The size of it very much depends on the computational +resources of your system: + +**I have a GPU**. Great, we can quickly train a baby GPT with the settings +provided in the [config/train_shakespeare_char.py] +(config/train_shakespeare_char.py) config file: + +```sh +python train.py config/train_shakespeare_char.py +``` + +If you peek inside it, you'll see that we're training a GPT with a context size + of up to 256 characters, 384 feature channels, and it is a 6-layer Transformer + with 6 heads in each layer. On one A100 GPU this training run takes about 3 +minutes and the best validation loss is 1.4697. Based on the configuration, the + model checkpoints are being written into the `--out_dir` directory + `out-shakespeare-char`. So once the training finishes we can sample from the + best model by pointing the sampling script at this directory: + +```sh +python sample.py --out_dir=out-shakespeare-char +``` + +This generates a few samples, for example: + +```sh +ANGELO: +And cowards it be strawn to my bed, +And thrust the gates of my threats, +Because he that ale away, and hang'd +An one with him. + +DUKE VINCENTIO: +I thank your eyes against it. + +DUKE VINCENTIO: +Then will answer him to save the malm: +And what have you tyrannous shall do this? + +DUKE VINCENTIO: +If you have done evils of all disposition +To end his power, the day of thrust for a common men +That I leave, to fight with over-liking +Hasting in a roseman. +``` + +`lol` `¯\_(ツ)_/¯`. Not bad for a character-level model after 3 minutes of +training on a GPU. Better results are quite likely obtainable by instead +finetuning a pretrained GPT-2 model on this `dataset` (see finetuning section +later). + +**I only have a macbook** (or other cheap computer). No worries, we can still +train a GPT but we want to dial things down a notch. I recommend getting the +bleeding edge PyTorch nightly ([select it here] +() when installing) as it is currently +quite likely to make your code more efficient. But even without it, a simple +train run could look as follows: +|:spellcheck-disable:| + +```sh +python train.py config/train_shakespeare_char.py --device=cpu --compile=False --eval_iters=20 --log_interval=1 --block_size=64 --batch_size=12 --n_layer=4 --n_head=4 --n_embd=128 --max_iters=2000 --lr_decay_iters=2000 --dropout=0.0 +``` + +|:spellcheck-enable:| +Here, since we are running on CPU instead of GPU we must set both +`--device=cpu` and also turn off PyTorch 2.0 compile with `--compile=False`. +Then when we evaluate we get a bit more noisy but faster estimate +(`--eval_iters=20`, down from 200), our context size is only 64 characters +instead of 256, and the batch size only 12 examples per iteration, not 64. +We'll also use a much smaller Transformer (4 layers, 4 heads, 128 embedding +size), and decrease the number of iterations to 2000 (and correspondingly +usually decay the learning rate to around `max_iters` with `--lr_decay_iters`). + Because our network is so small we also ease down on regularization + (`--dropout=0.0`). This still runs in about ~3 minutes, but gets us a loss of + only 1.88 and therefore also worse samples, but it's still good fun: + +```sh +python sample.py --out_dir=out-shakespeare-char --device=cpu +``` + +Generates samples like this: + +```sh +GLEORKEN VINGHARD III: +Whell's the couse, the came light gacks, +And the for mought you in Aut fries the not high shee +bot thou the sought bechive in that to doth groan you, +No relving thee post mose the wear +``` + +Not bad for ~3 minutes on a CPU, for a hint of the right character gestalt. +If you're willing to wait longer, feel free to tune the hyperparameters, +increase the size of the network, the context length (`--block_size`), the +length of training, etc. + +Finally, on Apple Silicon Macbooks and with a recent PyTorch version make sure +to add `--device=mps` (short for "`Metal Performance Shaders`"); PyTorch then +uses the on-chip GPU that can *significantly* accelerate training (2-3X) and +allow you to use larger networks. +See [Issue 28](https://github.com/karpathy/nanoGPT/issues/28) for more. + +## reproducing GPT-2 + +A more serious deep learning professional may be more interested in reproducing + GPT-2 results. So here we go - we first `tokenize` the `dataset`, in this case + the [OpenWebText](https://openwebtext2.readthedocs.io/en/latest/), an open +reproduction of OpenAI's (private) `WebText`: + +```sh +python data/openwebtext/prepare.py +``` + +This downloads and `tokenizes` the +[OpenWebText](https://huggingface.co/datasets/Skylion007/openwebtext) +`dataset`. It will create a `train.bin` and `val.bin` which holds the GPT2 BPE +token ids in one sequence, stored as raw `uint16` bytes. Then we're ready to +kick off training. To reproduce GPT-2 (124M) you'll want at least an 8X A100 +40GB node and run: + +```sh +torchrun --standalone --nproc_per_node=8 train.py config/train_gpt2.py +``` + +This will run for about 4 days using PyTorch Distributed Data Parallel (`DDP`) +and go down to loss of ~2.85. Now, a GPT-2 model just evaluated on `OWT` gets +a val loss of about 3.11, but if you finetune it it will come down to ~2.85 +territory (due to an apparent domain gap), making the two models ~match. + +If you're in a cluster environment and you are blessed with multiple GPU nodes +you can make GPU go `brrrr` e.g. across 2 nodes like: + +```sh +# Run on the first (master) node with example IP 123.456.123.456: +torchrun --nproc_per_node=8 --nnodes=2 --node_rank=0 --master_addr=123.456.123.456 --master_port=1234 train.py +# Run on the worker node: +torchrun --nproc_per_node=8 --nnodes=2 --node_rank=1 --master_addr=123.456.123.456 --master_port=1234 train.py +``` + +It is a good idea to benchmark your interconnect (e.g. `iperf3`). In particular +, if you don't have `Infiniband` then also prepend `NCCL_IB_DISABLE=1` to the +above launches. Your `multinode` training will work, but most likely *crawl*. +By default checkpoints are periodically written to the `--out_dir`. We can +sample from the model by simply `python sample.py`. + +Finally, to train on a single GPU simply run the `python train.py` script. +Have a look at all of its `args`, the script tries to be very readable, +`hackable` and transparent. You'll most likely want to tune a number of those +variables depending on your needs. + +## baselines + +OpenAI GPT-2 checkpoints allow us to get some baselines in place for +openwebtext. We can get the numbers as follows: + +```sh +python train.py config/eval_gpt2.py +python train.py config/eval_gpt2_medium.py +python train.py config/eval_gpt2_large.py +python train.py config/eval_gpt2_xl.py +``` + +and observe the following losses on train and val: + +| model | `params` | train loss | val loss | +| ------| ------ | ---------- | -------- | +| gpt2 | 124M | 3.11 | 3.12 | +| gpt2-medium | 350M | 2.85 | 2.84 | +| gpt2-large | 774M | 2.66 | 2.67 | +| gpt2-`xl` | 1558M | 2.56 | 2.54 | + +However, we have to note that GPT-2 was trained on (closed, never released) +`WebText`, while OpenWebText is just a best-effort open reproduction of this +`dataset`. This means there is a `dataset` domain gap. Indeed, taking the GPT-2 + (124M) checkpoint and finetuning on `OWT` directly for a while reaches loss +down to ~2.85. This then becomes the more appropriate baseline w.r.t. reproduction. + +## finetuning + +Finetuning is no different than training, we just make sure to initialize from +a pretrained model and train with a smaller learning rate. For an example of +how to finetune a GPT on new text go to `data/shakespeare` and run +`prepare.py` to download the tiny shakespeare `dataset` and render it into a +`train.bin` and `val.bin`, using the OpenAI BPE `tokenizer` from GPT-2. +Unlike OpenWebText this will run in seconds. Finetuning can take very little +time, e.g. on a single GPU just a few minutes. Run an example finetuning like: + +```sh +python train.py config/finetune_shakespeare.py +``` + +This will load the config parameter overrides in +`config/finetune_shakespeare.py` (I didn't tune them much though). Basically, +we initialize from a GPT2 checkpoint with `init_from` and train as normal, +except shorter and with a small learning rate. If you're running out of memory +try decreasing the model size (they are +`{'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'}`) or possibly decreasing the +`block_size` (context length). The best checkpoint (lowest validation loss) +will be in the `out_dir` directory, e.g. in `out-shakespeare` by default, per +the config file. You can then run the code in +`sample.py --out_dir=out-shakespeare`: + +```sh +THEODORE: +Thou shalt sell me to the highest bidder: if I die, +I sell thee to the first; if I go mad, +I sell thee to the second; if I +lie, I sell thee to the third; if I slay, +I sell thee to the fourth: so buy or sell, +I tell thee again, thou shalt not sell my +possession. + +JULIET: +And if thou steal, thou shalt not sell thyself. + +THEODORE: +I do not steal; I sell the stolen goods. + +THEODORE: +Thou know'st not what thou sell'st; thou, a woman, +Thou art ever a victim, a thing of no worth: +Thou hast no right, no right, but to be sold. +``` + +Whoa there, GPT, entering some dark place over there. I didn't really tune the +hyperparameters in the config too much, feel free to try! + +## sampling / inference + +Use the script `sample.py` to sample either from pre-trained GPT-2 models +released by OpenAI, or from a model you trained yourself. For example, here is +a way to sample from the largest available `gpt2-xl` model: + +```sh +python sample.py \ + --init_from=gpt2-xl \ + --start="What is the answer to life, the universe, and everything?" \ + --num_samples=5 --max_new_tokens=100 +``` + +If you'd like to sample from a model you trained, use the `--out_dir` to point +the code appropriately. You can also prompt the model with some text from a +file, e.g. ```python sample.py --start=FILE:prompt.txt```. + +## efficiency notes + +For simple model `benchmarking` and profiling, `bench.py` might be useful. +It's identical to what happens in the meat of the training loop of `train.py`, +but omits much of the other complexities. + +Note that the code by default uses +[PyTorch 2.0](https://pytorch.org/get-started/pytorch-2.0/). At the time of +writing (Dec 29, 2022) this makes `torch.compile()` available in the nightly +release. The improvement from the one line of code is noticeable, e.g. cutting +down iteration time from ~250ms / `iter` to 135ms / `iter`. Nice work PyTorch +team! + +## todos + +- Investigate and add `FSDP` instead of `DDP` +- `Eval` zero-shot perplexities on standard `evals` (e.g. LAMBADA? HELM? etc.) +- Finetune the finetuning script, I think the `hyperparams` are not great +- Schedule for linear batch size increase during training +- Incorporate other `embeddings` (rotary, alibi) +- Separate out the `optim` buffers from model `params` in checkpoints I think +- Additional logging around network health (e.g. gradient clip events, magnitudes) +- Few more investigations around better `init` etc. + +## troubleshooting + +Note that by default this `repo` uses PyTorch 2.0 (i.e. `torch.compile`). This +is fairly new and experimental, and not yet available on all platforms +(e.g. Windows). If you're running into related error messages try to disable +this by adding `--compile=False` flag. This will slow down the code but at +least it will run. + +For some context on this repository, GPT, and language modeling it might be +helpful to watch my +[Zero To Hero series](https://karpathy.ai/zero-to-hero.html). Specifically, the + [GPT video](https://www.youtube.com/watch?v=kCc8FmEb1nY) is popular if you + have some prior language modeling context. + +For more questions/discussions feel free to stop by **#nanoGPT** on Discord: + +[#nanoGPTonDiscord](https://discord.gg/3zy8kqD9Cp) + +## acknowledgements + +All nanoGPT experiments are powered by GPUs on +[Lambda labs](https://lambdalabs.com), my favorite Cloud GPU provider. +Thank you Lambda labs for sponsoring nanoGPT! diff --git a/docs/sphinx/applications/python/nanoGPT/assets/gpt2_124M_loss.png b/docs/sphinx/applications/python/nanoGPT/assets/gpt2_124M_loss.png new file mode 100644 index 00000000000..df5aac3c159 Binary files /dev/null and b/docs/sphinx/applications/python/nanoGPT/assets/gpt2_124M_loss.png differ diff --git a/docs/sphinx/applications/python/nanoGPT/assets/nanogpt.jpg b/docs/sphinx/applications/python/nanoGPT/assets/nanogpt.jpg new file mode 100644 index 00000000000..aa5d47dfc2d Binary files /dev/null and b/docs/sphinx/applications/python/nanoGPT/assets/nanogpt.jpg differ diff --git a/docs/sphinx/applications/python/nanoGPT/bench.py b/docs/sphinx/applications/python/nanoGPT/bench.py new file mode 100644 index 00000000000..a5e2bf95901 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/bench.py @@ -0,0 +1,150 @@ +""" +`A much shorter version of train.py for benchmarking` +""" +import os +from contextlib import nullcontext +import numpy as np +import time +import torch +from model import GPTConfig, GPT + +# ----------------------------------------------------------------------------- +batch_size = 12 +block_size = 1024 +bias = False +real_data = True +seed = 1337 +device = 'cuda' # `examples: 'cpu', 'cuda', 'cuda:0', 'cuda:1', etc.` +dtype = 'bfloat16' if torch.cuda.is_available( +) and torch.cuda.is_bf16_supported( +) else 'float16' # `'float32' or 'bfloat16' or 'float16'` +compile = True # use PyTorch 2.0 to compile the model to be faster +profile = False # `use pytorch profiler, or just simple benchmarking?` +exec(open( + 'configurator.py').read()) # overrides from command line or config file +# ----------------------------------------------------------------------------- + +torch.manual_seed(seed) +torch.cuda.manual_seed(seed) +torch.backends.cuda.matmul.allow_tf32 = True # `allow tf32 on matmul` +torch.backends.cudnn.allow_tf32 = True # `allow tf32 on cudnn` +device_type = 'cuda' if 'cuda' in device else 'cpu' # `for later use in torch.autocast` +ptdtype = { + 'float32': torch.float32, + 'bfloat16': torch.bfloat16, + 'float16': torch.float16 +}[dtype] +ctx = nullcontext() if device_type == 'cpu' else torch.amp.autocast( + device_type=device_type, dtype=ptdtype) + +# data loading `init` +if real_data: + dataset = 'openwebtext' + data_dir = os.path.join('data', dataset) + train_data = np.memmap(os.path.join(data_dir, 'train.bin'), + dtype=np.uint16, + mode='r') + + def get_batch(split): + data = train_data # `note ignore split in benchmarking script` + ix = torch.randint(len(data) - block_size, (batch_size,)) + x = torch.stack([ + torch.from_numpy((data[i:i + block_size]).astype(np.int64)) + for i in ix + ]) + y = torch.stack([ + torch.from_numpy((data[i + 1:i + 1 + block_size]).astype(np.int64)) + for i in ix + ]) + x, y = x.pin_memory().to(device, non_blocking=True), y.pin_memory().to( + device, non_blocking=True) + return x, y +else: + # alternatively, if fixed data is desired to not care about data loading + x = torch.randint(50304, (batch_size, block_size), device=device) + y = torch.randint(50304, (batch_size, block_size), device=device) + get_batch = lambda split: (x, y) + +# model `init` +gptconf = GPTConfig( + block_size=block_size, # how far back does the model look? i.e. context size + n_layer=12, + n_head=12, + n_embd=768, # size of the model + dropout=0, # for determinism + bias=bias, +) +model = GPT(gptconf) +model.to(device) + +optimizer = model.configure_optimizers(weight_decay=1e-2, + learning_rate=1e-4, + betas=(0.9, 0.95), + device_type=device_type) + +if compile: + print("Compiling model...") + model = torch.compile(model) # pytorch 2.0 + +if profile: + # `useful docs on pytorch profiler:` + # - tutorial https://pytorch.org/tutorials/intermediate/tensorboard_profiler_tutorial.html + # - `api` https://pytorch.org/docs/stable/profiler.html#torch.profiler.profile + wait, warmup, active = 5, 5, 5 + num_steps = wait + warmup + active + with torch.profiler.profile( + activities=[ + torch.profiler.ProfilerActivity.CPU, + torch.profiler.ProfilerActivity.CUDA + ], + schedule=torch.profiler.schedule(wait=wait, + warmup=warmup, + active=active, + repeat=1), + on_trace_ready=torch.profiler.tensorboard_trace_handler( + './bench_log'), + record_shapes=False, + profile_memory=False, + with_stack= + False, # incurs an additional overhead, disable if not needed + with_flops=True, + with_modules=False, # `only for torchscript models atm` + ) as prof: + + X, Y = get_batch('train') + for k in range(num_steps): + with ctx: + logits, loss = model(X, Y) + X, Y = get_batch('train') + optimizer.zero_grad(set_to_none=True) + loss.backward() + optimizer.step() + lossf = loss.item() + print(f"{k}/{num_steps} loss: {lossf:.4f}") + + prof.step() # `notify the profiler at end of each step` + +else: + + # simple `benchmarking` + torch.cuda.synchronize() + for stage, num_steps in enumerate([10, 20]): # `burnin`, then benchmark + t0 = time.time() + X, Y = get_batch('train') + for k in range(num_steps): + with ctx: + logits, loss = model(X, Y) + X, Y = get_batch('train') + optimizer.zero_grad(set_to_none=True) + loss.backward() + optimizer.step() + lossf = loss.item() + print(f"{k}/{num_steps} loss: {lossf:.4f}") + torch.cuda.synchronize() + t1 = time.time() + dt = t1 - t0 + mfu = model.estimate_mfu(batch_size * 1 * num_steps, dt) + if stage == 1: + print( + f"time per iteration: {dt/num_steps*1000:.4f}ms, MFU: {mfu*100:.2f}%" + ) diff --git a/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2.py b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2.py new file mode 100644 index 00000000000..c9703bdc692 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2.py @@ -0,0 +1,8 @@ +# evaluate the base gpt2 +# `n_layer=12, n_head=12, n_embd=768` +# 124M parameters +batch_size = 8 +eval_iters = 500 # use more iterations to get good estimate +eval_only = True +wandb_log = False +init_from = 'gpt2' diff --git a/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_large.py b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_large.py new file mode 100644 index 00000000000..01d1e38977d --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_large.py @@ -0,0 +1,8 @@ +# evaluate the base gpt2 +# `n_layer=36, n_head=20, n_embd=1280` +# 774M parameters +batch_size = 8 +eval_iters = 500 # use more iterations to get good estimate +eval_only = True +wandb_log = False +init_from = 'gpt2-large' diff --git a/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_medium.py b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_medium.py new file mode 100644 index 00000000000..f7fe192b5e0 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_medium.py @@ -0,0 +1,8 @@ +# evaluate the base gpt2 +# `n_layer=24, n_head=16, n_embd=1024` +# 350M parameters +batch_size = 8 +eval_iters = 500 # use more iterations to get good estimate +eval_only = True +wandb_log = False +init_from = 'gpt2-medium' diff --git a/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_xl.py b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_xl.py new file mode 100644 index 00000000000..9c9ae6c10ff --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/eval_gpt2_xl.py @@ -0,0 +1,8 @@ +# evaluate the base gpt2 +# `n_layer=48, n_head=25, n_embd=1600` +# 1558M parameters +batch_size = 8 +eval_iters = 500 # use more iterations to get good estimate +eval_only = True +wandb_log = False +init_from = 'gpt2-xl' diff --git a/docs/sphinx/applications/python/nanoGPT/config/finetune_shakespeare.py b/docs/sphinx/applications/python/nanoGPT/config/finetune_shakespeare.py new file mode 100644 index 00000000000..eb0c3b5b4c1 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/finetune_shakespeare.py @@ -0,0 +1,25 @@ +import time + +out_dir = 'out-shakespeare' +eval_interval = 5 +eval_iters = 40 +wandb_log = False # feel free to turn on +wandb_project = 'shakespeare' +wandb_run_name = 'ft-' + str(time.time()) + +dataset = 'shakespeare' +init_from = 'gpt2-xl' # this is the largest GPT-2 model + +# only save checkpoints if the validation loss improves +always_save_checkpoint = False + +# `the number of examples per iter:` +# `1 batch_size * 32 grad_accum * 1024 tokens = 32,768 tokens/iter` +# `shakespeare has 301,966 tokens, so 1 epoch ~= 9.2 iters` +batch_size = 1 +gradient_accumulation_steps = 32 +max_iters = 20 + +# finetune at constant LR +learning_rate = 3e-5 +decay_lr = False diff --git a/docs/sphinx/applications/python/nanoGPT/config/train_gpt2.py b/docs/sphinx/applications/python/nanoGPT/config/train_gpt2.py new file mode 100644 index 00000000000..726d193fcb2 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/train_gpt2.py @@ -0,0 +1,25 @@ +# config for training GPT-2 (124M) down to very nice loss of ~2.85 on 1 node of 8X A100 40GB +# launch as the following (e.g. in a screen session) and wait ~5 days: +# `$ torchrun --standalone --nproc_per_node=8 train.py config/train_gpt2.py` + +wandb_log = True +wandb_project = 'owt' +wandb_run_name = 'gpt2-124M' + +# these make the total batch size be ~0.5M +# `12 batch size * 1024 block size * 5 gradaccum * 8 GPUs = 491,520` +batch_size = 12 +block_size = 1024 +gradient_accumulation_steps = 5 * 8 + +# this makes total number of tokens be 300B +max_iters = 600000 +lr_decay_iters = 600000 + +# `eval stuff` +eval_interval = 1000 +eval_iters = 200 +log_interval = 10 + +# weight decay +weight_decay = 1e-1 diff --git a/docs/sphinx/applications/python/nanoGPT/config/train_shakespeare_char.py b/docs/sphinx/applications/python/nanoGPT/config/train_shakespeare_char.py new file mode 100644 index 00000000000..5df81de4539 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/config/train_shakespeare_char.py @@ -0,0 +1,37 @@ +# train a miniature character-level shakespeare model +# good for debugging and playing on `macbooks` and such + +out_dir = 'out-shakespeare-char' +eval_interval = 250 # keep frequent because we'll `overfit` +eval_iters = 200 +log_interval = 10 # don't print too too often + +# we expect to `overfit` on this small dataset, so only save when val improves +always_save_checkpoint = False + +wandb_log = False # override via command line if you like +wandb_project = 'shakespeare-char' +wandb_run_name = 'mini-gpt' + +dataset = 'shakespeare_char' +gradient_accumulation_steps = 1 +batch_size = 64 +block_size = 256 # context of up to 256 previous characters + +# baby GPT model :) +n_layer = 6 +n_head = 6 +n_embd = 384 +dropout = 0.2 + +learning_rate = 1e-3 # with baby networks can afford to go a bit higher +max_iters = 5000 +lr_decay_iters = 5000 # `make equal to max_iters usually` +min_lr = 1e-4 # learning_rate / 10 usually +beta2 = 0.99 # make a bit bigger because number of tokens per `iter` is small + +warmup_iters = 100 # not super necessary potentially + +# on macbook also add +# `device = 'cpu' # run on cpu only` +# compile = False # do not torch compile the model diff --git a/docs/sphinx/applications/python/nanoGPT/configurator.py b/docs/sphinx/applications/python/nanoGPT/configurator.py new file mode 100644 index 00000000000..02be1111893 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/configurator.py @@ -0,0 +1,47 @@ +""" +Poor Man's Configurator. Probably a terrible idea. Example usage: +`$ python train.py config/override_file.py --batch_size=32` +`this will first run config/override_file.py, then override batch_size to 32` + +`The code in this file will be run as follows from e.g. train.py:` +`>>> exec(open('configurator.py').read())` + +`So it's not a Python module, it's just shuttling this code away from train.py` +The code in this script then overrides the `globals()` + +I know people are not going to love this, I just really dislike configuration +complexity and having to prepend config. to every single variable. If someone +comes up with a better simple Python solution I am all ears. +""" + +import sys +from ast import literal_eval + +for arg in sys.argv[1:]: + if '=' not in arg: + # assume it's the name of a config file + assert not arg.startswith('--') + config_file = arg + print(f"Overriding config with {config_file}:") + with open(config_file) as f: + print(f.read()) + exec(open(config_file).read()) + else: + # assume it's a --key=value argument + assert arg.startswith('--') + key, val = arg.split('=') + key = key[2:] + if key in globals(): + try: + # attempt to `eval` it (e.g. if bool, number, or etc) + attempt = literal_eval(val) + except (SyntaxError, ValueError): + # if that goes wrong, just use the string + attempt = val + # ensure the types match `ok` + assert type(attempt) == type(globals()[key]) + # cross fingers + print(f"Overriding: {key} = {attempt}") + globals()[key] = attempt + else: + raise ValueError(f"Unknown config key: {key}") diff --git a/docs/sphinx/applications/python/nanoGPT/data/openwebtext/prepare.py b/docs/sphinx/applications/python/nanoGPT/data/openwebtext/prepare.py new file mode 100644 index 00000000000..f007025e45c --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/openwebtext/prepare.py @@ -0,0 +1,88 @@ +# saves the openwebtext dataset to a binary file for training. following was helpful: +# https://github.com/HazyResearch/flash-attention/blob/main/training/src/datamodules/language_modeling_hf.py + +import os +from tqdm import tqdm +import numpy as np +import tiktoken +from datasets import load_dataset # huggingface datasets + +# number of workers in .map() call +# good number to use is ~order number of `cpu` cores // 2 +num_proc = 8 + +# number of workers in load_dataset() call +# `best number might be different from num_proc above as it also depends on NW speed.` +# it is better than 1 usually though +num_proc_load_dataset = num_proc + +enc = tiktoken.get_encoding("gpt2") + +if __name__ == '__main__': + # takes 54GB in huggingface .cache `dir`, about 8M documents (8,013,769) + dataset = load_dataset("openwebtext", num_proc=num_proc_load_dataset) + + # `owt` by default only contains the 'train' split, so create a test split + split_dataset = dataset["train"].train_test_split(test_size=0.0005, + seed=2357, + shuffle=True) + split_dataset['val'] = split_dataset.pop( + 'test') # rename the test split to val + + # this results in: + # >>> split_dataset + # DatasetDict({ + # train: Dataset({ + # features: ['text'], + # `num_rows`: 8009762 + # }) + # val: Dataset({ + # features: ['text'], + # `num_rows`: 4007 + # }) + # }) + + # `we now want to tokenize the dataset. first define the encoding function (gpt2 bpe)` + def process(example): + ids = enc.encode_ordinary( + example['text']) # encode_ordinary ignores any special tokens + ids.append(enc.eot_token + ) # add the end of text token, e.g. 50256 for gpt2 `bpe` + # `note: I think eot should be prepended not appended... hmm. it's called "eot" though...` + out = {'ids': ids, 'len': len(ids)} + return out + + # `tokenize the dataset` + tokenized = split_dataset.map( + process, + remove_columns=['text'], + desc="tokenizing the splits", + num_proc=num_proc, + ) + + # concatenate all the ids in each dataset into one large file we can use for training + for split, dset in tokenized.items(): + arr_len = np.sum(dset['len'], dtype=np.uint64) + filename = os.path.join(os.path.dirname(__file__), f'{split}.bin') + dtype = np.uint16 # (can do since enc.max_token_value == 50256 is < 2**16) + arr = np.memmap(filename, dtype=dtype, mode='w+', shape=(arr_len,)) + total_batches = 1024 + + idx = 0 + for batch_idx in tqdm(range(total_batches), desc=f'writing {filename}'): + # Batch together samples for faster write + batch = dset.shard(num_shards=total_batches, + index=batch_idx, + contiguous=True).with_format('numpy') + arr_batch = np.concatenate(batch['ids']) + # Write into `mmap` + arr[idx:idx + len(arr_batch)] = arr_batch + idx += len(arr_batch) + arr.flush() + + # train.bin is ~17GB, val.bin ~8.5MB + # train has ~9B tokens (9,035,582,198) + # val has ~4M tokens (4,434,897) + + # to read the bin files later, e.g. with `numpy`: + # `m = np.memmap('train.bin', dtype=np.uint16, mode='r')` diff --git a/docs/sphinx/applications/python/nanoGPT/data/openwebtext/readme.md b/docs/sphinx/applications/python/nanoGPT/data/openwebtext/readme.md new file mode 100644 index 00000000000..2a318fc5c51 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/openwebtext/readme.md @@ -0,0 +1,14 @@ +# openwebtext dataset + +after running `prepare.py` (preprocess) we get: + +- train.bin is ~17GB, val.bin ~8.5MB +- train has ~9B tokens (9,035,582,198) +- val has ~4M tokens (4,434,897) + +this came from 8,013,769 documents in total. + +references: + +- OpenAI's `WebText` `dataset` is discussed in [GPT-2 paper](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf) +- [OpenWebText](https://skylion007.github.io/OpenWebTextCorpus/) `dataset` diff --git a/docs/sphinx/applications/python/nanoGPT/data/save_dir_3k.zip b/docs/sphinx/applications/python/nanoGPT/data/save_dir_3k.zip new file mode 100644 index 00000000000..a1ff3bcc987 Binary files /dev/null and b/docs/sphinx/applications/python/nanoGPT/data/save_dir_3k.zip differ diff --git a/docs/sphinx/applications/python/nanoGPT/data/shakespeare/prepare.py b/docs/sphinx/applications/python/nanoGPT/data/shakespeare/prepare.py new file mode 100644 index 00000000000..09ddf1ac2e8 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/shakespeare/prepare.py @@ -0,0 +1,33 @@ +import os +import requests +import tiktoken +import numpy as np + +# download the tiny shakespeare dataset +input_file_path = os.path.join(os.path.dirname(__file__), 'input.txt') +if not os.path.exists(input_file_path): + data_url = 'https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt' + with open(input_file_path, 'w', encoding='utf-8') as f: + f.write(requests.get(data_url).text) + +with open(input_file_path, 'r', encoding='utf-8') as f: + data = f.read() +n = len(data) +train_data = data[:int(n * 0.9)] +val_data = data[int(n * 0.9):] + +# `encode with tiktoken gpt2 bpe` +enc = tiktoken.get_encoding("gpt2") +train_ids = enc.encode_ordinary(train_data) +val_ids = enc.encode_ordinary(val_data) +print(f"train has {len(train_ids):,} tokens") +print(f"val has {len(val_ids):,} tokens") + +# export to bin files +train_ids = np.array(train_ids, dtype=np.uint16) +val_ids = np.array(val_ids, dtype=np.uint16) +train_ids.tofile(os.path.join(os.path.dirname(__file__), 'train.bin')) +val_ids.tofile(os.path.join(os.path.dirname(__file__), 'val.bin')) + +# train.bin has 301,966 tokens +# val.bin has 36,059 tokens diff --git a/docs/sphinx/applications/python/nanoGPT/data/shakespeare/readme.md b/docs/sphinx/applications/python/nanoGPT/data/shakespeare/readme.md new file mode 100644 index 00000000000..22c79fedc4b --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/shakespeare/readme.md @@ -0,0 +1,8 @@ +# tiny shakespeare + +Tiny shakespeare, of the good old `char-rnn` fame :) + +After running `prepare.py`: + +- train.bin has 301,966 tokens +- val.bin has 36,059 tokens diff --git a/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/prepare.py b/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/prepare.py new file mode 100644 index 00000000000..257db77281a --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/prepare.py @@ -0,0 +1,75 @@ +""" +Prepare the Shakespeare dataset for character-level language modeling. +So instead of encoding with GPT-2 BPE tokens, we just map characters to `ints`. +Will save train.bin, val.bin containing the ids, and `meta.pkl` containing the +encoder and decoder and some other related info. +""" +import os +import pickle +import requests +import numpy as np + +# download the tiny shakespeare dataset +input_file_path = os.path.join(os.path.dirname(__file__), 'input.txt') +if not os.path.exists(input_file_path): + data_url = 'https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt' + with open(input_file_path, 'w') as f: + f.write(requests.get(data_url).text) + +with open(input_file_path, 'r') as f: + data = f.read() +print(f"length of dataset in characters: {len(data):,}") + +# get all the unique characters that occur in this text +chars = sorted(list(set(data))) +vocab_size = len(chars) +print("all the unique characters:", ''.join(chars)) +print(f"vocab size: {vocab_size:,}") + +# create a mapping from characters to integers +stoi = {ch: i for i, ch in enumerate(chars)} +itos = {i: ch for i, ch in enumerate(chars)} + + +def encode(s): + return [stoi[c] for c in s + ] # encoder: take a string, output a list of integers + + +def decode(l): + return ''.join([itos[i] for i in l + ]) # decoder: take a list of integers, output a string + + +# create the train and test splits +n = len(data) +train_data = data[:int(n * 0.9)] +val_data = data[int(n * 0.9):] + +# encode both to integers +train_ids = encode(train_data) +val_ids = encode(val_data) +print(f"train has {len(train_ids):,} tokens") +print(f"val has {len(val_ids):,} tokens") + +# export to bin files +train_ids = np.array(train_ids, dtype=np.uint16) +val_ids = np.array(val_ids, dtype=np.uint16) +train_ids.tofile(os.path.join(os.path.dirname(__file__), 'train.bin')) +val_ids.tofile(os.path.join(os.path.dirname(__file__), 'val.bin')) + +# save the meta information as well, to help us encode/decode later +meta = { + 'vocab_size': vocab_size, + 'itos': itos, + 'stoi': stoi, +} +with open(os.path.join(os.path.dirname(__file__), 'meta.pkl'), 'wb') as f: + pickle.dump(meta, f) + +# length of dataset in characters: 1115394 +# all the unique characters: +# `!$&',-.3:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz` +# vocab size: 65 +# train has 1003854 tokens +# val has 111540 tokens diff --git a/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/readme.md b/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/readme.md new file mode 100644 index 00000000000..8f58a010065 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/data/shakespeare_char/readme.md @@ -0,0 +1,9 @@ + +# tiny shakespeare, character-level + +Tiny shakespeare, of the good old `char-rnn` fame :) Treated on character-level. + +After running `prepare.py`: + +- train.bin has 1,003,854 tokens +- val.bin has 111,540 tokens diff --git a/docs/sphinx/applications/python/nanoGPT/model.py b/docs/sphinx/applications/python/nanoGPT/model.py new file mode 100644 index 00000000000..6f4f27dcf4d --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/model.py @@ -0,0 +1,398 @@ +""" +Full definition of a GPT Language Model, all of it in this single file. +References: +1) the official GPT-2 TensorFlow implementation released by OpenAI: +https://github.com/openai/gpt-2/blob/master/src/model.py +2) huggingface/transformers PyTorch implementation: +https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py +""" + +import math +import inspect +from dataclasses import dataclass + +import torch +import torch.nn as nn +from torch.nn import functional as F + + +class LayerNorm(nn.Module): + """ LayerNorm but with an optional bias. PyTorch doesn't support simply bias=False """ + + def __init__(self, ndim, bias): + super().__init__() + self.weight = nn.Parameter(torch.ones(ndim)) + self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None + + def forward(self, input): + return F.layer_norm(input, self.weight.shape, self.weight, self.bias, + 1e-5) + + +class CausalSelfAttention(nn.Module): + + def __init__(self, config): + super().__init__() + assert config.n_embd % config.n_head == 0 + # key, query, value projections for all heads, but in a batch + self.c_attn = nn.Linear(config.n_embd, + 3 * config.n_embd, + bias=config.bias) + # output projection + self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias) + # regularization + self.attn_dropout = nn.Dropout(config.dropout) + self.resid_dropout = nn.Dropout(config.dropout) + self.n_head = config.n_head + self.n_embd = config.n_embd + self.dropout = config.dropout + # flash attention make GPU go `brrrrr` but support is only in PyTorch >= 2.0 + self.flash = hasattr(torch.nn.functional, + 'scaled_dot_product_attention') + if not self.flash: + print( + "WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0" + ) + # causal mask to ensure that attention is only applied to the left in the input sequence + self.register_buffer( + "bias", + torch.tril(torch.ones(config.block_size, + config.block_size)).view( + 1, 1, config.block_size, + config.block_size)) + + def forward(self, x): + B, T, C = x.size( + ) # batch size, sequence length, embedding dimensionality (`n_embd`) + + # calculate query, key, values for all heads in batch and move head forward to be the batch dim + q, k, v = self.c_attn(x).split(self.n_embd, dim=2) + k = k.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + q = q.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + v = v.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + + # `causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)` + if self.flash: + # efficient attention using Flash Attention CUDA kernels + y = torch.nn.functional.scaled_dot_product_attention( + q, + k, + v, + attn_mask=None, + dropout_p=self.dropout if self.training else 0, + is_causal=True) + else: + # manual implementation of attention + att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1))) + att = att.masked_fill(self.bias[:, :, :T, :T] == 0, float('-inf')) + att = F.softmax(att, dim=-1) + att = self.attn_dropout(att) + y = att @ v # `(B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)` + y = y.transpose(1, 2).contiguous().view( + B, T, C) # re-assemble all head outputs side by side + + # output projection + y = self.resid_dropout(self.c_proj(y)) + return y + + +class MLP(nn.Module): + + def __init__(self, config): + super().__init__() + self.c_fc = nn.Linear(config.n_embd, + 4 * config.n_embd, + bias=config.bias) + self.gelu = nn.GELU() + self.c_proj = nn.Linear(4 * config.n_embd, + config.n_embd, + bias=config.bias) + self.dropout = nn.Dropout(config.dropout) + + def forward(self, x): + x = self.c_fc(x) + x = self.gelu(x) + x = self.c_proj(x) + x = self.dropout(x) + return x + + +class Block(nn.Module): + + def __init__(self, config): + super().__init__() + self.ln_1 = LayerNorm(config.n_embd, bias=config.bias) + self.attn = CausalSelfAttention(config) + self.ln_2 = LayerNorm(config.n_embd, bias=config.bias) + self.mlp = MLP(config) + + def forward(self, x): + x = x + self.attn(self.ln_1(x)) + x = x + self.mlp(self.ln_2(x)) + return x + + +@dataclass +class GPTConfig: + block_size: int = 1024 + vocab_size: int = 50304 # GPT-2 vocab_size of 50257, padded up to nearest multiple of 64 for efficiency + n_layer: int = 12 + n_head: int = 12 + n_embd: int = 768 + dropout: float = 0.0 + bias: bool = True # True: bias in Linears and LayerNorms, like GPT-2. False: a bit better and faster + + +class GPT(nn.Module): + + def __init__(self, config): + super().__init__() + assert config.vocab_size is not None + assert config.block_size is not None + self.config = config + + self.transformer = nn.ModuleDict( + dict( + wte=nn.Embedding(config.vocab_size, config.n_embd), + wpe=nn.Embedding(config.block_size, config.n_embd), + drop=nn.Dropout(config.dropout), + h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]), + ln_f=LayerNorm(config.n_embd, bias=config.bias), + )) + self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + # with weight tying when using torch.compile() some warnings get generated: + # "`UserWarning`: functional_call was passed multiple values for tied weights. + # This behavior is deprecated and will be an error in future versions" + # not 100% sure what this is, so far seems to be harmless. TODO investigate + self.transformer.wte.weight = self.lm_head.weight # https://paperswithcode.com/method/weight-tying + + # `init` all weights + self.apply(self._init_weights) + # apply special scaled `init` to the residual projections, per GPT-2 paper + for pn, p in self.named_parameters(): + if pn.endswith('c_proj.weight'): + torch.nn.init.normal_(p, + mean=0.0, + std=0.02 / math.sqrt(2 * config.n_layer)) + + # report number of parameters + print("number of parameters: %.2fM" % (self.get_num_params() / 1e6,)) + + def get_num_params(self, non_embedding=True): + """ + Return the number of parameters in the model. + For non-embedding count (default), the position `embeddings` get subtracted. + The token `embeddings` would too, except due to the parameter sharing these + `params` are actually used as weights in the final layer, so we include them. + """ + n_params = sum(p.numel() for p in self.parameters()) + if non_embedding: + n_params -= self.transformer.wpe.weight.numel() + return n_params + + def _init_weights(self, module): + if isinstance(module, nn.Linear): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + if module.bias is not None: + torch.nn.init.zeros_(module.bias) + elif isinstance(module, nn.Embedding): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + + def forward(self, idx, targets=None): + device = idx.device + b, t = idx.size() + assert t <= self.config.block_size, f"Cannot forward sequence of length {t}, block size is only {self.config.block_size}" + pos = torch.arange(0, t, dtype=torch.long, device=device) # shape (t) + + # forward the GPT model itself + tok_emb = self.transformer.wte( + idx) # `token embeddings of shape (b, t, n_embd)` + pos_emb = self.transformer.wpe( + pos) # `position embeddings of shape (t, n_embd)` + x = self.transformer.drop(tok_emb + pos_emb) + for block in self.transformer.h: + x = block(x) + x = self.transformer.ln_f(x) + + if targets is not None: + # if we are given some desired targets also calculate the loss + logits = self.lm_head(x) + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), + targets.view(-1), + ignore_index=-1) + else: + # inference-time mini-optimization: only forward the `lm_head` on the very last position + logits = self.lm_head( + x[:, [-1], :]) # note: using list [-1] to preserve the time dim + loss = None + + return logits, loss + + def crop_block_size(self, block_size): + # model surgery to decrease the block size if necessary + # e.g. we may load the GPT2 pretrained model checkpoint (block size 1024) + # but want to use a smaller block size for some smaller, simpler model + assert block_size <= self.config.block_size + self.config.block_size = block_size + self.transformer.wpe.weight = nn.Parameter( + self.transformer.wpe.weight[:block_size]) + for block in self.transformer.h: + if hasattr(block.attn, 'bias'): + block.attn.bias = block.attn.bias[:, :, :block_size, : + block_size] + + @classmethod + def from_pretrained(cls, model_type, override_args=None): + assert model_type in {'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'} + override_args = override_args or {} # default to empty dict + # only dropout can be overridden see more notes below + assert all(k == 'dropout' for k in override_args) + from transformers import GPT2LMHeadModel + print("loading weights from pretrained gpt: %s" % model_type) + + # `n_layer, n_head and n_embd are determined from model_type` + config_args = { + 'gpt2': dict(n_layer=12, n_head=12, n_embd=768), # 124M `params` + 'gpt2-medium': dict(n_layer=24, n_head=16, + n_embd=1024), # 350M `params` + 'gpt2-large': dict(n_layer=36, n_head=20, + n_embd=1280), # 774M `params` + 'gpt2-xl': dict(n_layer=48, n_head=25, + n_embd=1600), # 1558M `params` + }[model_type] + print("forcing vocab_size=50257, block_size=1024, bias=True") + config_args[ + 'vocab_size'] = 50257 # always 50257 for GPT model checkpoints + config_args[ + 'block_size'] = 1024 # always 1024 for GPT model checkpoints + config_args['bias'] = True # always True for GPT model checkpoints + # we can override the dropout rate, if desired + if 'dropout' in override_args: + print(f"overriding dropout rate to {override_args['dropout']}") + config_args['dropout'] = override_args['dropout'] + # create a from-scratch initialized minGPT model + config = GPTConfig(**config_args) + model = GPT(config) + sd = model.state_dict() + sd_keys = sd.keys() + sd_keys = [k for k in sd_keys if not k.endswith('.attn.bias') + ] # discard this mask / buffer, not a `param` + + # `init` a huggingface/transformers model + model_hf = GPT2LMHeadModel.from_pretrained(model_type) + sd_hf = model_hf.state_dict() + + # copy while ensuring all of the parameters are aligned and match in names and shapes + sd_keys_hf = sd_hf.keys() + sd_keys_hf = [ + k for k in sd_keys_hf if not k.endswith('.attn.masked_bias') + ] # ignore these, just a buffer + sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.bias') + ] # same, just the mask (buffer) + transposed = [ + 'attn.c_attn.weight', 'attn.c_proj.weight', 'mlp.c_fc.weight', + 'mlp.c_proj.weight' + ] + # basically the `openai` checkpoints use a "`Conv1D`" module, but we only want to use a vanilla Linear + # this means that we have to transpose these weights when we import them + assert len(sd_keys_hf) == len( + sd_keys), f"mismatched keys: {len(sd_keys_hf)} != {len(sd_keys)}" + for k in sd_keys_hf: + if any(k.endswith(w) for w in transposed): + # special treatment for the Conv1D weights we need to transpose + assert sd_hf[k].shape[::-1] == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k].t()) + else: + # vanilla copy over the other parameters + assert sd_hf[k].shape == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k]) + + return model + + def configure_optimizers(self, weight_decay, learning_rate, betas, + device_type): + # start with all of the candidate parameters + param_dict = {pn: p for pn, p in self.named_parameters()} + # filter out those that do not require grad + param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad} + # `create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.` + # `i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.` + decay_params = [p for n, p in param_dict.items() if p.dim() >= 2] + nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2] + optim_groups = [{ + 'params': decay_params, + 'weight_decay': weight_decay + }, { + 'params': nodecay_params, + 'weight_decay': 0.0 + }] + num_decay_params = sum(p.numel() for p in decay_params) + num_nodecay_params = sum(p.numel() for p in nodecay_params) + print( + f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters" + ) + print( + f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters" + ) + # Create `AdamW` optimizer and use the fused version if it is available + fused_available = 'fused' in inspect.signature( + torch.optim.AdamW).parameters + use_fused = fused_available and device_type == 'cuda' + extra_args = dict(fused=True) if use_fused else dict() + optimizer = torch.optim.AdamW(optim_groups, + lr=learning_rate, + betas=betas, + **extra_args) + print(f"using fused AdamW: {use_fused}") + + return optimizer + + def estimate_mfu(self, fwdbwd_per_iter, dt): + """ estimate model flops utilization (`MFU`) in units of A100 `bfloat16` peak FLOPS """ + # first estimate the number of flops we do per iteration. + # see PaLM paper Appendix B as ref: https://arxiv.org/abs/2204.02311 + N = self.get_num_params() + cfg = self.config + L, H, Q, T = cfg.n_layer, cfg.n_head, cfg.n_embd // cfg.n_head, cfg.block_size + flops_per_token = 6 * N + 12 * L * H * Q * T + flops_per_fwdbwd = flops_per_token * T + flops_per_iter = flops_per_fwdbwd * fwdbwd_per_iter + # express our flops throughput as ratio of A100 `bfloat16` peak flops + flops_achieved = flops_per_iter * (1.0 / dt) # per second + flops_promised = 312e12 # A100 GPU `bfloat16` peak flops is 312 TFLOPS + mfu = flops_achieved / flops_promised + return mfu + + @torch.no_grad() + def generate(self, idx, max_new_tokens, temperature=1.0, top_k=None): + """ + Take a conditioning sequence of indices `idx` (`LongTensor` of shape (b,t)) and complete + the sequence max_new_tokens times, feeding the predictions back into the model each time. + Most likely you'll want to make sure to be in `model.eval()` mode of operation for this. + """ + for _ in range(max_new_tokens): + # if the sequence context is growing too long we must crop it at block_size + idx_cond = idx if idx.size( + 1) <= self.config.block_size else idx[:, + -self.config.block_size:] + # forward the model to get the `logits` for the index in the sequence + logits, _ = self(idx_cond) + # pluck the `logits` at the final step and scale by desired temperature + logits = logits[:, -1, :] / temperature + # optionally crop the `logits` to only the top k options + if top_k is not None: + v, _ = torch.topk(logits, min(top_k, logits.size(-1))) + logits[logits < v[:, [-1]]] = -float('Inf') + # apply `softmax` to convert `logits` to (normalized) probabilities + probs = F.softmax(logits, dim=-1) + # sample from the distribution + idx_next = torch.multinomial(probs, num_samples=1) + # append sampled index to the running sequence and continue + idx = torch.cat((idx, idx_next), dim=1) + + return idx diff --git a/docs/sphinx/applications/python/nanoGPT/model_pad.py b/docs/sphinx/applications/python/nanoGPT/model_pad.py new file mode 100644 index 00000000000..0a88b061e8b --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/model_pad.py @@ -0,0 +1,398 @@ +# VERSION SUPPORTING PADDING +""" +Full definition of a GPT Language Model, all of it in this single file. +References: +1) the official GPT-2 TensorFlow implementation released by OpenAI: +https://github.com/openai/gpt-2/blob/master/src/model.py +2) huggingface/transformers PyTorch implementation: +https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py +""" + +import math +import inspect +from dataclasses import dataclass + +import torch +import torch.nn as nn +from torch.nn import functional as F + + +class LayerNorm(nn.Module): + """ LayerNorm but with an optional bias. PyTorch doesn't support simply bias=False """ + + def __init__(self, ndim, bias): + super().__init__() + self.weight = nn.Parameter(torch.ones(ndim)) + self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None + + def forward(self, input): + return F.layer_norm(input, self.weight.shape, self.weight, self.bias, + 1e-5) + + +class CausalSelfAttention(nn.Module): + + def __init__(self, config): + super().__init__() + assert config.n_embd % config.n_head == 0 + # key, query, value projections for all heads, but in a batch + self.c_attn = nn.Linear(config.n_embd, + 3 * config.n_embd, + bias=config.bias) + # output projection + self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias) + # regularization + self.attn_dropout = nn.Dropout(config.dropout) + self.resid_dropout = nn.Dropout(config.dropout) + self.n_head = config.n_head + self.n_embd = config.n_embd + self.dropout = config.dropout + # flash attention make GPU go `brrrrr` but support is only in PyTorch >= 2.0 + self.flash = hasattr(torch.nn.functional, + 'scaled_dot_product_attention') + if not self.flash: + raise RuntimeError("Flash attention is required.") + # print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0") + # # causal mask to ensure that attention is only applied to the left in the input sequence + # `self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))` + # `.view(1, 1, config.block_size, config.block_size))` + + def forward(self, x, padding_mask=None): + B, T, C = x.size( + ) # batch size, sequence length, embedding dimensionality (`n_embd`) + + # calculate query, key, values for all heads in batch and move head forward to be the batch dim + q, k, v = self.c_attn(x).split(self.n_embd, dim=2) + k = k.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + q = q.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + v = v.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + + if padding_mask is not None: + # `Ensure padding_mask is (batch_size, 1, 1, seq_len) for broadcasting` + padding_mask = padding_mask[:, None, None, :] + + # `causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)` + # efficient attention using Flash Attention CUDA kernels + y = torch.nn.functional.scaled_dot_product_attention( + q, + k, + v, + attn_mask=padding_mask, + dropout_p=self.dropout if self.training else 0, + is_causal=True) + y = y.transpose(1, 2).contiguous().view( + B, T, C) # re-assemble all head outputs side by side + # output projection + y = self.resid_dropout(self.c_proj(y)) + #print(y) + return y + + +class MLP(nn.Module): + + def __init__(self, config): + super().__init__() + self.c_fc = nn.Linear(config.n_embd, + 4 * config.n_embd, + bias=config.bias) + self.gelu = nn.GELU() + self.c_proj = nn.Linear(4 * config.n_embd, + config.n_embd, + bias=config.bias) + self.dropout = nn.Dropout(config.dropout) + + def forward(self, x): + x = self.c_fc(x) + x = self.gelu(x) + x = self.c_proj(x) + x = self.dropout(x) + return x + + +class Block(nn.Module): + + def __init__(self, config): + super().__init__() + self.ln_1 = LayerNorm(config.n_embd, bias=config.bias) + self.attn = CausalSelfAttention(config) + self.ln_2 = LayerNorm(config.n_embd, bias=config.bias) + self.mlp = MLP(config) + + def forward(self, x, padding_mask=None): + x = x + self.attn(self.ln_1(x), padding_mask=padding_mask) + x = x + self.mlp(self.ln_2(x)) + return x + + +@dataclass +class GPTConfig: + block_size: int = 1024 + vocab_size: int = 50304 # GPT-2 vocab_size of 50257, padded up to nearest multiple of 64 for efficiency + n_layer: int = 12 + n_head: int = 12 + n_embd: int = 768 + dropout: float = 0.0 + bias: bool = True # True: bias in Linears and LayerNorms, like GPT-2. False: a bit better and faster + + +class GPT(nn.Module): + + def __init__(self, config): + print("Initiating nanoGPT model with padding support") + super().__init__() + assert config.vocab_size is not None + assert config.block_size is not None + self.config = config + + self.transformer = nn.ModuleDict( + dict( + wte=nn.Embedding(config.vocab_size, config.n_embd), + wpe=nn.Embedding(config.block_size, config.n_embd), + drop=nn.Dropout(config.dropout), + h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]), + ln_f=LayerNorm(config.n_embd, bias=config.bias), + )) + self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + # with weight tying when using torch.compile() some warnings get generated: + # "`UserWarning`: functional_call was passed multiple values for tied weights. + # This behavior is deprecated and will be an error in future versions" + # not 100% sure what this is, so far seems to be harmless. TODO investigate + self.transformer.wte.weight = self.lm_head.weight # https://paperswithcode.com/method/weight-tying + + # `init` all weights + self.apply(self._init_weights) + # apply special scaled `init` to the residual projections, per GPT-2 paper + for pn, p in self.named_parameters(): + if pn.endswith('c_proj.weight'): + torch.nn.init.normal_(p, + mean=0.0, + std=0.02 / math.sqrt(2 * config.n_layer)) + + # report number of parameters + print("number of parameters: %.2fM" % (self.get_num_params() / 1e6,)) + + def get_num_params(self, non_embedding=True): + """ + Return the number of parameters in the model. + For non-embedding count (default), the position `embeddings` get subtracted. + The token `embeddings` would too, except due to the parameter sharing these + `params` are actually used as weights in the final layer, so we include them. + """ + n_params = sum(p.numel() for p in self.parameters()) + if non_embedding: + n_params -= self.transformer.wpe.weight.numel() + return n_params + + def _init_weights(self, module): + if isinstance(module, nn.Linear): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + if module.bias is not None: + torch.nn.init.zeros_(module.bias) + elif isinstance(module, nn.Embedding): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + + def forward(self, + idx, + targets=None, + padding_mask=None, + preserve_time_dim=False): + device = idx.device + b, t = idx.size() + assert t <= self.config.block_size, f"Cannot forward sequence of length {t}, block size is only {self.config.block_size}" + pos = torch.arange(0, t, dtype=torch.long, device=device) # shape (t) + + # forward the GPT model itself + tok_emb = self.transformer.wte( + idx) # `token embeddings of shape (b, t, n_embd)` + pos_emb = self.transformer.wpe( + pos) # `position embeddings of shape (t, n_embd)` + x = self.transformer.drop(tok_emb + pos_emb) + for block in self.transformer.h: + x = block(x, padding_mask=padding_mask) + x = self.transformer.ln_f(x) + + if targets is not None: + # if we are given some desired targets also calculate the loss + logits = self.lm_head(x) + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), + targets.view(-1), + ignore_index=0) + elif not preserve_time_dim: + # inference-time mini-optimization: only forward the `lm_head` on the very last position + logits = self.lm_head( + x[:, [-1], :]) # note: using list [-1] to preserve the time dim + loss = None + else: + logits = self.lm_head(x) + loss = None + + return logits, loss + + def crop_block_size(self, block_size): + # model surgery to decrease the block size if necessary + # e.g. we may load the GPT2 pretrained model checkpoint (block size 1024) + # but want to use a smaller block size for some smaller, simpler model + assert block_size <= self.config.block_size + self.config.block_size = block_size + self.transformer.wpe.weight = nn.Parameter( + self.transformer.wpe.weight[:block_size]) + for block in self.transformer.h: + if hasattr(block.attn, 'bias'): + block.attn.bias = block.attn.bias[:, :, :block_size, : + block_size] + + @classmethod + def from_pretrained(cls, model_type, override_args=None): + assert model_type in {'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'} + override_args = override_args or {} # default to empty dict + # only dropout can be overridden see more notes below + assert all(k == 'dropout' for k in override_args) + from transformers import GPT2LMHeadModel + print("loading weights from pretrained gpt: %s" % model_type) + + # `n_layer, n_head and n_embd are determined from model_type` + config_args = { + 'gpt2': dict(n_layer=12, n_head=12, n_embd=768), # 124M `params` + 'gpt2-medium': dict(n_layer=24, n_head=16, + n_embd=1024), # 350M `params` + 'gpt2-large': dict(n_layer=36, n_head=20, + n_embd=1280), # 774M `params` + 'gpt2-xl': dict(n_layer=48, n_head=25, + n_embd=1600), # 1558M `params` + }[model_type] + print("forcing vocab_size=50257, block_size=1024, bias=True") + config_args[ + 'vocab_size'] = 50257 # always 50257 for GPT model checkpoints + config_args[ + 'block_size'] = 1024 # always 1024 for GPT model checkpoints + config_args['bias'] = True # always True for GPT model checkpoints + # we can override the dropout rate, if desired + if 'dropout' in override_args: + print(f"overriding dropout rate to {override_args['dropout']}") + config_args['dropout'] = override_args['dropout'] + # create a from-scratch initialized minGPT model + config = GPTConfig(**config_args) + model = GPT(config) + sd = model.state_dict() + sd_keys = sd.keys() + sd_keys = [k for k in sd_keys if not k.endswith('.attn.bias') + ] # discard this mask / buffer, not a `param` + + # `init` a huggingface/transformers model + model_hf = GPT2LMHeadModel.from_pretrained(model_type) + sd_hf = model_hf.state_dict() + + # copy while ensuring all of the parameters are aligned and match in names and shapes + sd_keys_hf = sd_hf.keys() + sd_keys_hf = [ + k for k in sd_keys_hf if not k.endswith('.attn.masked_bias') + ] # ignore these, just a buffer + sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.bias') + ] # same, just the mask (buffer) + transposed = [ + 'attn.c_attn.weight', 'attn.c_proj.weight', 'mlp.c_fc.weight', + 'mlp.c_proj.weight' + ] + # basically the `openai` checkpoints use a "`Conv1D`" module, but we only want to use a vanilla Linear + # this means that we have to transpose these weights when we import them + assert len(sd_keys_hf) == len( + sd_keys), f"mismatched keys: {len(sd_keys_hf)} != {len(sd_keys)}" + for k in sd_keys_hf: + if any(k.endswith(w) for w in transposed): + # special treatment for the Conv1D weights we need to transpose + assert sd_hf[k].shape[::-1] == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k].t()) + else: + # vanilla copy over the other parameters + assert sd_hf[k].shape == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k]) + + return model + + def configure_optimizers(self, weight_decay, learning_rate, betas, + device_type): + # start with all of the candidate parameters + param_dict = {pn: p for pn, p in self.named_parameters()} + # filter out those that do not require grad + param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad} + # `create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.` + # `i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.` + decay_params = [p for n, p in param_dict.items() if p.dim() >= 2] + nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2] + optim_groups = [{ + 'params': decay_params, + 'weight_decay': weight_decay + }, { + 'params': nodecay_params, + 'weight_decay': 0.0 + }] + num_decay_params = sum(p.numel() for p in decay_params) + num_nodecay_params = sum(p.numel() for p in nodecay_params) + print( + f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters" + ) + print( + f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters" + ) + # Create `AdamW` optimizer and use the fused version if it is available + fused_available = 'fused' in inspect.signature( + torch.optim.AdamW).parameters + use_fused = fused_available and device_type == 'cuda' + extra_args = dict(fused=True) if use_fused else dict() + optimizer = torch.optim.AdamW(optim_groups, + lr=learning_rate, + betas=betas, + **extra_args) + print(f"using fused AdamW: {use_fused}") + + return optimizer + + def estimate_mfu(self, fwdbwd_per_iter, dt): + """ estimate model flops utilization (`MFU`) in units of A100 `bfloat16` peak FLOPS """ + # first estimate the number of flops we do per iteration. + # see PaLM paper Appendix B as ref: https://arxiv.org/abs/2204.02311 + N = self.get_num_params() + cfg = self.config + L, H, Q, T = cfg.n_layer, cfg.n_head, cfg.n_embd // cfg.n_head, cfg.block_size + flops_per_token = 6 * N + 12 * L * H * Q * T + flops_per_fwdbwd = flops_per_token * T + flops_per_iter = flops_per_fwdbwd * fwdbwd_per_iter + # express our flops throughput as ratio of A100 `bfloat16` peak flops + flops_achieved = flops_per_iter * (1.0 / dt) # per second + flops_promised = 312e12 # A100 GPU `bfloat16` peak flops is 312 TFLOPS + mfu = flops_achieved / flops_promised + return mfu + + @torch.no_grad() + def generate(self, idx, max_new_tokens, temperature=1.0, top_k=None): + """ + Take a conditioning sequence of indices `idx` (`LongTensor` of shape (b,t)) and complete + the sequence max_new_tokens times, feeding the predictions back into the model each time. + Most likely you'll want to make sure to be in `model.eval()` mode of operation for this. + """ + for _ in range(max_new_tokens): + # if the sequence context is growing too long we must crop it at block_size + idx_cond = idx if idx.size( + 1) <= self.config.block_size else idx[:, + -self.config.block_size:] + # forward the model to get the `logits` for the index in the sequence + logits, _ = self(idx_cond) + # pluck the `logits` at the final step and scale by desired temperature + logits = logits[:, -1, :] / temperature + # optionally crop the `logits` to only the top k options + if top_k is not None: + v, _ = torch.topk(logits, min(top_k, logits.size(-1))) + logits[logits < v[:, [-1]]] = -float('Inf') + # apply `softmax` to convert `logits` to (normalized) probabilities + probs = F.softmax(logits, dim=-1) + # sample from the distribution + idx_next = torch.multinomial(probs, num_samples=1) + # append sampled index to the running sequence and continue + idx = torch.cat((idx, idx_next), dim=1) + + return idx diff --git a/docs/sphinx/applications/python/nanoGPT/model_pad_gemb.py b/docs/sphinx/applications/python/nanoGPT/model_pad_gemb.py new file mode 100644 index 00000000000..31637159f1c --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/model_pad_gemb.py @@ -0,0 +1,421 @@ +# VERSION SUPPORTING PADDING +""" +Full definition of a GPT Language Model, all of it in this single file. +References: +1) the official GPT-2 TensorFlow implementation released by OpenAI: +https://github.com/openai/gpt-2/blob/master/src/model.py +2) huggingface/transformers PyTorch implementation: +https://github.com/huggingface/transformers/blob/main/src/transformers/models/gpt2/modeling_gpt2.py +""" + +import math +import inspect +from dataclasses import dataclass + +import torch +import torch.nn as nn +from torch.nn import functional as F + + +class LayerNorm(nn.Module): + """ LayerNorm but with an optional bias. PyTorch doesn't support simply bias=False """ + + def __init__(self, ndim, bias): + super().__init__() + self.weight = nn.Parameter(torch.ones(ndim)) + self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None + + def forward(self, input): + return F.layer_norm(input, self.weight.shape, self.weight, self.bias, + 1e-5) + + +class CausalSelfAttention(nn.Module): + + def __init__(self, config): + super().__init__() + assert config.n_embd % config.n_head == 0 + # key, query, value projections for all heads, but in a batch + self.c_attn = nn.Linear(config.n_embd, + 3 * config.n_embd, + bias=config.bias) + # output projection + self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias) + # regularization + self.attn_dropout = nn.Dropout(config.dropout) + self.resid_dropout = nn.Dropout(config.dropout) + self.n_head = config.n_head + self.n_embd = config.n_embd + self.dropout = config.dropout + # flash attention make GPU go `brrrrr` but support is only in PyTorch >= 2.0 + self.flash = hasattr(torch.nn.functional, + 'scaled_dot_product_attention') + if not self.flash: + raise RuntimeError("Flash attention is required.") + # print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0") + # # causal mask to ensure that attention is only applied to the left in the input sequence + # `self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))` + # `.view(1, 1, config.block_size, config.block_size))` + + def forward(self, x, padding_mask=None): + B, T, C = x.size( + ) # batch size, sequence length, embedding dimensionality (`n_embd`) + + # calculate query, key, values for all heads in batch and move head forward to be the batch dim + q, k, v = self.c_attn(x).split(self.n_embd, dim=2) + k = k.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + q = q.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + v = v.view(B, T, self.n_head, + C // self.n_head).transpose(1, 2) # `(B, nh, T, hs)` + + if padding_mask is not None: + # `Ensure padding_mask is (batch_size, 1, 1, seq_len) for broadcasting` + padding_mask = padding_mask[:, None, None, :] + + # `causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)` + # efficient attention using Flash Attention CUDA kernels + y = torch.nn.functional.scaled_dot_product_attention( + q, + k, + v, + attn_mask=padding_mask, + dropout_p=self.dropout if self.training else 0, + is_causal=True) + y = y.transpose(1, 2).contiguous().view( + B, T, C) # re-assemble all head outputs side by side + # output projection + y = self.resid_dropout(self.c_proj(y)) + #print(y) + return y + + +class MLP(nn.Module): + + def __init__(self, config): + super().__init__() + self.c_fc = nn.Linear(config.n_embd, + 4 * config.n_embd, + bias=config.bias) + self.gelu = nn.GELU() + self.c_proj = nn.Linear(4 * config.n_embd, + config.n_embd, + bias=config.bias) + self.dropout = nn.Dropout(config.dropout) + + def forward(self, x): + x = self.c_fc(x) + x = self.gelu(x) + x = self.c_proj(x) + x = self.dropout(x) + return x + + +class Block(nn.Module): + + def __init__(self, config): + super().__init__() + self.ln_1 = LayerNorm(config.n_embd, bias=config.bias) + self.attn = CausalSelfAttention(config) + self.ln_2 = LayerNorm(config.n_embd, bias=config.bias) + self.mlp = MLP(config) + + def forward(self, x, padding_mask=None): + x = x + self.attn(self.ln_1(x), padding_mask=padding_mask) + x = x + self.mlp(self.ln_2(x)) + return x + + +@dataclass +class GPTConfig: + block_size: int = 1024 + vocab_size: int = 50304 # GPT-2 vocab_size of 50257, padded up to nearest multiple of 64 for efficiency + n_layer: int = 12 + n_head: int = 12 + n_embd: int = 768 + dropout: float = 0.0 + bias: bool = True # True: bias in Linears and LayerNorms, like GPT-2. False: a bit better and faster + graph_emb_dim: int = 500 # default for FEATHER graph + + +class GPT(nn.Module): + + def __init__(self, config): + print("Initiating nanoGPT model with padding support") + super().__init__() + assert config.vocab_size is not None + assert config.block_size is not None + self.config = config + + self.transformer = nn.ModuleDict( + dict( + wte=nn.Embedding(config.vocab_size, config.n_embd), + wpe=nn.Embedding(config.block_size, config.n_embd), + drop=nn.Dropout(config.dropout), + h=nn.ModuleList([Block(config) for _ in range(config.n_layer)]), + ln_f=LayerNorm(config.n_embd, bias=config.bias), + )) + self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + # with weight tying when using torch.compile() some warnings get generated: + # "`UserWarning`: functional_call was passed multiple values for tied weights. + # This behavior is deprecated and will be an error in future versions" + # not 100% sure what this is, so far seems to be harmless. TODO investigate + self.transformer.wte.weight = self.lm_head.weight # https://paperswithcode.com/method/weight-tying + + self.graph_emb_proj = nn.Linear(config.graph_emb_dim, config.n_embd) + + # `init` all weights + self.apply(self._init_weights) + # apply special scaled `init` to the residual projections, per GPT-2 paper + for pn, p in self.named_parameters(): + if pn.endswith('c_proj.weight'): + torch.nn.init.normal_(p, + mean=0.0, + std=0.02 / math.sqrt(2 * config.n_layer)) + + # report number of parameters + print("number of parameters: %.2fM" % (self.get_num_params() / 1e6,)) + + def get_num_params(self, non_embedding=True): + """ + Return the number of parameters in the model. + For non-embedding count (default), the position `embeddings` get subtracted. + The token `embeddings` would too, except due to the parameter sharing these + `params` are actually used as weights in the final layer, so we include them. + """ + n_params = sum(p.numel() for p in self.parameters()) + if non_embedding: + n_params -= self.transformer.wpe.weight.numel() + return n_params + + def _init_weights(self, module): + if isinstance(module, nn.Linear): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + if module.bias is not None: + torch.nn.init.zeros_(module.bias) + elif isinstance(module, nn.Embedding): + torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) + + def forward(self, + idx, + graph_emb, + targets=None, + padding_mask=None, + preserve_time_dim=False): + device = idx.device + b, t = idx.size() + assert t <= self.config.block_size, f"Cannot forward sequence of length {t}, block size is only {self.config.block_size}" + pos = torch.arange(0, t, dtype=torch.long, device=device) # shape (t) + + # forward the GPT model itself + tok_emb = self.transformer.wte( + idx) # `token embeddings of shape (b, t, n_embd)` + pos_emb = self.transformer.wpe( + pos) # `position embeddings of shape (t, n_embd)` + + #`print(f"From forward (2): graph_emb shape: {graph_emb.shape}")` + #`graph_emb = graph_emb.unsqueeze(1)` + + graph_emb = self.graph_emb_proj(graph_emb) + #`print(f"graph_emb proj shape: {graph_emb.shape}")` + + graph_emb = graph_emb.unsqueeze(1) + + #`print(f"graph_emb unsqueeze(1) shape: {graph_emb.shape}")` + #`print(f"tok_emb shape: {tok_emb.shape}")` + + x = tok_emb + pos_emb + graph_emb + + x = self.transformer.drop(x) + for block in self.transformer.h: + x = block(x, padding_mask=padding_mask) + x = self.transformer.ln_f(x) + + if targets is not None: + # if we are given some desired targets also calculate the loss + logits = self.lm_head(x) + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), + targets.view(-1), + ignore_index=0) + elif not preserve_time_dim: + # inference-time mini-optimization: only forward the `lm_head` on the very last position + logits = self.lm_head( + x[:, [-1], :]) # note: using list [-1] to preserve the time dim + loss = None + else: + logits = self.lm_head(x) + loss = None + + return logits, loss + + def crop_block_size(self, block_size): + # model surgery to decrease the block size if necessary + # e.g. we may load the GPT2 pretrained model checkpoint (block size 1024) + # but want to use a smaller block size for some smaller, simpler model + assert block_size <= self.config.block_size + self.config.block_size = block_size + self.transformer.wpe.weight = nn.Parameter( + self.transformer.wpe.weight[:block_size]) + for block in self.transformer.h: + if hasattr(block.attn, 'bias'): + block.attn.bias = block.attn.bias[:, :, :block_size, : + block_size] + + @classmethod + def from_pretrained(cls, model_type, override_args=None): + assert model_type in {'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'} + override_args = override_args or {} # default to empty dict + # only dropout can be overridden see more notes below + assert all(k == 'dropout' for k in override_args) + from transformers import GPT2LMHeadModel + print("loading weights from pretrained gpt: %s" % model_type) + + # `n_layer, n_head and n_embd are determined from model_type` + config_args = { + 'gpt2': dict(n_layer=12, n_head=12, n_embd=768), # 124M `params` + 'gpt2-medium': dict(n_layer=24, n_head=16, + n_embd=1024), # 350M `params` + 'gpt2-large': dict(n_layer=36, n_head=20, + n_embd=1280), # 774M `params` + 'gpt2-xl': dict(n_layer=48, n_head=25, + n_embd=1600), # 1558M `params` + }[model_type] + print("forcing vocab_size=50257, block_size=1024, bias=True") + config_args[ + 'vocab_size'] = 50257 # always 50257 for GPT model checkpoints + config_args[ + 'block_size'] = 1024 # always 1024 for GPT model checkpoints + config_args['bias'] = True # always True for GPT model checkpoints + # we can override the dropout rate, if desired + if 'dropout' in override_args: + print(f"overriding dropout rate to {override_args['dropout']}") + config_args['dropout'] = override_args['dropout'] + # create a from-scratch initialized minGPT model + config = GPTConfig(**config_args) + model = GPT(config) + sd = model.state_dict() + sd_keys = sd.keys() + sd_keys = [k for k in sd_keys if not k.endswith('.attn.bias') + ] # discard this mask / buffer, not a `param` + + # `init` a huggingface/transformers model + model_hf = GPT2LMHeadModel.from_pretrained(model_type) + sd_hf = model_hf.state_dict() + + # copy while ensuring all of the parameters are aligned and match in names and shapes + sd_keys_hf = sd_hf.keys() + sd_keys_hf = [ + k for k in sd_keys_hf if not k.endswith('.attn.masked_bias') + ] # ignore these, just a buffer + sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.bias') + ] # same, just the mask (buffer) + transposed = [ + 'attn.c_attn.weight', 'attn.c_proj.weight', 'mlp.c_fc.weight', + 'mlp.c_proj.weight' + ] + # basically the `openai` checkpoints use a "`Conv1D`" module, but we only want to use a vanilla Linear + # this means that we have to transpose these weights when we import them + assert len(sd_keys_hf) == len( + sd_keys), f"mismatched keys: {len(sd_keys_hf)} != {len(sd_keys)}" + for k in sd_keys_hf: + if any(k.endswith(w) for w in transposed): + # special treatment for the Conv1D weights we need to transpose + assert sd_hf[k].shape[::-1] == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k].t()) + else: + # vanilla copy over the other parameters + assert sd_hf[k].shape == sd[k].shape + with torch.no_grad(): + sd[k].copy_(sd_hf[k]) + + return model + + def configure_optimizers(self, weight_decay, learning_rate, betas, + device_type): + # start with all of the candidate parameters + param_dict = {pn: p for pn, p in self.named_parameters()} + # filter out those that do not require grad + param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad} + # `create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.` + # `i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.` + decay_params = [p for n, p in param_dict.items() if p.dim() >= 2] + nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2] + optim_groups = [{ + 'params': decay_params, + 'weight_decay': weight_decay + }, { + 'params': nodecay_params, + 'weight_decay': 0.0 + }] + num_decay_params = sum(p.numel() for p in decay_params) + num_nodecay_params = sum(p.numel() for p in nodecay_params) + print( + f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters" + ) + print( + f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters" + ) + # Create `AdamW` optimizer and use the fused version if it is available + fused_available = 'fused' in inspect.signature( + torch.optim.AdamW).parameters + use_fused = fused_available and device_type == 'cuda' + extra_args = dict(fused=True) if use_fused else dict() + optimizer = torch.optim.AdamW(optim_groups, + lr=learning_rate, + betas=betas, + **extra_args) + print(f"using fused AdamW: {use_fused}") + + return optimizer + + def estimate_mfu(self, fwdbwd_per_iter, dt): + """ estimate model flops utilization (`MFU`) in units of A100 `bfloat16` peak FLOPS """ + # first estimate the number of flops we do per iteration. + # see PaLM paper Appendix B as ref: https://arxiv.org/abs/2204.02311 + N = self.get_num_params() + cfg = self.config + L, H, Q, T = cfg.n_layer, cfg.n_head, cfg.n_embd // cfg.n_head, cfg.block_size + flops_per_token = 6 * N + 12 * L * H * Q * T + flops_per_fwdbwd = flops_per_token * T + flops_per_iter = flops_per_fwdbwd * fwdbwd_per_iter + # express our flops throughput as ratio of A100 `bfloat16` peak flops + flops_achieved = flops_per_iter * (1.0 / dt) # per second + flops_promised = 312e12 # A100 GPU `bfloat16` peak flops is 312 TFLOPS + mfu = flops_achieved / flops_promised + return mfu + + @torch.no_grad() + def generate(self, + idx, + graph_emb, + max_new_tokens, + temperature=1.0, + top_k=None): + """ + Take a conditioning sequence of indices `idx` (`LongTensor` of shape (b,t)) and complete + the sequence max_new_tokens times, feeding the predictions back into the model each time. + Most likely you'll want to make sure to be in `model.eval()` mode of operation for this. + """ + for _ in range(max_new_tokens): + # if the sequence context is growing too long we must crop it at block_size + idx_cond = idx if idx.size( + 1) <= self.config.block_size else idx[:, + -self.config.block_size:] + # forward the model to get the `logits` for the index in the sequence + logits, _ = self(idx_cond, graph_emb) + # pluck the `logits` at the final step and scale by desired temperature + logits = logits[:, -1, :] / temperature + # optionally crop the `logits` to only the top k options + if top_k is not None: + v, _ = torch.topk(logits, min(top_k, logits.size(-1))) + logits[logits < v[:, [-1]]] = -float('Inf') + # apply `softmax` to convert `logits` to (normalized) probabilities + probs = F.softmax(logits, dim=-1) + # sample from the distribution + idx_next = torch.multinomial(probs, num_samples=1) + # append sampled index to the running sequence and continue + idx = torch.cat((idx, idx_next), dim=1) + + return idx diff --git a/docs/sphinx/applications/python/nanoGPT/out-save_dir_3k.zip b/docs/sphinx/applications/python/nanoGPT/out-save_dir_3k.zip new file mode 100644 index 00000000000..5428631a039 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/out-save_dir_3k.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9484152545e55c8bf86ae83ab5d6a0fa2c5936b338ec3dee49c2a9ac49da2579 +size 127808579 diff --git a/docs/sphinx/applications/python/nanoGPT/sample.py b/docs/sphinx/applications/python/nanoGPT/sample.py new file mode 100644 index 00000000000..5550bf4ae8a --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/sample.py @@ -0,0 +1,102 @@ +""" +Sample from a trained model +""" +import os +import pickle +from contextlib import nullcontext +import torch +import tiktoken +from model import GPTConfig, GPT + +# ----------------------------------------------------------------------------- +init_from = 'resume' # either 'resume' (from an `out_dir`) or a gpt2 variant (e.g. '`gpt2-xl`') +out_dir = 'out' # ignored if `init_from` is not 'resume' +start = "\n" # `or "<|endoftext|>" or etc. Can also specify a file, use as: "FILE:prompt.txt"` +num_samples = 10 # number of samples to draw +max_new_tokens = 500 # number of tokens generated in each sample +temperature = 0.8 # 1.0 = no change, < 1.0 = less random, > 1.0 = more random, in predictions +top_k = 200 # retain only the top_k most likely tokens, clamp others to have 0 probability +seed = 1337 +device = 'cuda' # `examples: 'cpu', 'cuda', 'cuda:0', 'cuda:1', etc.` +dtype = 'bfloat16' if torch.cuda.is_available( +) and torch.cuda.is_bf16_supported( +) else 'float16' # 'float32' or '`bfloat16`' or 'float16' +compile = False # use PyTorch 2.0 to compile the model to be faster +exec(open( + 'configurator.py').read()) # overrides from command line or config file +# ----------------------------------------------------------------------------- + +torch.manual_seed(seed) +torch.cuda.manual_seed(seed) +torch.backends.cuda.matmul.allow_tf32 = True # `allow tf32 on matmul` +torch.backends.cudnn.allow_tf32 = True # `allow tf32 on cudnn` +device_type = 'cuda' if 'cuda' in device else 'cpu' # for later use in `torch.autocast` +ptdtype = { + 'float32': torch.float32, + 'bfloat16': torch.bfloat16, + 'float16': torch.float16 +}[dtype] +ctx = nullcontext() if device_type == 'cpu' else torch.amp.autocast( + device_type=device_type, dtype=ptdtype) + +# model +if init_from == 'resume': + # `init` from a model saved in a specific directory + ckpt_path = os.path.join(out_dir, 'ckpt.pt') + checkpoint = torch.load(ckpt_path, map_location=device) + gptconf = GPTConfig(**checkpoint['model_args']) + model = GPT(gptconf) + state_dict = checkpoint['model'] + unwanted_prefix = '_orig_mod.' + for k, v in list(state_dict.items()): + if k.startswith(unwanted_prefix): + state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k) + model.load_state_dict(state_dict) +elif init_from.startswith('gpt2'): + # `init` from a given GPT-2 model + model = GPT.from_pretrained(init_from, dict(dropout=0.0)) + +model.eval() +model.to(device) +if compile: + model = torch.compile(model) # requires PyTorch 2.0 (optional) + +# look for the meta pickle in case it is available in the dataset folder +load_meta = False +if init_from == 'resume' and 'config' in checkpoint and 'dataset' in checkpoint[ + 'config']: # older checkpoints might not have these... + meta_path = os.path.join('data', checkpoint['config']['dataset'], + 'meta.pkl') + load_meta = os.path.exists(meta_path) +if load_meta: + print(f"Loading meta from {meta_path}...") + with open(meta_path, 'rb') as f: + meta = pickle.load(f) + # TODO want to make this more general to arbitrary encoder/decoder schemes + stoi, itos = meta['stoi'], meta['itos'] + encode = lambda s: [stoi[c] for c in s] + decode = lambda l: ''.join([itos[i] for i in l]) +else: + # `ok` let's assume gpt-2 `encodings` by default + print("No meta.pkl found, assuming GPT-2 encodings...") + enc = tiktoken.get_encoding("gpt2") + encode = lambda s: enc.encode(s, allowed_special={"<|endoftext|>"}) + decode = lambda l: enc.decode(l) + +# encode the beginning of the prompt +if start.startswith('FILE:'): + with open(start[5:], 'r', encoding='utf-8') as f: + start = f.read() +start_ids = encode(start) +x = (torch.tensor(start_ids, dtype=torch.long, device=device)[None, ...]) + +# run generation +with torch.no_grad(): + with ctx: + for k in range(num_samples): + y = model.generate(x, + max_new_tokens, + temperature=temperature, + top_k=top_k) + print(decode(y[0].tolist())) + print('---------------') diff --git a/docs/sphinx/applications/python/nanoGPT/scaling_laws.ipynb b/docs/sphinx/applications/python/nanoGPT/scaling_laws.ipynb new file mode 100644 index 00000000000..946ea831dc7 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/scaling_laws.ipynb @@ -0,0 +1,792 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Reproducing some scaling laws results from [Chinchilla](https://arxiv.org/pdf/2203.15556.pdf). Can't get the numbers to match exactly, but can still be used as a rough guide to help determine compute-optimal models. Also contains related utilities for calculating flops and param counts." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "%matplotlib inline" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## params\n", + "\n", + "First some parameter calculations:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "123.653376" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def gpt_params(seq_len, vocab_size, d_model, num_heads, num_layers):\n", + " \"\"\" Given GPT config calculate total number of parameters \"\"\"\n", + " ffw_size = 4*d_model # in GPT the number of intermediate features is always 4*d_model\n", + " # token and position embeddings\n", + " embeddings = d_model * vocab_size + d_model * seq_len\n", + " # transformer blocks\n", + " attention = 3*d_model**2 + 3*d_model # weights and biases\n", + " attproj = d_model**2 + d_model\n", + " ffw = d_model*(ffw_size) + ffw_size\n", + " ffwproj = ffw_size*d_model + d_model\n", + " layernorms = 2*2*d_model\n", + " # dense\n", + " ln_f = 2*d_model\n", + " dense = d_model*vocab_size # note: no bias here\n", + " # note: embeddings are not included in the param count!\n", + " total_params = num_layers*(attention + attproj + ffw + ffwproj + layernorms) + ln_f + dense\n", + " return total_params\n", + "\n", + "gpt2 = dict(seq_len = 1024, vocab_size = 50257, d_model = 768, num_heads = 12, num_layers = 12)\n", + "gpt_params(**gpt2)/1e6" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "OpenAI reports gpt2 (small) as having 124M params, so this is a match. Also, loading the OpenAI weights into nanoGPT and then calling `model.parameters()` exactly matches the above number and verifies the implementation. Now Chinchilla parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def chinchilla_params(seq_len, vocab_size, d_model, num_heads, num_layers, ffw_size):\n", + " \"\"\" Parameters in the Chinchilla models. Unlike GPT they use relative positional embeddings. \"\"\"\n", + " # token embeddings only\n", + " embeddings = d_model * vocab_size\n", + " # transformer blocks\n", + " attention = 3*d_model**2 + 3*d_model # weights and biases\n", + " relative_pos = d_model**2 + 2*d_model # relative keys, content bias, relative bias\n", + " attproj = d_model**2 + d_model\n", + " ffw = d_model*ffw_size + ffw_size\n", + " ffwproj = ffw_size*d_model + d_model\n", + " layernorms = 2*2*d_model\n", + " # dense\n", + " ln_f = 2*d_model\n", + " dense = d_model*vocab_size # note: no bias here\n", + " # note: embeddings are not included in the param count!\n", + " total_params = num_layers*(attention + relative_pos + attproj + ffw + ffwproj + layernorms) + ln_f + dense\n", + " return total_params\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[44000000.0, 512, 2048, 64, 8, 8]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load in all the 50 Chinchilla models on the last page of the paper\n", + "import json\n", + "chinchilla_models_txt = '[[44000000.0, 512, 2048, 64, 8, 8], [57000000.0, 576, 2304, 64, 9, 9], [74000000.0, 640, 2560, 64, 10, 10], [90000000.0, 640, 2560, 64, 10, 13], [106000000.0, 640, 2560, 64, 10, 16], [117000000.0, 768, 3072, 64, 12, 12], [140000000.0, 768, 3072, 64, 12, 15], [163000000.0, 768, 3072, 64, 12, 18], [175000000.0, 896, 3584, 64, 14, 14], [196000000.0, 896, 3584, 64, 14, 16], [217000000.0, 896, 3584, 64, 14, 18], [251000000.0, 1024, 4096, 64, 16, 16], [278000000.0, 1024, 4096, 64, 16, 18], [306000000.0, 1024, 4096, 64, 16, 20], [425000000.0, 1280, 5120, 128, 10, 18], [489000000.0, 1280, 5120, 128, 10, 21], [509000000.0, 1408, 5632, 128, 11, 18], [552000000.0, 1280, 5120, 128, 10, 24], [587000000.0, 1408, 5632, 128, 11, 21], [632000000.0, 1536, 6144, 128, 12, 19], [664000000.0, 1408, 5632, 128, 11, 24], [724000000.0, 1536, 6144, 128, 12, 22], [816000000.0, 1536, 6144, 128, 12, 25], [893000000.0, 1792, 7168, 128, 14, 20], [1018000000.0, 1792, 7168, 128, 14, 23], [1143000000.0, 1792, 7168, 128, 14, 26], [1266000000.0, 2048, 8192, 128, 16, 22], [1424000000.0, 2176, 8704, 128, 17, 22], [1429000000.0, 2048, 8192, 128, 16, 25], [1593000000.0, 2048, 8192, 128, 16, 28], [1609000000.0, 2176, 8704, 128, 17, 25], [1731000000.0, 2304, 9216, 128, 18, 24], [1794000000.0, 2176, 8704, 128, 17, 28], [2007000000.0, 2304, 9216, 128, 18, 28], [2283000000.0, 2304, 9216, 128, 18, 32], [2298000000.0, 2560, 10240, 128, 20, 26], [2639000000.0, 2560, 10240, 128, 20, 30], [2980000000.0, 2560, 10240, 128, 20, 34], [3530000000.0, 2688, 10752, 128, 22, 36], [3802000000.0, 2816, 11264, 128, 22, 36], [4084000000.0, 2944, 11776, 128, 22, 36], [4516000000.0, 3072, 12288, 128, 24, 36], [6796000000.0, 3584, 14336, 128, 28, 40], [9293000000.0, 4096, 16384, 128, 32, 42], [11452000000.0, 4352, 17408, 128, 32, 47], [12295000000.0, 4608, 18432, 128, 36, 44], [12569000000.0, 4608, 18432, 128, 32, 47], [13735000000.0, 4864, 19456, 128, 32, 47], [14940000000.0, 4992, 19968, 128, 32, 49], [16183000000.0, 5120, 20480, 128, 40, 47]]'\n", + "chilchilla_models = json.loads(chinchilla_models_txt) # all 50 models\n", + "chilchilla_models[0] # tuples of params, d_model, ffw_size, kv_size, n_heads, n_layers from Table A9" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "our estimated params: 12296.1623M, chinchilla params: 12295.0000M, d_model: 4608, n_heads: 36, n_layers: 44\n", + "our estimated params: 13124.4826M, chinchilla params: 12569.0000M, d_model: 4608, n_heads: 32, n_layers: 47\n", + "our estimated params: 14614.4279M, chinchilla params: 13735.0000M, d_model: 4864, n_heads: 32, n_layers: 47\n", + "our estimated params: 16037.5039M, chinchilla params: 14940.0000M, d_model: 4992, n_heads: 32, n_layers: 49\n", + "our estimated params: 16184.4582M, chinchilla params: 16183.0000M, d_model: 5120, n_heads: 40, n_layers: 47\n" + ] + } + ], + "source": [ + "for m in chilchilla_models[-5:]: # only print last 5 models of the table\n", + " p, d, f, k, h, l = m\n", + " nparams = chinchilla_params(seq_len = 1024, vocab_size = 32000, d_model = d, num_heads = h, num_layers = l, ffw_size=f)\n", + " print(f\"our estimated params: {nparams/1e6:.4f}M, chinchilla params: {p/1e6:.4f}M, d_model: {d}, n_heads: {h}, n_layers: {l}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are almost able to reproduce the parameter counts for the Chinchilla models.\n", + "\n", + "Now turning to FLOPs:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## flops" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def chinchilla_flops(seq_len, vocab_size, d_model, num_heads, num_layers, ffw_size):\n", + " \"\"\" \n", + " Calculate total number of FLOPs, see Chinchilla \n", + " paper Appendix F as reference: https://arxiv.org/pdf/2203.15556.pdf\n", + " \"\"\" \n", + " key_size = d_model // num_heads\n", + "\n", + " # embeddings\n", + " embeddings = 2 * seq_len * vocab_size * d_model\n", + "\n", + " # attention\n", + " # key, query, value projections\n", + " attention = 2 * 3 * seq_len * d_model * (key_size * num_heads)\n", + " # key @ query logits\n", + " attlogits = 2 * seq_len * seq_len * (key_size * num_heads)\n", + " # softmax\n", + " attsoftmax = 3 * num_heads * seq_len * seq_len # 3* is for subtract (max), exp, divide (?)\n", + " # softmax @ value reductions\n", + " attvalue = 2 * seq_len * seq_len * (key_size * num_heads)\n", + " # final linear\n", + " attlinear = 2 * seq_len * (key_size * num_heads) * d_model\n", + " att = attention + attlogits + attsoftmax + attvalue + attlinear\n", + " # feed forward\n", + " dense = 2 * seq_len * (d_model * ffw_size + d_model * ffw_size)\n", + "\n", + " # logits\n", + " logits = 2 * seq_len * d_model * vocab_size\n", + " \n", + " # this is what you'd expect:\n", + " # forward_flops = embeddings + num_layers * (att + dense) + logits\n", + " # but:\n", + " # per author correspondence apparently there is typo in the paper,\n", + " # they do not count embeddings and logits to repro table 4. So instead:\n", + " forward_flops = num_layers * (att + dense)\n", + " backward_flops = 2 * forward_flops # as in Kaplan et al. 2020\n", + " total_flops = forward_flops + backward_flops\n", + "\n", + " return total_flops\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
seq_lenvocab_sized_modelnum_headsnum_layersffw_sizeNFapprox_flopschinch_flopsratio
020483200064010102560738252809298771968009071650406409.298772e+111.025036
1204832000102416204096305707008413524819968037565277143044.135248e+121.100817
2204832000128010245120552604160735345377280067903999180807.353454e+121.082919
3204832000179214267168114345369614670316437504140507590164481.467032e+131.044094
4204832000204816288192159312691220220437594112195763434946562.022044e+131.032902
52048320003584284014336679627468883021046743040835126233661448.302105e+130.994114
\n", + "
" + ], + "text/plain": [ + " seq_len vocab_size d_model num_heads num_layers ffw_size N \\\n", + "0 2048 32000 640 10 10 2560 73825280 \n", + "1 2048 32000 1024 16 20 4096 305707008 \n", + "2 2048 32000 1280 10 24 5120 552604160 \n", + "3 2048 32000 1792 14 26 7168 1143453696 \n", + "4 2048 32000 2048 16 28 8192 1593126912 \n", + "5 2048 32000 3584 28 40 14336 6796274688 \n", + "\n", + " F approx_flops chinch_flops ratio \n", + "0 929877196800 907165040640 9.298772e+11 1.025036 \n", + "1 4135248199680 3756527714304 4.135248e+12 1.100817 \n", + "2 7353453772800 6790399918080 7.353454e+12 1.082919 \n", + "3 14670316437504 14050759016448 1.467032e+13 1.044094 \n", + "4 20220437594112 19576343494656 2.022044e+13 1.032902 \n", + "5 83021046743040 83512623366144 8.302105e+13 0.994114 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Now try reproduce Table A4 from Chinchilla paper Appendix, \n", + "# comparing accurate flops above to approximate flops F = 6*N*D\n", + "# note Chinchilla mentions using vocab_size = 32K\n", + "\n", + "chilchilla_models_table4 = [\n", + " [10, 640, 2560, 10, 64],\n", + " [20, 1024, 4096, 16, 64],\n", + " [24, 1280, 5120, 10, 128 ],\n", + " [26, 1792, 7168, 14, 128 ],\n", + " [28, 2048, 8192, 16, 128],\n", + " [40, 3584, 14336, 28, 128]\n", + "]\n", + "\n", + "rows = []\n", + "for num_layers, d_model, ffw_size, num_heads, _ in chilchilla_models_table4:\n", + "\n", + " args = dict(seq_len = 2048, vocab_size = 32000, d_model = d_model, \n", + " num_heads = num_heads, num_layers = num_layers, ffw_size=ffw_size)\n", + "\n", + " D = args['seq_len'] # dataset size (cancels anyway, for the purposes of the ratio calculation below)\n", + " N = chinchilla_params(**args)\n", + " F = chinchilla_flops(**args)\n", + "\n", + " approx_flops = 6*D*N # approximate flops\n", + " chinch_flops = F * (float(D) / args['seq_len']) # exact flops according to Chinchilla paper calculations\n", + "\n", + " # print('---')\n", + " # print(f\"params: {N/1e6:.2f}M\")\n", + " # print(f\"approx flops: {approx_flops/1e9:.2f}B\")\n", + " # print(f\"chinchilla flops: {chinch_flops/1e9:.2f}B\")\n", + " # print(f\"ratio (chinchilla / approx): {chinch_flops / approx_flops:.2f}\")\n", + "\n", + " # first copy all keyvalues from args into out\n", + " out = {k:v for k,v in args.items()}\n", + " # then add the calculated values\n", + " out['N'] = N\n", + " out['F'] = F\n", + " out['approx_flops'] = approx_flops\n", + " out['chinch_flops'] = chinch_flops\n", + " out['ratio'] = chinch_flops / approx_flops\n", + " rows.append(out)\n", + "\n", + "# make a pandas dataframe from rows\n", + "df = pd.DataFrame(rows)\n", + "df" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pretty good match! Except the param counts are still not perfectly accurate." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scaling Laws: Approach 3\n", + "\n", + "In their \"Aproach 3\", Chinchilla paper fits a function L(N,D) to approximate the final loss gives the model size and the data size. Here is the final fit:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA8UAAAHWCAYAAABe7ytwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9eXwb9bnv//mOdslavMu7HTv74jgLIZAWKCkJUJYWKPTQS+FwoJcboJD7u72Hvs4BSin0dDvcnkPLgVOgO5SwUxpKA4QtqxMncRYn3ld5k619saT5/TGSLNkzskYayUu+b17fVxtp5pnvjO35zjPP83wewrIsCwqFQqFQKBQKhUKhUM5DmNmeAIVCoVAoFAqFQqFQKLMFdYopFAqFQqFQKBQKhXLeQp1iCoVCoVAoFAqFQqGct1CnmEKhUCgUCoVCoVAo5y3UKaZQKBQKhUKhUCgUynkLdYopFAqFQqFQKBQKhXLeQp1iCoVCoVAoFAqFQqGct1CnmEKhUCgUCoVCoVAo5y3UKaZQKBQKhUKhUCgUynkLdYoplAS8+OKLIISgs7NztqdCoVAoFMqCZiGuuT/5yU+waNEiyGQyrF27FgBQXV2N22+/fVbnRaFQ4qFOMYVCoVAoFArlvOFXv/oVbrrpJlRWVoIQktBBHR8fx913343CwkLodDpcdtllOHLkSFLH+dvf/obvfve7uPjii/HCCy/giSeekOgMKBSK1MhnewIUCoVCoVAoFEq2+Ld/+zc4HA5ccMEFGBgYENwuFArh6quvxrFjx/B//s//QUFBAX75y1/i0ksvRWNjIxYvXpzwOB988AEYhsGvf/1rKJVKqU+DQqFICHWKKRQKhUKhUCjnDXv37o1GiXNycgS327VrFz7//HO88soruPHGGwEAX//617FkyRI88sgj+OMf/5jwOENDQ9BoNNQhplDmATR9mkIRyS9/+UusXLkSKpUKpaWl2LFjB8bHx+O2OXfuHG644QaYzWao1WqUl5fjlltugc1mi27z/vvvY8uWLTCZTMjJycHSpUvxve99L8tnQ6FQKBTK3CaZdRcAnn76aSxatAgajQYXXHABPvnkE1x66aW49NJL47arqqoCIWTG4+7atQvFxcX42te+Fv2ssLAQX//61/Hmm2/C5/MJ7ksIwQsvvACXywVCCAghePHFFwW3b29vx0033YS8vDxotVpceOGF+Mtf/hK3zUcffQRCCF5++WV873vfg9lshk6nw7XXXouenp64bZN5DqFQKJPQSDGFIoJHH30U3//+97F161bcc889aGlpwa9+9SscOnQIn332GRQKBfx+P7Zt2wafz4f77rsPZrMZfX19eOeddzA+Pg6j0YiTJ0/iK1/5CtasWYPHHnsMKpUKra2t+Oyzz2b7FCkUCoVCmTMks+4CXJ3wvffeiy984Qt48MEH0dnZieuvvx65ubkoLy9P6dhHjx7FunXrwDDxMaQLLrgAzz77LM6ePYvVq1fz7vu73/0Ozz77LA4ePIj//u//BgBcdNFFvNsODg7ioosugtvtxv3334/8/Hz85je/wbXXXotdu3bhq1/9atz2P/zhD0EIwf/9v/8XQ0NDeOqpp7B161Y0NTVBo9Ek9RxCoVCmwFIoFEFeeOEFFgDb0dHBDg0NsUqlkr3iiivYYDAY3eY///M/WQDs888/z7Isyx49epQFwL7yyiuCdv/93/+dBcAODw9n/BwoFAqFQpkPxK65LMsmve76fD42Pz+f3bhxIzsxMRHd7sUXX2QBsJdccongMXU6Hfutb31L8Lt//Md/nPb5X/7yFxYAu3v37oTn861vfYvV6XTTPq+qqoo75gMPPMACYD/55JPoZw6Hg62pqWGrq6uj5/7hhx+yANiysjLWbrdHt/3zn//MAmD/3//7fyzLJvccQqFQ4qHp0xRKkvz973+H3+/HAw88EPfW+K677oLBYIimOUXewL733ntwu928tkwmEwDgzTffRCgUyuzEKRQKhUKZhyS77h4+fBijo6O46667IJdPJkHeeuutyM3NTfn4Ho8HKpVq2udqtTr6vRS8++67uOCCC7Bly5boZzk5Obj77rvR2dmJU6dOxW1/2223Qa/XR/994403oqSkBO+++y6A5J5DKBRKPNQpplCSpKurCwCwdOnSuM+VSiUWLVoU/b6mpgY7d+7Ef//3f6OgoADbtm3D008/HVfHc/PNN+Piiy/GP/3TP6G4uBi33HIL/vznP1MHmUKhUCiUMMmuu5H/rauri9tOLpejuro65eNrNBreumGv1xv9Xgq6urqmnSMALF++PPp9LFNVrwkhqKuri/Z3TuY5hEKhxEOdYgolA/zsZz/D8ePH8b3vfQ8ejwf3338/Vq5cid7eXgDcQvrxxx/j73//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EhISEhK6YCWgWEhISEhISEhISEhISOiC1QUJxdFolPvuu4+uri6sVitXXnklr7766qzH7N69m0suuQSz2czq1at59NFHz0yyGqo2/927dyNJUtmtUOJkqfXcc8/xtre9jdbWViRJ4kc/+lHJ44qi8OlPf5qWlhasViu7du3i+PHjc8b9+te/Tnd3NxaLhZ07d/LKK6+cM/k/+OCDZX+P9evXn5X8f/CDH/CWt7yFuro6JEkq1nGdS0888QTr16/HYrGwZcsWfvrTn9Y+eZYm/0cffbTs9bdYLGc8/0wmwyc/+Um2bNmC3W6ntbWV9773vQwNDc0Z90y9/4WEzlWJsV6M9Wc7fzHWz19irNeWGOvPnC5IKP7gBz/IL37xC7797W+zf/9+3vKWt7Br1y4GBwc1+/f09HD77bdzww03sHfvXu677z4++MEP8rOf/ewMZ66q2vwLOnr0KMPDw8VbY2PjGck3Ho+zdetWvv71r2s+/sUvfpG///u/5xvf+AYvv/wydrudW265BVmWK8b8/ve/z8c+9jEeeOAB3njjDbZu3cott9zC2NjYOZE/wKZNm0r+Hr/5zW9qnjvMnX88Hufqq6/mC1/4wrxjvvDCC9x999184AMfYM+ePdx5553ceeedHDhwoFZpl+RX6/wBXC5Xyevf29tbi3Q186uUfyKR4I033uAv//IveeONN/jBD37A0aNHefvb3z5rzDP5/hcSOlclxnox1p/t/EGM9fOVGOvLJcb6M6yqCjidB0okEoper1eefPLJkvZLLrlE+d//+39rHvOJT3xC2bRpU0nbXXfdpdxyyy1LlmclLST/Qs2wYDB4BjKcXYDywx/+sPh7Pp9XmpublS996UvFtlAopJjNZuW73/1uxTg7duxQPvShDxV/z+VySmtrq/LQQw8tSd4F1Sr/Bx54QNm6desSZqqtmflPV09PjwIoe/bsmTPO7/3e7ym33357SdvOnTuVP/qjP6pBlpVVq/wfeeQRxe121zS3+Wi2/At65ZVXFEDp7e2t2Odsvf+FhM4VibH+7EqM9arEWL8wibFelRjrz6wuuJnibDZLLpcrWz5htVorfnv34osvsmvXrpK2W265hRdffHHJ8qykheRf0LZt22hpaeHmm2/m+eefX8o0562enh5GRkZKXl+3283OnTsrvr7pdJrXX3+95BidTseuXbvO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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def L(N, D):\n", + " \"\"\" \n", + " Approximates loss given N parameters and D dataset size (in tokens),\n", + " per Chinchilla paper.\n", + " \"\"\"\n", + " E = 1.69 # entropy of natural language, limit of infinite model on infinite data\n", + " A = 406.4\n", + " B = 410.7\n", + " alpha = 0.34\n", + " beta = 0.28\n", + " return A / (N ** alpha) + B / (D ** beta) + E\n", + "\n", + "ns = 10 ** np.arange(7, 11, step=2**-4) # model sizes from 10M to 100B\n", + "ds = 10 ** np.arange(9, 12, step=2**-4) # dataset sizes from 1B to 1T\n", + "plt.figure(figsize=(12, 5))\n", + "plt.subplot(121)\n", + "# create a 2D countour plot of loss L as a function of model size and dataset size in ns,ds\n", + "loss2d = np.log10(np.array([[L(n, d) for d in ds] for n in ns]))\n", + "plt.imshow(loss2d, extent=[9, 12, 7, 11], origin='lower', alpha=0.5)\n", + "plt.contour(loss2d, levels=30, extent=[9, 12, 7, 11], origin='lower')\n", + "plt.xlabel('log10(dataset size)')\n", + "plt.ylabel('log10(model size)')\n", + "plt.title('loss')\n", + "plt.colorbar()\n", + "# plot the compute for each point, which is a deterministic function: flops = 6*N*D\n", + "plt.subplot(122)\n", + "compute2d = np.log10(np.array([[6*n*d for d in ds] for n in ns]))\n", + "plt.imshow(compute2d, extent=[9, 12, 7, 11], origin='lower', alpha=0.5)\n", + "plt.contour(compute2d, levels=30, extent=[9, 12, 7, 11], origin='lower')\n", + "plt.xlabel('log10(dataset size)')\n", + "plt.ylabel('log10(model size)')\n", + "plt.title('log10 flops')\n", + "plt.colorbar()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ok so given any N,D we can estimate both: 1) the loss, and 2) the total flops. Now we want to solve the following problem: Given a specific budget of flops C, find: N_opt, D_opt = argmin_{FLOPs(N,D) = C} L(N, D). i.e. how big of a model should we train and for how many tokens?" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "best model size: 316.23M\n", + "best dataset size: 11.65B\n" + ] + }, + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'loss')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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x5Tp06crnp6eytChQ5VPP/1UOXbsmPLEE08oYWFhSkVFhaIoipKXl6f4+/srTz75pHLkyBFlyZIlSqtWrZTXXnvN+rpPPPGEEhsbq6xbt045cOCAMmLECEWn0ymTJ0+2HvPoo48q6enpyubNm5Xs7Gxl5syZilarVY4dO6YoiqLMmzdPCQoKarT2mTNnKjExMcrmzZuV06dPKz/88IMyf/586+OAsmTJEkVRFKW8vFwpKCiwbu+8847i7++vZGVlKYqiKG+++aaSkJCgfP/998qJEyeUefPmKVqtVtm4caNtftHimiS8xA3r37+/0rdvX+v3dXV1SkBAgPLQQw9Z9xUUFCiAkpmZqSiKorzyyitKfHy8YjabrcfMmTNHCQwMVEwmk1JeXq74+PgoixYtsj5eUlKi+Pn5WcPrzJkziqenp5Kfn9+gnrvvvluZNm2aoijXD69nnnlGueuuuxrUcaUrw+tKmZmZiq+vr7Jw4UJFURSlqqpK8ff3V7Zu3drguIkTJypjx45t9P2F7Uifl2iWpKQk69eenp6EhYVx6623WvfVr4JTv2DqkSNHSEtLQ6PRWI/p06cPRqORvLw8SktLqampoVevXtbHQ0NDGyy9lpWVhclkonPnzg1qqa6ubvJqOQ8//DCDBg0iPj6eIUOGMGLECO65555rPicnJ4fRo0fz4osvcv/99wOQnZ1NZWUlgwYNanBsTU0NKSkpTapF3BwJL9Es3t7eDb7XaDQN9tWHlNlsttl7Go1GPD092b17N56eng0eCwwMbNJr9OjRg1OnTrFq1SrWrVvH/fffz8CBAxvtq6qoqGDkyJGkpaXxxhtvNKgFYMWKFURFRTV4jlarvZEfSzSThJewiy5duvDtt9+iKIo12LZs2YJOpyM6OprQ0FC8vb3Zvn07sbGxAJSWlnLs2DH69+8PQEpKCiaTieLiYu64445m16LX63nggQd44IEHuO+++xgyZAgXLlwgNDS0wXGKovDggw9iNpv55z//2aDVmJiYiFarJScnx1qfsC8JL2EXTz75JO+//z7PPPMMTz/9NEePHuW1115jypQpeHh4EBgYyMSJE5k6dSphYWG0adOGP//5z3h4/HxBvHPnzowbN47x48cza9YsUlJSOHfuHOvXrycpKYnhw4dft453332XyMhI68K633zzDREREVcd2Pr666+zbt061qxZg9FotLa2goKC0Ol0vPjiizz//POYzWb69u1LWVkZW7ZsQa/XM2HCBJv97sTVSXgJu4iKimLlypVMnTqV5ORkQkNDmThxIq+++qr1mJkzZ2I0Grn33nvR6XS88MILlJWVNXidefPm8eabb/LCCy+Qn59Pq1at6N27NyNGjGhSHTqdjr/97W8cP34cT09Pbr/9dlauXNkgJOtt2rQJo9FIenr6r2p4+OGHmT59Oq1btyYjI4OTJ08SHBxMjx49eOWVV5rxGxI3ShbgEEI4JRmkKoRwShJeQginJOElhHBKEl5CCKck4SWEcEoSXkIIpyThJYRwShJeQginJOElhHBKEl5CCKck4SWEcEr/HzrztTTmmeQlAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "c = 2.21e19 # target compute budget (usually know this because we know how many GPU for how long go brrr)\n", + "# (I got this flop number from row 1 of Table A3)\n", + "# sweep model sizes from 10M to 100B\n", + "ns = 10 ** np.arange(7, 11, step=2**-4)\n", + "# using C = 6*N*D, solve for D that maintains the compute budget c\n", + "ds = c / (6 * ns)\n", + "# evaluate the loss in each case\n", + "losses = L(ns, ds)\n", + "# find the argmin\n", + "best = np.argmin(losses)\n", + "print(f\"best model size: {ns[best]/1e6:.2f}M\")\n", + "print(f\"best dataset size: {ds[best]/1e9:.2f}B\")\n", + "# plot the loss\n", + "plt.figure(figsize=(3,3))\n", + "plt.plot(ns, losses)\n", + "plt.xscale('log')\n", + "# plot a vertical bar at the best model size\n", + "plt.axvline(ns[best], color='red')\n", + "plt.xlabel('model size')\n", + "plt.ylabel('loss')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the plot above, basically the models on the left of best are too small and trained for too long. The models on the right of best are way too large and trained for too little. The model at the red line is just right.\n", + "\n", + "Now, the Chinchilla paper says that best model size for this flop budget is 400M params and 9.2B tokens (instead of 316M params and 11.65B tokens) so there is some unresolved disagreement here too..." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2304" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Calculate the Chinchilla optimal models for a range of compute budgets\n", + "\n", + "# sweep over compute budgets from 1e17 to 1e26\n", + "cs = 10 ** np.arange(17, 26, step=2**-8)\n", + "models = []\n", + "for c in cs:\n", + " # sweep over model sizes\n", + " ns = 10 ** np.arange(7, 14, step=2**-8)\n", + " # the dataset sizes that would maintain the given compute budget\n", + " ds = c / (6 * ns)\n", + " # losses at each point\n", + " losses = L(ns, ds)\n", + " # n,d for the best model\n", + " best = np.argmin(losses)\n", + " models.append((c, ns[best], ds[best])) # c, n, d tuple log\n", + "\n", + "len(models)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "closest model found:\n", + "model size: 399.54M\n", + "dataset size: 14.43B\n", + "flops: 3.459892e+19\n", + "loss: 2.76\n" + ] + } + ], + "source": [ + "query_model_size = 400e6\n", + "ns = np.array([n for c, n, d in models])\n", + "ds = np.array([d for c, n, d in models])\n", + "# find the index of the closest model size in ns\n", + "ix = np.argmin(np.abs(ns - query_model_size))\n", + "# retrieve the corresponding params, flops, and data size\n", + "print(\"closest model found:\")\n", + "print(f\"model size: {ns[ix]/1e6:.2f}M\")\n", + "print(f\"dataset size: {ds[ix]/1e9:.2f}B\")\n", + "print(f\"flops: {6*ns[ix]*ds[ix]:e}\")\n", + "print(f\"loss: {L(ns[ix], ds[ix]):.2f}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This should have come out as 9.2B according to Table A3 in Chinchilla paper, per my understanding of it." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scaling Laws: Approach 2\n", + "\n", + "Approach 2 is probably my favorite one because it fixes a flop budget and runs a number of model/dataset sizes, measures the loss, fits a parabolla, and gets the minimum. So it's a fairly direct measurement of what we're after. The best way to then calculate the compute-optimal number of tokens for any given model size, as an example, is via simple interpolation." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Approach 1 numbers\n", + "# # parameters, tokens\n", + "# raw = [\n", + "# [400e6, 8e9],\n", + "# [1e9, 20.2e9],\n", + "# [10e9, 205.1e9],\n", + "# [67e9, 1.5e12],\n", + "# [175e9, 3.7e12],\n", + "# [280e9, 5.9e12],\n", + "# [520e9, 11e12],\n", + "# [1e12, 21.2e12],\n", + "# [10e12, 216.2e12],\n", + "# ]\n", + "\n", + "# Approach 2 numbers\n", + "# parameters, tokens\n", + "raw = [\n", + " [400e6, 7.7e9],\n", + " [1e9, 20.0e9],\n", + " [10e9, 219.5e9],\n", + " [67e9, 1.7e12],\n", + " [175e9, 4.3e12],\n", + " [280e9, 7.1e12],\n", + " [520e9, 13.4e12],\n", + " [1e12, 26.5e12],\n", + " [10e12, 292.0e12],\n", + "]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "y = 1.0409573169995892x + 0.9353887152390791\n" + ] + } + ], + "source": [ + "# fit a line by linear regression to the raw data\n", + "import numpy as np\n", + "x = np.array([np.log10(x[0]) for x in raw])\n", + "y = np.array([np.log10(x[1]) for x in raw])\n", + "A = np.vstack([x, np.ones(len(x))]).T\n", + "m, c = np.linalg.lstsq(A, y, rcond=None)[0]\n", + "print(f\"y = {m}x + {c}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(3, 3))\n", + "# plot the line\n", + "plt.plot([q[0] for q in raw], [10**(m*np.log10(q[0]) + c) for q in raw], label='linear regression', color='r')\n", + "# plot the raw data\n", + "plt.scatter([q[0] for q in raw], [q[1] for q in raw], label='raw data')\n", + "plt.xscale('log')\n", + "plt.yscale('log')\n", + "plt.xlabel('parameters')\n", + "plt.ylabel('tokens')\n", + "plt.title('compute optimal models')\n", + "plt.grid()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "predicted parameters for 1.240000e+08 tokens: 2.292426e+09\n" + ] + } + ], + "source": [ + "xquery = 124e6 # query model size here (e.g. GPT-2 small is 124M)\n", + "yquery = 10**(m*np.log10(xquery) + c)\n", + "print(f\"predicted parameters for {xquery:e} tokens: {yquery:e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pytorch2", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7f5833218766b48e6e35e4452ee875aac0e2188d05bbe5298f2c62b79f08b222" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/sphinx/applications/python/nanoGPT/train.py b/docs/sphinx/applications/python/nanoGPT/train.py new file mode 100644 index 00000000000..b9e68fcdf5d --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/train.py @@ -0,0 +1,383 @@ +""" +`This training script can be run both on a single gpu in debug mode,` +`and also in a larger training run with distributed data parallel (ddp).` + +`To run on a single GPU, example:` +`$ python train.py --batch_size=32 --compile=False` + +`To run with DDP on 4 gpus on 1 node, example:` +`$ torchrun --standalone --nproc_per_node=4 train.py` + +`To run with DDP on 4 gpus across 2 nodes, example:` +`- Run on the first (master) node with example IP 123.456.123.456:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=0 --master_addr=123.456.123.456 --master_port=1234 train.py` +`- Run on the worker node:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=1 --master_addr=123.456.123.456 --master_port=1234 train.py` +`(If your cluster does not have Infiniband interconnect prepend NCCL_IB_DISABLE=1)` +""" + +import os +import time +import math +import pickle +from contextlib import nullcontext + +import numpy as np +import torch +from torch.nn.parallel import DistributedDataParallel as DDP +from torch.distributed import init_process_group, destroy_process_group + +from model import GPTConfig, GPT + +# ----------------------------------------------------------------------------- +# default config values designed to train a gpt2 (124M) on OpenWebText +# I/O +out_dir = 'out' +eval_interval = 2000 +log_interval = 1 +eval_iters = 200 +eval_only = False # if True, script exits right after the first `eval` +always_save_checkpoint = True # if True, always save a checkpoint after each `eval` +init_from = 'scratch' # 'scratch' or 'resume' or 'gpt2*' +# `wandb` logging +wandb_log = False # disabled by default +wandb_project = 'owt' +wandb_run_name = 'gpt2' # `'run' + str(time.time())` +# data +dataset = 'openwebtext' +gradient_accumulation_steps = 5 * 8 # used to simulate larger batch sizes +batch_size = 12 # if gradient_accumulation_steps > 1, this is the micro-batch size +block_size = 1024 +# model +n_layer = 12 +n_head = 12 +n_embd = 768 +dropout = 0.0 # for `pretraining` 0 is good, for finetuning try 0.1+ +bias = False # do we use bias inside LayerNorm and Linear layers? +# `adamw` optimizer +learning_rate = 6e-4 # max learning rate +max_iters = 600000 # total number of training iterations +weight_decay = 1e-1 +beta1 = 0.9 +beta2 = 0.95 +grad_clip = 1.0 # clip gradients at this value, or disable if == 0.0 +# learning rate decay settings +decay_lr = True # whether to decay the learning rate +warmup_iters = 2000 # how many steps to warm up for +lr_decay_iters = 600000 # `should be ~= max_iters per Chinchilla` +min_lr = 6e-5 # minimum learning rate, should be ~= learning_rate/10 per Chinchilla +# DDP settings +backend = 'nccl' # `'nccl', 'gloo', etc.` +# system +device = 'cuda' # examples: `'cpu', 'cuda', 'cuda:0', 'cuda:1' etc., or try 'mps' on macbooks` +dtype = 'bfloat16' if torch.cuda.is_available( +) and torch.cuda.is_bf16_supported( +) else 'float16' # 'float32', '`bfloat16`', or 'float16', the latter will auto implement a GradScaler +compile = True # use PyTorch 2.0 to compile the model to be faster +# ----------------------------------------------------------------------------- +config_keys = [ + k for k, v in globals().items() + if not k.startswith('_') and isinstance(v, (int, float, bool, str)) +] +exec(open( + 'configurator.py').read()) # overrides from command line or config file +config = {k: globals()[k] for k in config_keys} # will be useful for logging +# ----------------------------------------------------------------------------- + +# various `inits`, derived attributes, I/O setup +ddp = int(os.environ.get('RANK', -1)) != -1 # is this a `ddp` run? +if ddp: + init_process_group(backend=backend) + ddp_rank = int(os.environ['RANK']) + ddp_local_rank = int(os.environ['LOCAL_RANK']) + ddp_world_size = int(os.environ['WORLD_SIZE']) + device = f'cuda:{ddp_local_rank}' + torch.cuda.set_device(device) + master_process = ddp_rank == 0 # this process will do logging, `checkpointing` etc. + seed_offset = ddp_rank # each process gets a different seed + # world_size number of processes will be training simultaneously, so we can scale + # down the desired gradient accumulation iterations per process proportionally + assert gradient_accumulation_steps % ddp_world_size == 0 + gradient_accumulation_steps //= ddp_world_size +else: + # if not `ddp`, we are running on a single `gpu`, and one process + master_process = True + seed_offset = 0 + ddp_world_size = 1 +tokens_per_iter = gradient_accumulation_steps * ddp_world_size * batch_size * block_size +print(f"tokens per iteration will be: {tokens_per_iter:,}") + +if master_process: + os.makedirs(out_dir, exist_ok=True) +torch.manual_seed(1337 + seed_offset) +torch.backends.cuda.matmul.allow_tf32 = True # `allow tf32 on matmul` +torch.backends.cudnn.allow_tf32 = True # `allow tf32 on cudnn` +device_type = 'cuda' if 'cuda' in device else 'cpu' # for later use in `torch.autocast` +# note: float16 data type will automatically use a GradScaler +ptdtype = { + 'float32': torch.float32, + 'bfloat16': torch.bfloat16, + 'float16': torch.float16 +}[dtype] +ctx = nullcontext() if device_type == 'cpu' else torch.amp.autocast( + device_type=device_type, dtype=ptdtype) + +# poor man's data loader +data_dir = os.path.join('data', dataset) + + +def get_batch(split): + # We recreate `np.memmap` every batch to avoid a memory leak, as per + # https://stackoverflow.com/questions/45132940/numpy-memmap-memory-usage-want-to-iterate-once/61472122#61472122 + if split == 'train': + data = np.memmap(os.path.join(data_dir, 'train.bin'), + dtype=np.uint16, + mode='r') + else: + data = np.memmap(os.path.join(data_dir, 'val.bin'), + dtype=np.uint16, + mode='r') + ix = torch.randint(len(data) - block_size, (batch_size,)) + x = torch.stack([ + torch.from_numpy((data[i:i + block_size]).astype(np.int64)) for i in ix + ]) + y = torch.stack([ + torch.from_numpy((data[i + 1:i + 1 + block_size]).astype(np.int64)) + for i in ix + ]) + if device_type == 'cuda': + # pin arrays x,y, which allows us to move them to GPU asynchronously (non_blocking=True) + x, y = x.pin_memory().to(device, non_blocking=True), y.pin_memory().to( + device, non_blocking=True) + else: + x, y = x.to(device), y.to(device) + return x, y + + +# `init` these up here, can override if `init_from='resume'` (i.e. from a checkpoint) +iter_num = 0 +best_val_loss = 1e9 + +# attempt to derive vocab_size from the dataset +meta_path = os.path.join(data_dir, 'meta.pkl') +meta_vocab_size = None +if os.path.exists(meta_path): + with open(meta_path, 'rb') as f: + meta = pickle.load(f) + meta_vocab_size = meta['vocab_size'] + print(f"found vocab_size = {meta_vocab_size} (inside {meta_path})") + +# model `init` +model_args = dict(n_layer=n_layer, + n_head=n_head, + n_embd=n_embd, + block_size=block_size, + bias=bias, + vocab_size=None, + dropout=dropout) # start with `model_args` from command line +if init_from == 'scratch': + # `init` a new model from scratch + print("Initializing a new model from scratch") + # determine the vocab size we'll use for from-scratch training + if meta_vocab_size is None: + print( + "defaulting to vocab_size of GPT-2 to 50304 (50257 rounded up for efficiency)" + ) + model_args[ + 'vocab_size'] = meta_vocab_size if meta_vocab_size is not None else 50304 + gptconf = GPTConfig(**model_args) + model = GPT(gptconf) +elif init_from == 'resume': + print(f"Resuming training from {out_dir}") + # resume training from a checkpoint. + ckpt_path = os.path.join(out_dir, 'ckpt.pt') + checkpoint = torch.load(ckpt_path, map_location=device) + checkpoint_model_args = checkpoint['model_args'] + # force these config attributes to be equal otherwise we can't even resume training + # the rest of the attributes (e.g. dropout) can stay as desired from command line + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = checkpoint_model_args[k] + # create the model + gptconf = GPTConfig(**model_args) + model = GPT(gptconf) + state_dict = checkpoint['model'] + # fix the keys of the state dictionary :( + # honestly no idea how checkpoints sometimes get this prefix, have to debug more + unwanted_prefix = '_orig_mod.' + for k, v in list(state_dict.items()): + if k.startswith(unwanted_prefix): + state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k) + model.load_state_dict(state_dict) + iter_num = checkpoint['iter_num'] + best_val_loss = checkpoint['best_val_loss'] +elif init_from.startswith('gpt2'): + print(f"Initializing from OpenAI GPT-2 weights: {init_from}") + # initialize from OpenAI GPT-2 weights + override_args = dict(dropout=dropout) + model = GPT.from_pretrained(init_from, override_args) + # read off the created config `params`, so we can store them into checkpoint correctly + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = getattr(model.config, k) +# crop down the model block size if desired, using model surgery +if block_size < model.config.block_size: + model.crop_block_size(block_size) + model_args[ + 'block_size'] = block_size # so that the checkpoint will have the right value +model.to(device) + +# initialize a GradScaler. If enabled=False `scaler` is a no-op +scaler = torch.cuda.amp.GradScaler(enabled=(dtype == 'float16')) + +# optimizer +optimizer = model.configure_optimizers(weight_decay, learning_rate, + (beta1, beta2), device_type) +if init_from == 'resume': + optimizer.load_state_dict(checkpoint['optimizer']) +checkpoint = None # free up memory + +# compile the model +if compile: + print("compiling the model... (takes a ~minute)") + unoptimized_model = model + model = torch.compile(model) # requires PyTorch 2.0 + +# wrap model into DDP container +if ddp: + model = DDP(model, device_ids=[ddp_local_rank]) + + +# helps estimate an arbitrarily accurate loss over either split using many batches +@torch.no_grad() +def estimate_loss(): + out = {} + model.eval() + for split in ['train', 'val']: + losses = torch.zeros(eval_iters) + for k in range(eval_iters): + X, Y = get_batch(split) + with ctx: + logits, loss = model(X, Y) + losses[k] = loss.item() + out[split] = losses.mean() + model.train() + return out + + +# learning rate decay scheduler (cosine with `warmup`) +def get_lr(it): + # 1) linear `warmup` for `warmup_iters` steps + if it < warmup_iters: + return learning_rate * (it + 1) / (warmup_iters + 1) + # 2) if `it > lr_decay_iters`, return min learning rate + if it > lr_decay_iters: + return min_lr + # 3) in between, use cosine decay down to min learning rate + decay_ratio = (it - warmup_iters) / (lr_decay_iters - warmup_iters) + assert 0 <= decay_ratio <= 1 + coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio)) # `coeff` ranges 0..1 + return min_lr + coeff * (learning_rate - min_lr) + + +# logging +if wandb_log and master_process: + import wandb + wandb.init(project=wandb_project, name=wandb_run_name, config=config) + +# training loop +X, Y = get_batch('train') # fetch the very first batch +t0 = time.time() +local_iter_num = 0 # number of iterations in the lifetime of this process +raw_model = model.module if ddp else model # unwrap DDP container if needed +running_mfu = -1.0 +while True: + + # determine and set the learning rate for this iteration + lr = get_lr(iter_num) if decay_lr else learning_rate + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + # evaluate the loss on train/val sets and write checkpoints + if iter_num % eval_interval == 0 and master_process: + losses = estimate_loss() + print( + f"step {iter_num}: train loss {losses['train']:.4f}, val loss {losses['val']:.4f}" + ) + if wandb_log: + wandb.log({ + "iter": iter_num, + "train/loss": losses['train'], + "val/loss": losses['val'], + "lr": lr, + "mfu": running_mfu * 100, # convert to percentage + }) + if losses['val'] < best_val_loss or always_save_checkpoint: + best_val_loss = losses['val'] + if iter_num > 0: + checkpoint = { + 'model': raw_model.state_dict(), + 'optimizer': optimizer.state_dict(), + 'model_args': model_args, + 'iter_num': iter_num, + 'best_val_loss': best_val_loss, + 'config': config, + } + print(f"saving checkpoint to {out_dir}") + torch.save(checkpoint, os.path.join(out_dir, 'ckpt.pt')) + if iter_num == 0 and eval_only: + break + + # forward backward update, with optional gradient accumulation to simulate larger batch size + # and using the GradScaler if data type is float16 + for micro_step in range(gradient_accumulation_steps): + if ddp: + # in DDP training we only need to sync gradients at the last micro step. + # the official way to do this is with model.no_sync() context manager, but + # I really dislike that this bloats the code and forces us to repeat code + # looking at the source of that context manager, it just toggles this variable + model.require_backward_grad_sync = ( + micro_step == gradient_accumulation_steps - 1) + with ctx: + logits, loss = model(X, Y) + loss = loss / gradient_accumulation_steps # scale the loss to account for gradient accumulation + # immediately `async prefetch` next batch while model is doing the forward pass on the GPU + X, Y = get_batch('train') + # backward pass, with gradient scaling if training in `fp16` + scaler.scale(loss).backward() + # clip the gradient + if grad_clip != 0.0: + scaler.unscale_(optimizer) + torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip) + # step the optimizer and `scaler` if training in `fp16` + scaler.step(optimizer) + scaler.update() + # flush the gradients as soon as we can, no need for this memory anymore + optimizer.zero_grad(set_to_none=True) + + # timing and logging + t1 = time.time() + dt = t1 - t0 + t0 = t1 + if iter_num % log_interval == 0 and master_process: + # get loss as float. note: this is a CPU-GPU sync point + # scale up to undo the division above, approximating the true total loss (exact would have been a sum) + lossf = loss.item() * gradient_accumulation_steps + if local_iter_num >= 5: # let the training loop settle a bit + mfu = raw_model.estimate_mfu( + batch_size * gradient_accumulation_steps, dt) + running_mfu = mfu if running_mfu == -1.0 else 0.9 * running_mfu + 0.1 * mfu + print( + f"iter {iter_num}: loss {lossf:.4f}, time {dt*1000:.2f}ms, mfu {running_mfu*100:.2f}%" + ) + iter_num += 1 + local_iter_num += 1 + + # termination conditions + if iter_num > max_iters: + break + +if ddp: + destroy_process_group() diff --git a/docs/sphinx/applications/python/nanoGPT/train_pad_gemb.py b/docs/sphinx/applications/python/nanoGPT/train_pad_gemb.py new file mode 100644 index 00000000000..580edfdd319 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/train_pad_gemb.py @@ -0,0 +1,408 @@ +""" +`This training script can be run both on a single gpu in debug mode,` +`and also in a larger training run with distributed data parallel (ddp).` + +`To run on a single GPU, example:` +`$ python train.py --batch_size=32 --compile=False` + +`To run with DDP on 4 gpus on 1 node, example:` +`$ torchrun --standalone --nproc_per_node=4 train.py` + +`To run with DDP on 4 gpus across 2 nodes, example:` +`- Run on the first (master) node with example IP 123.456.123.456:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=0 --master_addr=123.456.123.456 --master_port=1234 train.py` +`- Run on the worker node:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=1 --master_addr=123.456.123.456 --master_port=1234 train.py` +`(If your cluster does not have Infiniband interconnect prepend NCCL_IB_DISABLE=1)` +""" + +import os +import time +import math +import pickle +from contextlib import nullcontext +from tqdm import tqdm + +import numpy as np +import torch +from torch.nn.parallel import DistributedDataParallel as DDP +from torch.distributed import init_process_group, destroy_process_group + +from model_pad_gemb import GPTConfig, GPT + +n_epochs = 3 # (approximately) + +# ----------------------------------------------------------------------------- +# default config values designed to train a gpt2 (124M) on OpenWebText +# I/O +out_dir = 'out' +eval_interval = 20_000 +log_interval = 1 +eval_iters = 200 +eval_only = False # if True, script exits right after the first `eval` +always_save_checkpoint = True # if True, always save a checkpoint after each `eval` +init_from = 'scratch' # 'scratch' or 'resume' or 'gpt2*' +# `wandb` logging +wandb_log = False # disabled by default +wandb_project = 'owt' +wandb_run_name = 'gpt2' # `'run' + str(time.time())` +# data +dataset = 'openwebtext' +gradient_accumulation_steps = 5 * 8 # used to simulate larger batch sizes +batch_size = 12 # if gradient_accumulation_steps > 1, this is the micro-batch size +block_size = 1024 +# model +n_layer = 12 +n_head = 12 +n_embd = 768 +dropout = 0.0 # for `pretraining` 0 is good, for finetuning try 0.1+ +bias = False # do we use bias inside LayerNorm and Linear layers? +# `adamw` optimizer +learning_rate = 6e-4 # max learning rate +max_iters = 600000 # total number of training iterations +weight_decay = 1e-1 +beta1 = 0.9 +beta2 = 0.95 +grad_clip = 1.0 # clip gradients at this value, or disable if == 0.0 +# learning rate decay settings +decay_lr = True # whether to decay the learning rate +warmup_iters = 2000 # how many steps to warm up for +lr_decay_iters = 600000 # `should be ~= max_iters per Chinchilla` +min_lr = 6e-5 # minimum learning rate, should be ~= learning_rate/10 per Chinchilla +# DDP settings +backend = 'nccl' # `'nccl', 'gloo', etc.` +# system +device = 'cuda' # examples: `'cpu', 'cuda', 'cuda:0', 'cuda:1' etc., or try 'mps' on macbooks` +dtype = 'bfloat16' if torch.cuda.is_available( +) and torch.cuda.is_bf16_supported( +) else 'float16' # 'float32', '`bfloat16`', or 'float16', the latter will auto implement a GradScaler +compile = True # use PyTorch 2.0 to compile the model to be faster +# ----------------------------------------------------------------------------- +config_keys = [ + k for k, v in globals().items() + if not k.startswith('_') and isinstance(v, (int, float, bool, str)) +] +exec(open( + 'configurator.py').read()) # overrides from command line or config file +config = {k: globals()[k] for k in config_keys} # will be useful for logging +# ----------------------------------------------------------------------------- + +# various `inits`, derived attributes, I/O setup +ddp = int(os.environ.get('RANK', -1)) != -1 # is this a `ddp` run? +if ddp: + init_process_group(backend=backend) + ddp_rank = int(os.environ['RANK']) + ddp_local_rank = int(os.environ['LOCAL_RANK']) + ddp_world_size = int(os.environ['WORLD_SIZE']) + device = f'cuda:{ddp_local_rank}' + torch.cuda.set_device(device) + master_process = ddp_rank == 0 # this process will do logging, `checkpointing` etc. + seed_offset = ddp_rank # each process gets a different seed + # world_size number of processes will be training simultaneously, so we can scale + # down the desired gradient accumulation iterations per process proportionally + assert gradient_accumulation_steps % ddp_world_size == 0 + gradient_accumulation_steps //= ddp_world_size +else: + # if not `ddp`, we are running on a single `gpu`, and one process + master_process = True + seed_offset = 0 + ddp_world_size = 1 +tokens_per_iter = gradient_accumulation_steps * ddp_world_size * batch_size * block_size +print(f"tokens per iteration will be: {tokens_per_iter:,}") + +if master_process: + os.makedirs(out_dir, exist_ok=True) +torch.manual_seed(1337 + seed_offset) +torch.backends.cuda.matmul.allow_tf32 = True # `allow tf32 on matmul` +torch.backends.cudnn.allow_tf32 = True # `allow tf32 on cudnn` +device_type = 'cuda' if 'cuda' in device else 'cpu' # for later use in `torch.autocast` +# note: float16 data type will automatically use a GradScaler +ptdtype = { + 'float32': torch.float32, + 'bfloat16': torch.bfloat16, + 'float16': torch.float16 +}[dtype] +ctx = nullcontext() if device_type == 'cpu' else torch.amp.autocast( + device_type=device_type, dtype=ptdtype) + +# poor man's data loader +data_dir = os.path.join('data', dataset) + +mmap = 'r' +mmap = None +print(f'Opening data (mmap mode: {mmap})...') +train_data = np.load(os.path.join(data_dir, 'train.npy'), mmap_mode=mmap) +val_data = np.load(os.path.join(data_dir, 'val.npy'), mmap_mode=mmap) +graph_emb_np = np.load(os.path.join(data_dir, 'feather_emb_d500.npy'), + mmap_mode=mmap) +emb_dim = graph_emb_np.shape[1] + + +def get_batch(split): + + if split == 'train': + data = train_data + emb_idx_data = train_data_graph_idx_list + else: + data = val_data + emb_idx_data = val_data_graph_idx_list + ix = np.random.randint(low=0, high=data.shape[0] - 1, size=batch_size) + data_batch_np = data[ix] + graph_emb_data = torch.tensor(graph_emb_np[emb_idx_data[ix]]) + + #`print(f"Get batch graph_emb_data shape: {graph_emb_data.shape}, {graph_emb_data.dtype}")` + x = torch.tensor(data_batch_np[:, :1, :].astype(np.int64)).flatten(1) + y = torch.tensor(data_batch_np[:, 1:2, :].astype(np.int64)).flatten(1) + if device_type == 'cuda': + # pin arrays x,y, which allows us to move them to GPU asynchronously (non_blocking=True) + x, y = x.pin_memory().to(device, non_blocking=True), y.pin_memory().to( + device, non_blocking=True) + graph_emb_data = graph_emb_data.pin_memory().to(device, + non_blocking=True).to( + torch.bfloat16) + else: + x, y = x.to(device), y.to(device) + graph_emb_data = graph_emb_data.to(device) + #`print(f"graph_emb_data dtype: {graph_emb_data.dtype}\n\n\n")` + + return x, y, graph_emb_data + + +# `init` these up here, can override if `init_from='resume'` (i.e. from a checkpoint) +iter_num = 0 +best_val_loss = 1e9 + +# attempt to derive vocab_size from the dataset +meta_path = os.path.join(data_dir, 'meta.pkl') +meta_vocab_size = None +if os.path.exists(meta_path): + with open(meta_path, 'rb') as f: + meta = pickle.load(f) + meta_vocab_size = meta['vocab_size'] + print(f"found vocab_size = {meta_vocab_size} (inside {meta_path})") + +# For graph `embeddings` +emb_graph_id_to_idx_dict = meta['emb_graph_id_to_idx_dict'] +emb_graph_idx_to_id_dict = meta['emb_graph_idx_to_id_dict'] +train_data_graph_idx_list = np.array(meta['train_data_graph_idx_list']) +val_data_graph_idx_list = np.array(meta['val_data_graph_idx_list']) + +# model `init` +model_args = dict(n_layer=n_layer, + n_head=n_head, + n_embd=n_embd, + block_size=block_size, + bias=bias, + vocab_size=None, + dropout=dropout) # start with `model_args` from command line +if init_from == 'scratch': + # `init` a new model from scratch + print("Initializing a new model from scratch") + # determine the vocab size we'll use for from-scratch training + if meta_vocab_size is None: + print( + "defaulting to vocab_size of GPT-2 to 50304 (50257 rounded up for efficiency)" + ) + model_args[ + 'vocab_size'] = meta_vocab_size if meta_vocab_size is not None else 50304 + gptconf = GPTConfig(**model_args) + model = GPT(gptconf) +elif init_from == 'resume': + print(f"Resuming training from {out_dir}") + # resume training from a checkpoint. + ckpt_path = os.path.join(out_dir, 'ckpt.pt') + checkpoint = torch.load(ckpt_path, map_location=device) + checkpoint_model_args = checkpoint['model_args'] + # force these config attributes to be equal otherwise we can't even resume training + # the rest of the attributes (e.g. dropout) can stay as desired from command line + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = checkpoint_model_args[k] + # create the model + gptconf = GPTConfig(**model_args) + model = GPT(gptconf) + state_dict = checkpoint['model'] + # fix the keys of the state dictionary :( + # honestly no idea how checkpoints sometimes get this prefix, have to debug more + unwanted_prefix = '_orig_mod.' + for k, v in list(state_dict.items()): + if k.startswith(unwanted_prefix): + state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k) + model.load_state_dict(state_dict) + iter_num = checkpoint['iter_num'] + best_val_loss = checkpoint['best_val_loss'] +elif init_from.startswith('gpt2'): + print(f"Initializing from OpenAI GPT-2 weights: {init_from}") + # initialize from OpenAI GPT-2 weights + override_args = dict(dropout=dropout) + model = GPT.from_pretrained(init_from, override_args) + # read off the created config `params`, so we can store them into checkpoint correctly + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = getattr(model.config, k) +# crop down the model block size if desired, using model surgery +if block_size < model.config.block_size: + model.crop_block_size(block_size) + model_args[ + 'block_size'] = block_size # so that the checkpoint will have the right value +model.to(device) + +# initialize a GradScaler. If enabled=False `scaler` is a no-op +scaler = torch.cuda.amp.GradScaler(enabled=(dtype == 'float16')) + +# optimizer +optimizer = model.configure_optimizers(weight_decay, learning_rate, + (beta1, beta2), device_type) +if init_from == 'resume': + optimizer.load_state_dict(checkpoint['optimizer']) +checkpoint = None # free up memory + +# compile the model +if compile: + print("compiling the model... (takes a ~minute)") + unoptimized_model = model + model = torch.compile(model) # requires PyTorch 2.0 + +# wrap model into DDP container +if ddp: + model = DDP(model, device_ids=[ddp_local_rank]) + + +# helps estimate an arbitrarily accurate loss over either split using many batches +@torch.no_grad() +def estimate_loss(): + out = {} + model.eval() + for split in ['train', 'val']: + losses = torch.zeros(eval_iters) + for k in range(eval_iters): + X, Y, cur_graph_emb = get_batch(split) + with ctx: + logits, loss = model(X, cur_graph_emb, Y) + losses[k] = loss.item() + out[split] = losses.mean() + model.train() + return out + + +# learning rate decay scheduler (cosine with `warmup`) +def get_lr(it): + # 1) linear `warmup` for `warmup_iters` steps + if it < warmup_iters: + return learning_rate * (it + 1) / (warmup_iters + 1) + # 2) if `it > lr_decay_iters`, return min learning rate + if it > lr_decay_iters: + return min_lr + # 3) in between, use cosine decay down to min learning rate + decay_ratio = (it - warmup_iters) / (lr_decay_iters - warmup_iters) + assert 0 <= decay_ratio <= 1 + coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio)) # `coeff` ranges 0..1 + return min_lr + coeff * (learning_rate - min_lr) + + +# logging +if wandb_log and master_process: + import wandb + wandb.init(project=wandb_project, name=wandb_run_name, config=config) + +# training loop +X, Y, cur_graph_emb = get_batch('train') # fetch the very first batch +#`print(f"From training loop cur_graph_emb: {cur_graph_emb.shape}")` +t0 = time.time() +local_iter_num = 0 # number of iterations in the lifetime of this process +raw_model = model.module if ddp else model # unwrap DDP container if needed +running_mfu = -1.0 +#while True: + +dataset_n_batches = train_data.shape[0] // batch_size +pbar = tqdm(list(range(n_epochs * dataset_n_batches))) + +for i in pbar: + + # determine and set the learning rate for this iteration + lr = get_lr(iter_num) if decay_lr else learning_rate + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + # evaluate the loss on train/val sets and write checkpoints + if iter_num % eval_interval == 0 and master_process: + losses = estimate_loss() + #`print(f"step {iter_num}: train loss {losses['train']:.4f}, val loss {losses['val']:.4f}")` + pbar.set_description( + f"train loss {losses['train']:.4f}, val loss {losses['val']:.4f}") + if wandb_log: + wandb.log({ + "iter": iter_num, + "train/loss": losses['train'], + "val/loss": losses['val'], + "lr": lr, + "mfu": running_mfu * 100, # convert to percentage + }) + if losses['val'] < best_val_loss or always_save_checkpoint: + best_val_loss = losses['val'] + if iter_num > 0: + checkpoint = { + 'model': raw_model.state_dict(), + 'optimizer': optimizer.state_dict(), + 'model_args': model_args, + 'iter_num': iter_num, + 'best_val_loss': best_val_loss, + 'config': config, + } + print(f"saving checkpoint to {out_dir}") + torch.save(checkpoint, os.path.join(out_dir, f'ckpt_{i}.pt')) + torch.save(checkpoint, os.path.join(out_dir, 'ckpt_overfit.pt')) + if iter_num == 0 and eval_only: + break + + # forward backward update, with optional gradient accumulation to simulate larger batch size + # and using the GradScaler if data type is float16 + for micro_step in range(gradient_accumulation_steps): + if ddp: + # in DDP training we only need to sync gradients at the last micro step. + # the official way to do this is with model.no_sync() context manager, but + # I really dislike that this bloats the code and forces us to repeat code + # looking at the source of that context manager, it just toggles this variable + model.require_backward_grad_sync = ( + micro_step == gradient_accumulation_steps - 1) + with ctx: + logits, loss = model(X, cur_graph_emb, Y) + loss = loss / gradient_accumulation_steps # scale the loss to account for gradient accumulation + # `immediately async prefetch next batch while model is doing the forward pass on the GPU` + X, Y, cur_graph_emb = get_batch('train') + # backward pass, with gradient scaling if training in `fp16` + scaler.scale(loss).backward() + # clip the gradient + if grad_clip != 0.0: + scaler.unscale_(optimizer) + torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip) + # step the optimizer and `scaler` if training in `fp16` + scaler.step(optimizer) + scaler.update() + # flush the gradients as soon as we can, no need for this memory anymore + optimizer.zero_grad(set_to_none=True) + + # timing and logging + t1 = time.time() + dt = t1 - t0 + t0 = t1 + if iter_num % log_interval == 0 and master_process: + # get loss as float. note: this is a CPU-GPU sync point + # scale up to undo the division above, approximating the true total loss (exact would have been a sum) + lossf = loss.item() * gradient_accumulation_steps + if local_iter_num >= 5: # let the training loop settle a bit + mfu = raw_model.estimate_mfu( + batch_size * gradient_accumulation_steps, dt) + running_mfu = mfu if running_mfu == -1.0 else 0.9 * running_mfu + 0.1 * mfu + #`print(f"iter {iter_num}: loss {lossf:.4f}, time {dt*1000:.2f}ms, mfu {running_mfu*100:.2f}%")` + iter_num += 1 + local_iter_num += 1 + + # termination conditions + if iter_num > max_iters: + break + +if ddp: + destroy_process_group() diff --git a/docs/sphinx/applications/python/nanoGPT/train_pad_gemb_ar_eval.py b/docs/sphinx/applications/python/nanoGPT/train_pad_gemb_ar_eval.py new file mode 100644 index 00000000000..264816537e1 --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/train_pad_gemb_ar_eval.py @@ -0,0 +1,565 @@ +""" +`This training script can be run both on a single gpu in debug mode,` +`and also in a larger training run with distributed data parallel (ddp).` + +`To run on a single GPU, example:` +`$ python train.py --batch_size=32 --compile=False` + +`To run with DDP on 4 gpus on 1 node, example:` +`$ torchrun --standalone --nproc_per_node=4 train.py` + +`To run with DDP on 4 gpus across 2 nodes, example:` +`- Run on the first (master) node with example IP 123.456.123.456:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=0 --master_addr=123.456.123.456 --master_port=1234 train.py` +`- Run on the worker node:` +`$ torchrun --nproc_per_node=8 --nnodes=2 --node_rank=1 --master_addr=123.456.123.456 --master_port=1234 train.py` +`(If your cluster does not have Infiniband interconnect prepend NCCL_IB_DISABLE=1)` +""" + +import os +import time +import math +import pickle +from contextlib import nullcontext +from tqdm import tqdm +import sys +import pandas as pd + +sys.path.append("../") + +from datetime import datetime +import numpy as np +import torch +import json +from torch.nn.parallel import DistributedDataParallel as DDP +from torch.distributed import init_process_group, destroy_process_group + +from nanoGPT.model_pad_gemb import GPTConfig as GPTConfig_gemb +from nanoGPT.model_pad_gemb import GPT as GPT_gemb + +from nanoGPT.model_pad import GPTConfig as GPTConfig_nogemb +from nanoGPT.model_pad import GPT as GPT_nogemb + +from qaoa_gpt_src.util import generate_circ_from_df, eval_adapt_gpt_circ_cudaq + +# `val_sampled_df = pd.read_pickle('data/qaoa_n10w_012325_v7/test_run_df.pkl')` +# `val_graph_emb_np = np.load('data/qaoa_n10w_012325_v7/feather_emb_d500.npy')` +# `val_meta = pd.read_pickle("data/qaoa_n10w_012325_v7/meta.pkl")` +# `val_emb_graph_id_to_idx_dict = val_meta['emb_graph_id_to_idx_dict']` + +n_epochs = 100 # (approximately) +eval_ar_every = 1000 + +# ----------------------------------------------------------------------------- +# default config values designed to train a gpt2 (124M) on OpenWebText +# I/O +out_dir = 'out' +eval_interval = 20000 +log_interval = 1 +eval_iters = 200 +eval_only = False # if True, script exits right after the first `eval` +always_save_checkpoint = True # if True, always save a checkpoint after each `eval` +init_from = 'scratch' # 'scratch' or 'resume' or 'gpt2*' +# `wandb` logging +wandb_log = False # disabled by default +wandb_project = 'owt' +wandb_run_name = 'gpt2' # `'run' + str(time.time())` +# data +dataset = 'openwebtext' +gradient_accumulation_steps = 5 * 8 # used to simulate larger batch sizes +batch_size = 12 # if gradient_accumulation_steps > 1, this is the micro-batch size +block_size = 1024 +# model +n_layer = 12 +n_head = 12 +n_embd = 768 +dropout = 0.0 # for `pretraining` 0 is good, for finetuning try 0.1+ +bias = False # do we use bias inside LayerNorm and Linear layers? +# `adamw` optimizer +learning_rate = 6e-4 # max learning rate +max_iters = 600000 # total number of training iterations +weight_decay = 1e-1 +beta1 = 0.9 +beta2 = 0.95 +grad_clip = 1.0 # clip gradients at this value, or disable if == 0.0 +# learning rate decay settings +decay_lr = True # whether to decay the learning rate +warmup_iters = 2000 # how many steps to warm up for +lr_decay_iters = 600000 # `should be ~= max_iters per Chinchilla` +min_lr = 6e-5 # minimum learning rate, should be ~= learning_rate/10 per Chinchilla +# DDP settings +backend = 'nccl' # `'nccl', 'gloo', etc.` +# system +device = 'cuda' # `examples: 'cpu', 'cuda', 'cuda:0', 'cuda:1' etc., or try 'mps' on macbooks` +dtype = 'bfloat16' if torch.cuda.is_available( +) and torch.cuda.is_bf16_supported( +) else 'float16' # 'float32', '`bfloat16`', or 'float16', the latter will auto implement a GradScaler +compile = True # use PyTorch 2.0 to compile the model to be faster +# ----------------------------------------------------------------------------- +config_keys = [ + k for k, v in globals().items() + if not k.startswith('_') and isinstance(v, (int, float, bool, str)) +] +use_graph_emb = False +pool_type = "all_pool" +token_seq_round = "token_seq_round_d2" +n_samples = 5 +exec(open( + 'configurator.py').read()) # overrides from command line or config file +config = {k: globals()[k] for k in config_keys} # will be useful for logging +# ----------------------------------------------------------------------------- + +if use_graph_emb: + print("Training model with graph embeddings") + model_suf = 'gemb' +else: + print("Training model with NO graph embeddings") + model_suf = 'nogemb' + +# various `inits`, derived attributes, I/O setup +ddp = int(os.environ.get('RANK', -1)) != -1 # is this a `ddp` run? +if ddp: + init_process_group(backend=backend) + ddp_rank = int(os.environ['RANK']) + ddp_local_rank = int(os.environ['LOCAL_RANK']) + ddp_world_size = int(os.environ['WORLD_SIZE']) + device = f'cuda:{ddp_local_rank}' + torch.cuda.set_device(device) + master_process = ddp_rank == 0 # this process will do logging, `checkpointing` etc. + seed_offset = ddp_rank # each process gets a different seed + # world_size number of processes will be training simultaneously, so we can scale + # down the desired gradient accumulation iterations per process proportionally + assert gradient_accumulation_steps % ddp_world_size == 0 + gradient_accumulation_steps //= ddp_world_size +else: + # if not `ddp`, we are running on a single `gpu`, and one process + master_process = True + seed_offset = 0 + ddp_world_size = 1 +tokens_per_iter = gradient_accumulation_steps * ddp_world_size * batch_size * block_size +print(f"tokens per iteration will be: {tokens_per_iter:,}") + +if master_process: + os.makedirs(out_dir, exist_ok=True) +torch.manual_seed(1337 + seed_offset) +torch.backends.cuda.matmul.allow_tf32 = True # `allow tf32 on matmul` +torch.backends.cudnn.allow_tf32 = True # `allow tf32 on cudnn` +device_type = 'cuda' if 'cuda' in device else 'cpu' # for later use in `torch.autocast` +# note: float16 data type will automatically use a GradScaler +ptdtype = { + 'float32': torch.float32, + 'bfloat16': torch.bfloat16, + 'float16': torch.float16 +}[dtype] +ctx = nullcontext() if device_type == 'cpu' else torch.amp.autocast( + device_type=device_type, dtype=ptdtype) + +# poor man's data loader +data_dir = os.path.join('data', dataset) + +mmap = 'r' +mmap = None +print(f'Opening data (mmap mode: {mmap})...') +train_data = np.load(os.path.join(data_dir, 'train.npy'), mmap_mode=mmap) +val_data = np.load(os.path.join(data_dir, 'val.npy'), mmap_mode=mmap) +graph_emb_np = np.load(os.path.join(data_dir, 'feather_emb_d500.npy'), + mmap_mode=mmap) +emb_dim = graph_emb_np.shape[1] + +logging_json_file = os.path.join(out_dir, 'train_log.json') +logging_list = [] + + +def get_batch(split): + + if split == 'train': + data = train_data + emb_idx_data = train_data_graph_idx_list + else: + data = val_data + emb_idx_data = val_data_graph_idx_list + ix = np.random.randint(low=0, high=data.shape[0] - 1, size=batch_size) + data_batch_np = data[ix] + graph_emb_data = torch.tensor(graph_emb_np[emb_idx_data[ix]]) + + #`print(f"Get batch graph_emb_data shape: {graph_emb_data.shape}, {graph_emb_data.dtype}")` + x = torch.tensor(data_batch_np[:, :1, :].astype(np.int64)).flatten(1) + y = torch.tensor(data_batch_np[:, 1:2, :].astype(np.int64)).flatten(1) + if device_type == 'cuda': + # pin arrays x,y, which allows us to move them to GPU asynchronously (non_blocking=True) + x, y = x.pin_memory().to(device, non_blocking=True), y.pin_memory().to( + device, non_blocking=True) + graph_emb_data = graph_emb_data.pin_memory().to(device, + non_blocking=True).to( + torch.bfloat16) + else: + x, y = x.to(device), y.to(device) + graph_emb_data = graph_emb_data.to(device) + #`print(f"graph_emb_data dtype: {graph_emb_data.dtype}\n\n\n")` + + return x, y, graph_emb_data + + +######################################### +# `init` these up here, can override if `init_from='resume'` (i.e. from a checkpoint) +iter_num = 0 +best_val_loss = 1e9 + +# attempt to derive vocab_size from the dataset +meta_path = os.path.join(data_dir, 'meta.pkl') +meta_vocab_size = None +if os.path.exists(meta_path): + with open(meta_path, 'rb') as f: + meta = pickle.load(f) + meta_vocab_size = meta['vocab_size'] + print(f"found vocab_size = {meta_vocab_size} (inside {meta_path})") + +# For graph `embeddings` +emb_graph_id_to_idx_dict = meta['emb_graph_id_to_idx_dict'] +emb_graph_idx_to_id_dict = meta['emb_graph_idx_to_id_dict'] +train_data_graph_idx_list = np.array(meta['train_data_graph_idx_list']) +val_data_graph_idx_list = np.array(meta['val_data_graph_idx_list']) + +# For AR validation +########################################## +val_sampled_df = pd.read_pickle( + os.path.join(data_dir, 'combined_res_tok_shf_val_df.pkl')) +val_sampled_df = val_sampled_df[val_sampled_df['has_emb']] +val_n_nodes = int(val_sampled_df['n_nodes'].max()) +val_graph_emb_np = graph_emb_np +val_meta = meta +val_emb_graph_id_to_idx_dict = val_meta['emb_graph_id_to_idx_dict'] + +# ADAPT GPT-specific code +#------------------------- +#------------------------- +#------------------------- + + +def get_test_energies_df(): + + model.eval() + + print("Generating circuits with current state of the model") + gc_df = generate_circ_from_df( + val_sampled_df, + model=model, + graph_emb_np=val_graph_emb_np if use_graph_emb else None, + emb_graph_id_to_idx_dict=val_emb_graph_id_to_idx_dict + if use_graph_emb else None, + meta=meta, + device=device, + ctx=ctx, + n_samples_per_batch=50, # max number of distinct graphs in a batch + num_samples=n_samples, # number of samples to draw + max_new_tokens=150, # number of tokens generated in each sample + temperature= + 0.1, # 1.0 = no change, < 1.0 = less random, > 1.0 = more random, in predictions + top_k= + 200, # retain only the top_k most likely tokens, clamp others to have 0 probability + token_seq_col=token_seq_round, + normalize_weights_flag=False, + ) + + ## Evaluating energies with ADAPT-QAOA in CUDA-Q + + print("Evaluating energies with ADAPT-QAOA in CUDA-Q") + energies_cudaq_gc_df = eval_adapt_gpt_circ_cudaq( + gc_df, + temp_folder='../temp_data/', + n_nodes=val_n_nodes, + pool_type=pool_type, + ) + + return energies_cudaq_gc_df + + +def eval_model_ar(): + + print("Model evaluation...") + test_energies_df = get_test_energies_df() + + test_energies_expl_df = test_energies_df[[ + 'adapt_gpt_energies', 'true_energy' + ]].explode('adapt_gpt_energies') + + test_energies_expl_corr_df = test_energies_expl_df[ + test_energies_expl_df['adapt_gpt_energies'] != 999] + + test_energies_expl_corr_df['ar'] = test_energies_expl_corr_df[ + 'adapt_gpt_energies'] / test_energies_expl_corr_df['true_energy'] + + avg_ar = round(test_energies_expl_corr_df['ar'].mean(), 5) + + test_energies_expl_inc_df = test_energies_expl_df[ + test_energies_expl_df['adapt_gpt_energies'] == 999] + + wrong_circ_rate = round( + len(test_energies_expl_inc_df) / len(test_energies_expl_df), 5) + + return test_energies_df, avg_ar, wrong_circ_rate + + +#------------------------- +#------------------------- +#------------------------- +########################################## + +# model `init` +model_args = dict(n_layer=n_layer, + n_head=n_head, + n_embd=n_embd, + block_size=block_size, + bias=bias, + vocab_size=None, + dropout=dropout) # start with `model_args` from command line +if init_from == 'scratch': + # `init` a new model from scratch + print("Initializing a new model from scratch") + # determine the vocab size we'll use for from-scratch training + if meta_vocab_size is None: + print( + "defaulting to vocab_size of GPT-2 to 50304 (50257 rounded up for efficiency)" + ) + model_args[ + 'vocab_size'] = meta_vocab_size if meta_vocab_size is not None else 50304 + if use_graph_emb: + gptconf = GPTConfig_gemb(**model_args) + model = GPT_gemb(gptconf) + else: + gptconf = GPTConfig_nogemb(**model_args) + model = GPT_nogemb(gptconf) +elif init_from == 'resume': + print(f"Resuming training from {out_dir}") + # resume training from a checkpoint. + ckpt_path = os.path.join(out_dir, 'ckpt.pt') + checkpoint = torch.load(ckpt_path, map_location=device) + checkpoint_model_args = checkpoint['model_args'] + # force these config attributes to be equal otherwise we can't even resume training + # the rest of the attributes (e.g. dropout) can stay as desired from command line + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = checkpoint_model_args[k] + # create the model + if use_graph_emb: + gptconf = GPTConfig_gemb(**model_args) + model = GPT_gemb(gptconf) + else: + gptconf = GPTConfig_nogemb(**model_args) + model = GPT_nogemb(gptconf) + state_dict = checkpoint['model'] + # fix the keys of the state dictionary :( + # honestly no idea how checkpoints sometimes get this prefix, have to debug more + unwanted_prefix = '_orig_mod.' + for k, v in list(state_dict.items()): + if k.startswith(unwanted_prefix): + state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k) + model.load_state_dict(state_dict) + iter_num = checkpoint['iter_num'] + best_val_loss = checkpoint['best_val_loss'] +elif init_from.startswith('gpt2'): + print(f"Initializing from OpenAI GPT-2 weights: {init_from}") + # initialize from OpenAI GPT-2 weights + override_args = dict(dropout=dropout) + model = GPT.from_pretrained(init_from, override_args) + # read off the created config `params`, so we can store them into checkpoint correctly + for k in [ + 'n_layer', 'n_head', 'n_embd', 'block_size', 'bias', 'vocab_size' + ]: + model_args[k] = getattr(model.config, k) +# crop down the model block size if desired, using model surgery +if block_size < model.config.block_size: + model.crop_block_size(block_size) + model_args[ + 'block_size'] = block_size # so that the checkpoint will have the right value +model.to(device) + +# `initialize a GradScaler. If enabled=False scaler is a no-op` +# `scaler = torch.cuda.amp.GradScaler(enabled=(dtype == 'float16'))` +scaler = torch.amp.GradScaler('cuda', enabled=(dtype == 'float16')) + +# optimizer +optimizer = model.configure_optimizers(weight_decay, learning_rate, + (beta1, beta2), device_type) +if init_from == 'resume': + optimizer.load_state_dict(checkpoint['optimizer']) +checkpoint = None # free up memory + +# compile the model +if compile: + print("compiling the model... (takes a ~minute)") + unoptimized_model = model + model = torch.compile(model) # requires PyTorch 2.0 + +# wrap model into DDP container +if ddp: + model = DDP(model, device_ids=[ddp_local_rank]) + + +# helps estimate an arbitrarily accurate loss over either split using many batches +@torch.no_grad() +def estimate_loss(): + out = {} + model.eval() + for split in ['train', 'val']: + losses = torch.zeros(eval_iters) + for k in range(eval_iters): + X, Y, cur_graph_emb = get_batch(split) + with ctx: + if use_graph_emb: + logits, loss = model(X, cur_graph_emb, Y) + else: + logits, loss = model(X, Y) + losses[k] = loss.item() + out[split] = losses.mean() + model.train() + return out + + +# learning rate decay scheduler (cosine with `warmup`) +def get_lr(it): + # 1) linear `warmup` for `warmup_iters` steps + if it < warmup_iters: + return learning_rate * (it + 1) / (warmup_iters + 1) + # 2) if `it > lr_decay_iters`, return min learning rate + if it > lr_decay_iters: + return min_lr + # 3) in between, use cosine decay down to min learning rate + decay_ratio = (it - warmup_iters) / (lr_decay_iters - warmup_iters) + assert 0 <= decay_ratio <= 1 + coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio)) # `coeff` ranges 0..1 + return min_lr + coeff * (learning_rate - min_lr) + + +# logging +if wandb_log and master_process: + import wandb + wandb.init(project=wandb_project, name=wandb_run_name, config=config) + +# training loop +X, Y, cur_graph_emb = get_batch('train') # fetch the very first batch +#`print(f"From training loop cur_graph_emb: {cur_graph_emb.shape}")` +t0 = time.time() +local_iter_num = 0 # number of iterations in the lifetime of this process +raw_model = model.module if ddp else model # unwrap DDP container if needed +running_mfu = -1.0 +#while True: + +dataset_n_batches = train_data.shape[0] // batch_size +pbar = tqdm(list(range(n_epochs * dataset_n_batches))) + +for i in pbar: + + # determine and set the learning rate for this iteration + lr = get_lr(iter_num) if decay_lr else learning_rate + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + # evaluate the loss on train/val sets and write checkpoints + if iter_num % eval_interval == 0 and master_process: + losses = estimate_loss() + saving_model_name = f'ckpt_{i}_{model_suf}.pt' + if iter_num % eval_ar_every == 0 and iter_num > 0: + print("\tEvaluating model ER and AR...") + cur_test_energies_df, cur_ar, cur_er = eval_model_ar() + print(f"\tCurrent ar: {cur_ar}, error rate: {cur_er}\n\n") + cur_ar_str = str(cur_ar).replace('.', '_') + cur_er_str = str(cur_er).replace('.', '_') + saving_model_name = f'ckpt_{i}_{model_suf}__ar_{cur_ar_str}__er_{cur_er_str}.pt' + + logging_list.append({ + 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'model_dir': out_dir, + 'iter_num': iter_num, + 'cur_gpt_loss_train': losses['train'].item(), + 'cur_gpt_loss_val': losses['val'].item(), + 'cur_ar_val': cur_ar, + 'cur_er_val': cur_er, + 'cur_val_df': cur_test_energies_df.to_json(), + }) + with open(logging_json_file, 'w') as f: + json.dump(logging_list, f) + + #`print(f"step {iter_num}: train loss {losses['train']:.4f}, val loss {losses['val']:.4f}")` + pbar.set_description( + f"train loss {losses['train']:.4f}, val loss {losses['val']:.4f}") + if wandb_log: + wandb.log({ + "iter": iter_num, + "train/loss": losses['train'], + "val/loss": losses['val'], + "lr": lr, + "mfu": running_mfu * 100, # convert to percentage + }) + #if losses['val'] < best_val_loss or always_save_checkpoint: + if losses['val'] < best_val_loss: + best_val_loss = losses['val'] + if iter_num > 0: + checkpoint = { + 'model': raw_model.state_dict(), + 'optimizer': optimizer.state_dict(), + 'model_args': model_args, + 'iter_num': iter_num, + 'best_val_loss': best_val_loss, + 'config': config, + } + print(f"saving checkpoint to {out_dir}") + torch.save(checkpoint, os.path.join(out_dir, saving_model_name)) + #`torch.save(checkpoint, os.path.join(out_dir, 'ckpt_overfit.pt'))` + + if iter_num == 0 and eval_only: + break + + # forward backward update, with optional gradient accumulation to simulate larger batch size + # and using the GradScaler if data type is float16 + for micro_step in range(gradient_accumulation_steps): + if ddp: + # in DDP training we only need to sync gradients at the last micro step. + # the official way to do this is with model.no_sync() context manager, but + # I really dislike that this bloats the code and forces us to repeat code + # looking at the source of that context manager, it just toggles this variable + model.require_backward_grad_sync = ( + micro_step == gradient_accumulation_steps - 1) + with ctx: + if use_graph_emb: + logits, loss = model(X, cur_graph_emb, Y) + else: + logits, loss = model(X, Y) + loss = loss / gradient_accumulation_steps # scale the loss to account for gradient accumulation + # immediately `async prefetch` next batch while model is doing the forward pass on the GPU + X, Y, cur_graph_emb = get_batch('train') + # backward pass, with gradient scaling if training in `fp16` + scaler.scale(loss).backward() + # clip the gradient + if grad_clip != 0.0: + scaler.unscale_(optimizer) + torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip) + # step the optimizer and `scaler` if training in `fp16` + scaler.step(optimizer) + scaler.update() + # flush the gradients as soon as we can, no need for this memory anymore + optimizer.zero_grad(set_to_none=True) + + # timing and logging + t1 = time.time() + dt = t1 - t0 + t0 = t1 + if iter_num % log_interval == 0 and master_process: + # get loss as float. note: this is a CPU-GPU sync point + # scale up to undo the division above, approximating the true total loss (exact would have been a sum) + lossf = loss.item() * gradient_accumulation_steps + if local_iter_num >= 5: # let the training loop settle a bit + mfu = raw_model.estimate_mfu( + batch_size * gradient_accumulation_steps, dt) + running_mfu = mfu if running_mfu == -1.0 else 0.9 * running_mfu + 0.1 * mfu + #`print(f"iter {iter_num}: loss {lossf:.4f}, time {dt*1000:.2f}ms, mfu {running_mfu*100:.2f}%")` + iter_num += 1 + local_iter_num += 1 + + # termination conditions + if iter_num > max_iters: + break + +if ddp: + destroy_process_group() diff --git a/docs/sphinx/applications/python/nanoGPT/transformer_sizing.ipynb b/docs/sphinx/applications/python/nanoGPT/transformer_sizing.ipynb new file mode 100644 index 00000000000..43262add3ba --- /dev/null +++ b/docs/sphinx/applications/python/nanoGPT/transformer_sizing.ipynb @@ -0,0 +1,402 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Transformer Theoretical Model\n", + "\n", + "This notebook stores a bunch of analysis about a Transformer, e.g. estimates the number of FLOPs, parameters, peak memory footprint, checkpoint size, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import OrderedDict" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# config_args = {\n", + "# 'gpt2': dict(n_layer=12, n_head=12, n_embd=768), # 124M params\n", + "# 'gpt2-medium': dict(n_layer=24, n_head=16, n_embd=1024), # 350M params\n", + "# 'gpt2-large': dict(n_layer=36, n_head=20, n_embd=1280), # 774M params\n", + "# 'gpt2-xl': dict(n_layer=48, n_head=25, n_embd=1600), # 1558M params\n", + "# }[model_type]\n", + "\n", + "block_size = 1024\n", + "vocab_size = 50257\n", + "n_layer = 12\n", + "n_head = 12\n", + "n_embd = 768\n", + "bias = False\n", + "assert not bias, \"this notebook assumes bias=False just for simplicity\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "we see: 124337664, expected: 124337664, match: True\n", + "name params ratio (%) \n", + "emebedding/position 786432 0.6325\n", + "embedding/token 38597376 31.0424\n", + "embedding 39383808 31.6749\n", + "attention/ln 768 0.0006\n", + "attention/kqv 1769472 1.4231\n", + "attention/proj 589824 0.4744\n", + "attention 2360064 1.8981\n", + "mlp/ln 768 0.0006\n", + "mlp/ffw 2359296 1.8975\n", + "mlp/proj 2359296 1.8975\n", + "mlp 4719360 3.7956\n", + "block 7079424 5.6937\n", + "transformer 84953088 68.3245\n", + "ln_f 768 0.0006\n", + "dense 0 0.0000\n", + "total 124337664 100.0000\n" + ] + } + ], + "source": [ + "def params():\n", + " \"\"\" estimates the number of parameters in the model\"\"\"\n", + " out = OrderedDict()\n", + "\n", + " # token and position embeddings\n", + " out['emebedding/position'] = n_embd * block_size\n", + " out['embedding/token'] = n_embd * vocab_size\n", + " out['embedding'] = out['emebedding/position'] + out['embedding/token']\n", + "\n", + " # attention blocks\n", + " out['attention/ln'] = n_embd # note, bias=False in our LN\n", + " out['attention/kqv'] = n_embd * 3*n_embd\n", + " out['attention/proj'] = n_embd**2\n", + " out['attention'] = out['attention/ln'] + out['attention/kqv'] + out['attention/proj']\n", + "\n", + " # MLP blocks\n", + " ffw_size = 4*n_embd # feed forward size\n", + " out['mlp/ln'] = n_embd\n", + " out['mlp/ffw'] = n_embd * ffw_size\n", + " out['mlp/proj'] = ffw_size * n_embd\n", + " out['mlp'] = out['mlp/ln'] + out['mlp/ffw'] + out['mlp/proj']\n", + " \n", + " # the transformer and the rest of it\n", + " out['block'] = out['attention'] + out['mlp']\n", + " out['transformer'] = n_layer * out['block']\n", + " out['ln_f'] = n_embd # final layernorm\n", + " out['dense'] = 0 # 0 because of parameter sharing. This layer uses the weights from the embedding layer\n", + "\n", + " # total\n", + " out['total'] = out['embedding'] + out['transformer'] + out['ln_f'] + out['dense']\n", + "\n", + " return out\n", + "\n", + "# compare our param count to that reported by PyTorch\n", + "p = params()\n", + "params_total = p['total']\n", + "print(f\"we see: {params_total}, expected: {124337664}, match: {params_total == 124337664}\")\n", + "# create a header\n", + "print(f\"{'name':20s} {'params':10s} {'ratio (%)':10s}\")\n", + "for k,v in p.items():\n", + " print(f\"{k:20s} {v:10d} {v/params_total*100:10.4f}\")\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "est checkpoint size: 1.49 GB\n", + "measured with wc -c ckpt.pt: 1542470366\n", + "fluff ratio: 103.38%\n" + ] + } + ], + "source": [ + "# we can now calculate the size of each checkpoint\n", + "# params are stored in fp32, and the AdamW optimizer has 2 additional buffers per param for statistics\n", + "params_bytes = params_total*4\n", + "params_and_buffers_bytes = params_bytes + 2*params_bytes\n", + "print(f\"est checkpoint size: {params_and_buffers_bytes/1e9:.2f} GB\")\n", + "measured_bytes = 1542470366 # from wc -c ckpt.pt\n", + "print(f\"measured with wc -c ckpt.pt: {measured_bytes}\")\n", + "print(f\"fluff ratio: {measured_bytes/params_and_buffers_bytes*100:.2f}%\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also estimate the ratio of our GPU memory that will be taken up just by the weights and the buffers inside the AdamW optimizer" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "memory ratio taken up just for parameters: 3.73%\n" + ] + } + ], + "source": [ + "gpu_memory = 40e9 # 40 GB A100 GPU, roughly\n", + "print(f\"memory ratio taken up just for parameters: {params_and_buffers_bytes / gpu_memory * 100:.2f}%\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "i.e. not that much of the memory for this tiny model, most of the memory is activations (forward and backward). This of course changes dramatically for larger and larger models." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's estimate FLOPs for a single forward pass." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name flops ratio (%) \n", + "attention/kqv 3623878656 1.2426\n", + "attention/scores 1610612736 0.5522\n", + "attention/reduce 1610612736 0.5522\n", + "attention/proj 1207959552 0.4142\n", + "attention 8053063680 2.7612\n", + "mlp/ffw1 4831838208 1.6567\n", + "mlp/ffw2 4831838208 1.6567\n", + "mlp 9663676416 3.3135\n", + "block 17716740096 6.0747\n", + "transformer 212600881152 72.8963\n", + "dense 79047426048 27.1037\n", + "forward_total 291648307200 100.0000\n", + "backward_total 583296614400 200.0000\n", + "total 874944921600 300.0000\n" + ] + } + ], + "source": [ + "def flops():\n", + " # we only count Weight FLOPs, all other layers (LayerNorm, Softmax, etc) are effectively irrelevant\n", + " # we count actual FLOPs, not MACs. Hence 2* all over the place\n", + " # basically for any matrix multiply A (BxC) @ B (CxD) -> (BxD) flops are 2*B*C*D\n", + "\n", + " out = OrderedDict()\n", + " head_size = n_embd // n_head\n", + "\n", + " # attention blocks\n", + " # 1) the projection to key, query, values\n", + " out['attention/kqv'] = 2 * block_size * (n_embd * 3*n_embd)\n", + " # 2) calculating the attention scores\n", + " out['attention/scores'] = 2 * block_size * block_size * n_embd\n", + " # 3) the reduction of the values (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)\n", + " out['attention/reduce'] = 2 * n_head * (block_size * block_size * head_size)\n", + " # 4) the final linear projection\n", + " out['attention/proj'] = 2 * block_size * (n_embd * n_embd)\n", + " out['attention'] = sum(out['attention/'+k] for k in ['kqv', 'scores', 'reduce', 'proj'])\n", + "\n", + " # MLP blocks\n", + " ffw_size = 4*n_embd # feed forward size\n", + " out['mlp/ffw1'] = 2 * block_size * (n_embd * ffw_size)\n", + " out['mlp/ffw2'] = 2 * block_size * (ffw_size * n_embd)\n", + " out['mlp'] = out['mlp/ffw1'] + out['mlp/ffw2']\n", + "\n", + " # the transformer and the rest of it\n", + " out['block'] = out['attention'] + out['mlp']\n", + " out['transformer'] = n_layer * out['block']\n", + " out['dense'] = 2 * block_size * (n_embd * vocab_size)\n", + "\n", + " # forward,backward,total\n", + " out['forward_total'] = out['transformer'] + out['dense']\n", + " out['backward_total'] = 2 * out['forward_total'] # use common estimate of bwd = 2*fwd\n", + " out['total'] = out['forward_total'] + out['backward_total']\n", + "\n", + " return out\n", + " \n", + "# compare our param count to that reported by PyTorch\n", + "f = flops()\n", + "flops_total = f['forward_total']\n", + "print(f\"{'name':20s} {'flops':14s} {'ratio (%)':10s}\")\n", + "for k,v in f.items():\n", + " print(f\"{k:20s} {v:14d} {v/flops_total*100:10.4f}\")\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "palm_flops: 875062886400, flops: 874944921600, ratio: 1.0001\n" + ] + } + ], + "source": [ + "# now here is an estimate copy pasted from the PaLM paper\n", + "# this formula is often used to calculate MFU (model flops utilization)\n", + "def palm_flops():\n", + " \"\"\"estimate of the model flops following PaLM paper formula\"\"\"\n", + " # non-embedding model parameters. note that we do not subtract the\n", + " # embedding/token params because those are tied and get used in the last layer.\n", + " N = params()['total'] - params()['emebedding/position']\n", + " L, H, Q, T = n_layer, n_head, n_embd//n_head, block_size\n", + " mf_per_token = 6*N + 12*L*H*Q*T\n", + " mf = mf_per_token * block_size\n", + " return mf\n", + "\n", + "print(f\"palm_flops: {palm_flops():d}, flops: {flops()['total']:d}, ratio: {palm_flops()/flops()['total']:.4f}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ok they are quite similar, giving some confidence that my math in flops() function was ~ok. Now, A100 is cited at 312TFLOPS bfloat16 on tensor cores. So what is our model flops utilization (MFU)? I trained the model above with a batch_size of 20 and grad_accum of 5, which runs in about 755ms on a single A100 GPU. We get:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fraction of A100 used: 37.14%\n" + ] + } + ], + "source": [ + "# here is what we currently roughly measure\n", + "batch_size = 20 * 5 # 5 is grad_accum, so total batch size is 100\n", + "measured_time = 0.755 # in seconds per iteration\n", + "measured_throughput = batch_size / measured_time\n", + "flops_achieved = f['total'] * measured_throughput\n", + "\n", + "# A100 is cited to be 312 TFLOPS of bloat16 running on tensor cores\n", + "a100_flops_promised = 312e12\n", + "\n", + "# the fraction of the A100 that we are using:\n", + "print(f\"fraction of A100 used: {flops_achieved / a100_flops_promised * 100:.2f}%\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For reference, we'd prefer to be somewhere around 50%+, and not just for a single GPU but for an entire DDP run. So we still have some work to do, but at least we're within a factor of ~2X of what is achievable with this GPU." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "time needed to train the model: 3.46 days\n" + ] + } + ], + "source": [ + "# Finally let's check out the 6ND approximation as total cost of training in FLOPs\n", + "model_size = params()['total'] # this is number of parameters, N\n", + "tokens_num = 300e9 # 300B tokens, this is dataset size in tokens, D\n", + "a100_flops = 312e12 # 312 TFLOPS\n", + "assumed_mfu = 0.3 # assume this model flops utilization (take the current 37% from above and add some DDP overhead)\n", + "flops_throughput = a100_flops * 8 * assumed_mfu # assume an 8XA100 node at 30% utilization\n", + "flops_needed = 6 * model_size * tokens_num # 6ND\n", + "time_needed_s = flops_needed / flops_throughput # in seconds\n", + "print(f\"time needed to train the model: {time_needed_s/3600/24:.2f} days\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is not a bad estimate at all. I trained this model and it converged in roughly 4 days. Btw as a good reference for where 6ND comes from and some intuition around it I recommend [Dzmitry's post](https://medium.com/@dzmitrybahdanau/the-flops-calculus-of-language-model-training-3b19c1f025e4)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, FLOPs are just one constraint, the other that we have to keep a close track of is the memory bandwidth. TODO estimate LOAD/STORE costs of our model later." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pytorch2", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7f5833218766b48e6e35e4452ee875aac0e2188d05bbe5298f2c62b79f08b222" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/sphinx/applications/python/qaoa_gpt.ipynb b/docs/sphinx/applications/python/qaoa_gpt.ipynb new file mode 100644 index 00000000000..fd993be8203 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt.ipynb @@ -0,0 +1,1760 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e0044964", + "metadata": {}, + "source": [ + "# QAOA-GPT: GPT model for generating efficient QAOA quantum circuit.\n", + "\n", + "In this tutorial, we will explain the QAOA-GPT workflow introduced in this [paper](https://arxiv.org/pdf/2504.16350), [GitHub](https://github.com/IlyaTyagin/ADAPT-GPT): Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, Ilya Safro \"QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum Approximate Optimization Algorithm Circuits\", IEEE International Conference on Quantum Computing and Engineering (QCE), 2025. \n", + "\n", + "## Overview:\n", + "\n", + "The QAOA-GPT framework is a novel approach that uses Generative Pretrained Transformers (GPT) to directly synthesize quantum circuits for solving Quadratic Unconstrained Binary Optimization (QUBO) problems, specifically demonstrated on the MaxCut problem. Unlike traditional Quantum Approximate Optimization Algorithm (QAOA) methods that rely on iterative classical optimization, QAOA-GPT aims to significantly reduce computational overhead by generating high-quality circuits through a single inference pass of a trained GPT model.\n", + "\n", + "## Background concepts:\n", + "\n", + "To understand QAOA-GPT, it's helpful to be familiar with these concepts:\n", + "\n", + "- MaxCut Problem: A combinatorial optimization problem that involves partitioning the vertices of a graph into two disjoint subsets to maximize the total weight of edges connecting nodes in different subsets. It can be formulated as a QUBO problem. To learn more see this [tutorial](https://nvidia.github.io/cuda-quantum/latest/applications/python/qaoa.html).\n", + "\n", + "- Quantum Approximate Optimization Algorithm (QAOA): A hybrid quantum-classical variational algorithm used for combinatorial optimization. It approximates optimal solutions by alternating between a cost Hamiltonian ($H_c$) derived from the problem's objective function and a mixing Hamiltonian ($H_B$). A classical optimizer iteratively adjusts parameters to minimize the expectation value of the cost Hamiltonian. To learn more see this [tutorial](https://nvidia.github.io/cuda-quantum/latest/applications/python/qaoa.html).\n", + "\n", + "- ADAPT-QAOA: An advancement of standard QAOA that iteratively constructs a problem-tailored ansatz from an operator pool. It addresses limitations of fixed-ansatz QAOA, such as slow convergence and high classical optimization overhead. Operators are selected based on their energy gradient, leading to more compact and expressive circuits. To learn more, check this [tutorial](https://nvidia.github.io/cuda-quantum/latest/applications/python/adapt_qaoa.html)\n", + "\n", + "\n", + "- Generative Pre-trained Transformer (GPT): A semi-supervised learning framework that uses a decoder-only transformer architecture to learn transferable language representations. In QAOA-GPT, this token-based generation paradigm is adapted for synthesizing quantum circuits. See link [here](https://github.com/karpathy/nanoGPT/tree/b580a454dca683cd8b5181767e32549f2d88541f).\n", + "\n", + "- FEATHER (Graph-level embeddings): A non-parametric graph embedding method that captures local distributions of node features and higher-order structural information. In this tutorial, we wraps the original FEATHER [library](https://github.com/benedekrozemberczki/FEATHER) and adapt it to use multiple features (log degree and clustering coefficient). These embeddings are used as conditioning input for the GPT model in QAOA-GPT. See this [paper](https://arxiv.org/pdf/2005.07959) to learn more about FEATHER.\n", + "\n", + "## QAOA-GPT Method: A Four-Stage Pipeline\n", + "\n", + "The QAOA-GPT framework operates through a four-stage training pipeline (illustrated in the figure below).\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "### Step 1: Random Graph Generation\n", + "- Generate Graphs: Input graphs are sampled from the Erdős-Rényi distribution $G(n,s)$, where n is the number of nodes and s is the edge probability. The generated graphs are restricted to connected graphs without isolated nodes or multiple components. Each edge is assigned a random weight $w_{ij} ∈ U(0,1)$.\n", + "\n", + "- Process Graphs: Each generated graph is processed by two components: \n", + "\n", + "Graph Embedding (FEATHER): Computes fixed-length, low-dimensional vector representations of the graph based on characteristic functions of random walk distributions. These embeddings serve as conditioning input for the generative model.\n", + "\n", + "Circuit Generation (ADAPT-QAOA): Generates quantum circuits that provide high-quality approximate solutions to the MaxCut problem. This involves an iterative process:\n", + "\n", + "1- Initialize the variational parameter ($\\gamma_0$) of the problem Hamiltonian.\n", + "\n", + "2- At each iteration $k$, select an operator $O(k)$ from a predefined mixer pool $P$ that has the largest gradient; append the selected operator and re-optimize all variational parameters {$\\beta_1, \\gamma_1, ...., \\beta_k, \\gamma_k$}; repeat until stopping criteria are met (e.g., gradient norm falls below a threshold, circuit depth limit, or energy error-based conditions). \n", + "\n", + "Only circuits achieving a target approximation ratio ($\\alpha >= 0.97$)\n", + "are included in the dataset. Multiple valid circuits per graph are generated to enrich the dataset.\n", + "\n", + "### Step 2: Graph-Circuit Collection:\n", + "- Store Data: The generated graph embeddings (from FEATHER) and the optimized ADAPT-QAOA circuits are stored together in a structured collection.\n", + "\n", + "### Step 3: Training Set Construction: \n", + "- Curate and Tokenize: The graph-circuit collection is curated and tokenized to form the final training set.\n", + "\n", + "1- Graph Tokens: Each graph $G$ is represented as a weighted edge list, enclosed by `` and `` tokens.\n", + "\n", + "2- Circuit Tokens: Each circuit is expressed as a sequence of layer blocks, with ``, operator index $o_k$, and optimized parameters $\\gamma_k, \\beta_k$ for each layer.\n", + "\n", + "3- Numeric Handling: All real numbers (parameters) are rounded to two decimal places and clipped to [−10, 10]. Circuits with parameters outside this range are excluded.\n", + "\n", + "- Training Sample Construction: The dataset consists of independent token sequences, each encoding a graph-circuit pair. Training samples are constructed as consecutive segments of each instance using a sliding window. This ensures strict instance isolation, unlike typical large-scale language model training where sequences might be concatenated.\n", + "\n", + "### Step 4: Model Architecture and Training QAOA-GPT \n", + "\n", + "- Model Training: The tokenized training set is used to train QAOA-GPT, a decoder-only Transformer model based on the nanoGPT implementation of GPT-2. The model is trained from scratch without pre-trained weights, given its custom circuit tokenization schema.\n", + "\n", + "- Transformer Input: The input to the Transformer is computed by combining token embeddings, positional embeddings, and broadcasted graph embeddings from FEATHER. This allows the model to condition circuit generation on global graph features.\n", + "\n", + "- Training Procedure: The model is trained using an autoregressive next-token prediction loss. The average approximation ratio (AR) on a validation set and the circuit error rate (fraction of structurally invalid circuits) are calculated each predefined number of iterations during training.\n" + ] + }, + { + "cell_type": "markdown", + "id": "d93897d3", + "metadata": {}, + "source": [ + "### Requirments:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "645073f0", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install torch \n", + "%pip install numpy \n", + "%pip install transformers \n", + "%pip install datasets \n", + "%pip install tiktoken \n", + "%pip install wandb \n", + "%pip install ipykernel \n", + "%pip install pandas \n", + "%pip install tqdm \n", + "%pip install networkx \n", + "%pip install matplotlib \n", + "%pip install joblib \n", + "%pip install scipy \n", + "%pip install gurobipy\n", + "%pip install seaborn" + ] + }, + { + "cell_type": "markdown", + "id": "4989ac1d", + "metadata": {}, + "source": [ + "## 1- Dataset generation:\n", + "\n", + "Here, we generate graphs and the quantum circuits that provide a solution to the max-cut problem. ADAPT-QAOA is emplyed to generate the circuits. Data are stored in `adap_data` folder. For production, user must generate a large data size (5000 and above).\n", + "\n", + "To do: Dataset generation is the most expensive step. Parallelization of ADAPT-QAOA will accelerate data generation for larger graph problems. The current version is not paralleized yet. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b59364a", + "metadata": {}, + "outputs": [], + "source": [ + "from qaoa_gpt_src.generate_adapt_qaoa_data import generate_data_max_cut\n", + "\n", + "# out_dir is where user wants to save the output data\n", + "# The output will be saved in a folder called adapt_results in the current directory.\n", + "# The output will contain the results of the ADAPT-QAOA algorithm for various graphs.\n", + "adapt_data = 'adapt_results'\n", + "\n", + "# The following parameters are used to generate the dataset for the ADAPT-QAOA algorithm.\n", + "# The graphs_number parameter specifies how many graphs to generate, \n", + "# n_nodes specifies the number of nodes in each graph\n", + "# op_pool specifies the pool of operators to use for the ADAPT-QAOA algorithm. 'qaoa_mixer', 'qaoa_single_x', 'qaoa_double_ops', 'all_pool'\n", + "# use_brute_force, use_simulated_annealing, and use_one_exchange are boolean flags \n", + "# that determine how to calculate the optimal solution for the MaxCut problem using classical methods.\n", + "# init_gamma is a list of initial gamma values for the QAOA circuit, \n", + "# optimizer: BFGS, L-BFGS-B, or COBYLA.\n", + "# approx_ratio is the approximation ratio for the QAOA circuit (vqe_energy/classical_value)\n", + "# norm_threshold is the threshold for the norm of the ADAPT gradient,\n", + "# energy_threshold is the threshold for the energy of the ADAPT-QAOA circuit,\n", + "# max_iter is the maximum number of iterations for the ADAPT. \n", + "# If approx ratio or the other threshold achieved, the loop will break.\n", + "#p_init (float): Initial probability for Erdos-Renyi graph generation.\n", + "#p_final (float): Final probability for Erdos-Renyi graph generation. \n", + "#the probability will be randomly selected between p_init and p_final.\n", + "# seed_g (int): Random seed for for Erdos-Renyi graph generation reproducibility.\n", + "# seed_weight (int): Random seed for edge weight generation reproducibility.\n", + "#seed_adapt (int): Random seed for ADAPT-QAOA reproducibility. \n", + "# If more than operator pools are large, we randomly choose one.\n", + "\n", + "\n", + "generate_data_max_cut(output_dir= adapt_data, graphs_number=5, n_nodes=6, weighted=True, \n", + " use_negative_weights=False,\n", + " use_brute_force=True, use_simulated_annealing=True, \n", + " use_one_exchange=True,\n", + " op_pool='all_pool', \n", + " init_gamma=[0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1], \n", + " scaling_coef=1.0, norm_weights=False, norm_coef=1.0,\n", + " trials_per_graph=1, optimizer='BFGS', \n", + " approx_ratio=0.97, norm_threshold= 1e-4, \n", + " energy_threshold = 1e-15, max_iter=15, \n", + " p_init=0.3, p_final=0.9, seed_g=None, seed_weight=None,\n", + " seed_adapt=None, verbose=False)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "cc7fb505", + "metadata": {}, + "source": [ + "User can run the function above across different GPUs and collect all results in `adapt_data` in `csv` files. Multiple csv files are supported in the function below. The function below will read all files in `adapt_data` as long as the files are named distinctively and generate token files that will combin all results. " + ] + }, + { + "cell_type": "markdown", + "id": "0978f6a6", + "metadata": {}, + "source": [ + "## 2- Filter dataset and tokenization.\n", + "\n", + "First, we filter data. Only, circuits achieving the approximation ration are included. All real number are rounded to two decimal place. Afterward, we tokenize graph and circuits for the GPT training. Data will be stored in a folder inside the `~/nanoGPT/data/save_dir`.\n", + "\n", + "Note: `train_adapt_gpt_config_template.py` is the configuration file where user should set up the inputs for the subsequent GPT training of their data. In the configuration file, user can change `batch_size`, `max_iters`, `lr_decay_iters`, `eval_interval`, `eval_iters`, `eval_ar_every`, and `n_epochs` parameters based on their preferences. User can also change the GPT model: `n_layer`, `n_head`, `n_embd`, `dropout`. The configuration file `train_adapt_gpt_config.py` will be generated and stored together with the other data inside `~/nanoGPT/data/save_dir`. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ae5ed3da", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Opening ADAPT results (adapt_results): 100%|██████████| 1/1 [00:00<00:00, 310.83it/s]\n", + "Opening graphs (adapt_results): 100%|██████████| 1/1 [00:00<00:00, 855.63it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 26247.21it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 38550.59it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 7246.55it/s]\n", + "100%|██████████| 14/14 [00:00<00:00, 82938.21it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 25327.92it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 6474.69it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 107546.26it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generated embedding dimension: 500\n", + "Expected dimension: 500\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 5/5 [00:00<00:00, 6955.73it/s]\n", + "100%|██████████| 5/5 [00:00<00:00, 26247.21it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Applying sliding window...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 5/5 [00:00<00:00, 24585.60it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\tNumber of training samples: 5, val samples: 0, test samples: 0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from qaoa_gpt_src.prepare_tokens import generate_tokens\n", + "import os\n", + "\n", + "# Get directory containing the current script\n", + "current_dir = os.getcwd()\n", + "token_folder_name = 'save_dir'\n", + "\n", + "# The path where the adapt results are saved\n", + "path_input = os.path.join(current_dir, adapt_data)\n", + "\n", + "# The path where the generated tokens will be saved \n", + "# (it should be inside the data folder inside nanoGPT)\n", + "path_save = os.path.join(current_dir, 'nanoGPT', 'data', token_folder_name)\n", + "\n", + "# The path to the configuration file for the QAOA-GPT trainaing\n", + "config_path = os.path.join(current_dir, 'qaoa_gpt_src')\n", + "\n", + "# The following parameters are used to generate the tokens for the ADAPT-QAOA algorithm.\n", + "# n_nodes is the number of nodes in the graph\n", + "# approx_ratio_thr is the threshold for the approximation ratio (vqe_energy/classical_value)\n", + "# val_frac is the fraction of the dataset to be used for validation\n", + "# test_frac is the fraction of the dataset to be used for testing\n", + "# target_val_size is the target size of the validation set\n", + "\n", + "generate_tokens(results_fpath_str = path_input, save_path_str = path_save, \n", + " config_path_temp = config_path, n_nodes = 6, approx_ratio_thr= 0.97, \n", + " val_frac = 0.1, test_frac = 0.0, target_val_size = 2, verbose = False)\n" + ] + }, + { + "cell_type": "markdown", + "id": "79c28b09", + "metadata": {}, + "source": [ + "Let's look at the generated data and tokens." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4dfc44bd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
methodgraph_namegraph_numtrial_numn_nodesinit_gammaenergy_listtrue_energyoptimizerpool_typeedge_weight_scaling_coefedge_weight_norm_coefmixer_pool_pauli_wordmixer_pool_indexgamma_coefbeta_coefapprox_ratiocut_adaptcut_classicalnum_layersoptimizer_success_flagelapsed_timeworker_id_xprefixg_methodedgelist_jsonH_frob_normworker_id_yedgelist_listedgelist_list_lennum_connected_compn_layersgraph_idonly_qaoa_circhas_embtoken_seq_round_d2token_int_seq_round_d2labeltoken_int_seq_round_d2_sw
0ADAPT-QAOAGraph_22160.10[-3.359999999537821, -3.7949999999952433, -4.0...-4.50BFGSall_pool1.01.0[['YIZIII'], ['IZIYII'], ['IIIIYZ'], ['IIIIXX']][14, 35, 66, 64][0.01745972054338572, -0.019733899238238935, 0...[-0.7856887670086344, -0.7854085503209243, -0....0.9888951101('001101', '110010')4True2.087118pid1777360_25-08-21__00_59_resultspid1777360_25-08-21erdos_renyi[[1, 3, 0.97], [1, 4, 0.4], [1, 6, 0.16], [2, ...24.329998pid1777360_25-08-21__00_59_graphs[[1, 3, 0.97], [1, 4, 0.4], [1, 6, 0.16], [2, ...1114pid1777360_25-08-21_^_2FalseTrue[bos, (1, 3), 0.97, (1, 4), 0.4, (1, 6), 0.16,...[1, 18, 671, 10, 775, 16, 1926, 11, 1153, 5, 1...train[[[1, 18, 671, 10, 775, 16, 1926, 11, 1153, 5,...
1ADAPT-QAOAGraph_55160.01[-1.8899999987838123, -2.164999996558533, -2.4...-2.77BFGSall_pool1.01.0[['YZIIII'], ['IZIYII'], ['IIIIYZ'], ['ZIYIII'...[10, 35, 66, 15, 64][2.8691569393532094e-05, -0.000100819259514377...[-0.7853997378605556, -0.785397455801531, -0.7...1.000000100101('100101', '011010')5True3.434215pid1777360_25-08-21__00_59_resultspid1777360_25-08-21erdos_renyi[[1, 2, 0.93], [1, 3, 0.24], [2, 4, 0.55], [2,...12.461396pid1777360_25-08-21__00_59_graphs[[1, 2, 0.93], [1, 3, 0.24], [2, 4, 0.55], [2,...915pid1777360_25-08-21_^_5FalseTrue[bos, (1, 2), 0.93, (1, 3), 0.24, (2, 4), 0.55...[1, 6, 164, 18, 914, 5, 1624, 13, 406, 7, 1470...train[[[1, 6, 164, 18, 914, 5, 1624, 13, 406, 7, 14...
2ADAPT-QAOAGraph_33160.01[-3.179999999999917, -3.5199999985319583, -3.7...-4.48BFGSall_pool1.01.0[['IIYZII'], ['ZIIIIY'], ['IZIIYI'], ['YIIIIY'...[46, 27, 39, 25, 36, 45][6.274385167217878e-07, -5.3271375143128285e-0...[-0.7854006450112557, -0.785394076248958, -0.7...1.000000100110('100110', '011001')6True4.721877pid1777360_25-08-21__00_59_resultspid1777360_25-08-21erdos_renyi[[1, 2, 0.41], [1, 3, 0.6], [1, 4, 0.28], [1, ...23.108302pid1777360_25-08-21__00_59_graphs[[1, 2, 0.41], [1, 3, 0.6], [1, 4, 0.28], [1, ...1316pid1777360_25-08-21_^_3FalseTrue[bos, (1, 2), 0.41, (1, 3), 0.6, (1, 4), 0.28,...[1, 6, 1919, 18, 1238, 10, 1337, 8, 1293, 16, ...train[[[1, 6, 1919, 18, 1238, 10, 1337, 8, 1293, 16...
3ADAPT-QAOAGraph_11160.01[-2.674999999999453, -3.054999999160295, -3.38...-3.69BFGSall_pool1.01.0[['ZYIIII'], ['IIZYII'], ['ZIIIIY'], ['ZIIIYI']][11, 47, 27, 23][-0.0003976113260684765, 0.0006218543845905527...[-0.7853976823592153, -0.7853980176827251, -0....0.97425510111('100100', '011011')4True1.734386pid1777360_25-08-21__00_59_resultspid1777360_25-08-21erdos_renyi[[1, 2, 0.84], [1, 3, 0.68], [1, 4, 0.51], [1,...19.236133pid1777360_25-08-21__00_59_graphs[[1, 2, 0.84], [1, 3, 0.68], [1, 4, 0.51], [1,...1214pid1777360_25-08-21_^_1FalseTrue[bos, (1, 2), 0.84, (1, 3), 0.68, (1, 4), 0.51...[1, 6, 96, 18, 163, 10, 1111, 8, 1034, 16, 916...train[[[1, 6, 96, 18, 163, 10, 1111, 8, 1034, 16, 9...
4ADAPT-QAOAGraph_44160.01[-2.194999999966217, -2.6599999999399873, -2.9...-3.33BFGSall_pool1.01.0[['IIYIZI'], ['IZIIIY'], ['YIIZII'], ['IIXIXI'...[50, 43, 18, 48, 16][1.1212799835079733e-05, 0.008775041659259374,...[-0.7853989447692689, -0.7854053059827186, -0....0.97089311100('100011', '011100')5True3.711532pid1777360_25-08-21__00_59_resultspid1777360_25-08-21erdos_renyi[[1, 4, 0.59], [2, 4, 0.08], [2, 5, 0.41], [2,...15.203894pid1777360_25-08-21__00_59_graphs[[1, 4, 0.59], [2, 4, 0.08], [2, 5, 0.41], [2,...715pid1777360_25-08-21_^_4FalseTrue[bos, (1, 4), 0.59, (2, 4), 0.08, (2, 5), 0.41...[1, 10, 95, 5, 984, 17, 1919, 13, 164, 19, 164...train[[[1, 10, 95, 5, 984, 17, 1919, 13, 164, 19, 1...
\n", + "
" + ], + "text/plain": [ + " method graph_name graph_num trial_num n_nodes init_gamma \\\n", + "0 ADAPT-QAOA Graph_2 2 1 6 0.10 \n", + "1 ADAPT-QAOA Graph_5 5 1 6 0.01 \n", + "2 ADAPT-QAOA Graph_3 3 1 6 0.01 \n", + "3 ADAPT-QAOA Graph_1 1 1 6 0.01 \n", + "4 ADAPT-QAOA Graph_4 4 1 6 0.01 \n", + "\n", + " energy_list true_energy optimizer \\\n", + "0 [-3.359999999537821, -3.7949999999952433, -4.0... -4.50 BFGS \n", + "1 [-1.8899999987838123, -2.164999996558533, -2.4... -2.77 BFGS \n", + "2 [-3.179999999999917, -3.5199999985319583, -3.7... -4.48 BFGS \n", + "3 [-2.674999999999453, -3.054999999160295, -3.38... -3.69 BFGS \n", + "4 [-2.194999999966217, -2.6599999999399873, -2.9... -3.33 BFGS \n", + "\n", + " pool_type edge_weight_scaling_coef edge_weight_norm_coef \\\n", + "0 all_pool 1.0 1.0 \n", + "1 all_pool 1.0 1.0 \n", + "2 all_pool 1.0 1.0 \n", + "3 all_pool 1.0 1.0 \n", + "4 all_pool 1.0 1.0 \n", + "\n", + " mixer_pool_pauli_word \\\n", + "0 [['YIZIII'], ['IZIYII'], ['IIIIYZ'], ['IIIIXX']] \n", + "1 [['YZIIII'], ['IZIYII'], ['IIIIYZ'], ['ZIYIII'... \n", + "2 [['IIYZII'], ['ZIIIIY'], ['IZIIYI'], ['YIIIIY'... \n", + "3 [['ZYIIII'], ['IIZYII'], ['ZIIIIY'], ['ZIIIYI']] \n", + "4 [['IIYIZI'], ['IZIIIY'], ['YIIZII'], ['IIXIXI'... \n", + "\n", + " mixer_pool_index \\\n", + "0 [14, 35, 66, 64] \n", + "1 [10, 35, 66, 15, 64] \n", + "2 [46, 27, 39, 25, 36, 45] \n", + "3 [11, 47, 27, 23] \n", + "4 [50, 43, 18, 48, 16] \n", + "\n", + " gamma_coef \\\n", + "0 [0.01745972054338572, -0.019733899238238935, 0... \n", + "1 [2.8691569393532094e-05, -0.000100819259514377... \n", + "2 [6.274385167217878e-07, -5.3271375143128285e-0... \n", + "3 [-0.0003976113260684765, 0.0006218543845905527... \n", + "4 [1.1212799835079733e-05, 0.008775041659259374,... \n", + "\n", + " beta_coef approx_ratio cut_adapt \\\n", + "0 [-0.7856887670086344, -0.7854085503209243, -0.... 0.988895 1101 \n", + "1 [-0.7853997378605556, -0.785397455801531, -0.7... 1.000000 100101 \n", + "2 [-0.7854006450112557, -0.785394076248958, -0.7... 1.000000 100110 \n", + "3 [-0.7853976823592153, -0.7853980176827251, -0.... 0.974255 10111 \n", + "4 [-0.7853989447692689, -0.7854053059827186, -0.... 0.970893 11100 \n", + "\n", + " cut_classical num_layers optimizer_success_flag elapsed_time \\\n", + "0 ('001101', '110010') 4 True 2.087118 \n", + "1 ('100101', '011010') 5 True 3.434215 \n", + "2 ('100110', '011001') 6 True 4.721877 \n", + "3 ('100100', '011011') 4 True 1.734386 \n", + "4 ('100011', '011100') 5 True 3.711532 \n", + "\n", + " worker_id_x prefix g_method \\\n", + "0 pid1777360_25-08-21__00_59_results pid1777360_25-08-21 erdos_renyi \n", + "1 pid1777360_25-08-21__00_59_results pid1777360_25-08-21 erdos_renyi \n", + "2 pid1777360_25-08-21__00_59_results pid1777360_25-08-21 erdos_renyi \n", + "3 pid1777360_25-08-21__00_59_results pid1777360_25-08-21 erdos_renyi \n", + "4 pid1777360_25-08-21__00_59_results pid1777360_25-08-21 erdos_renyi \n", + "\n", + " edgelist_json H_frob_norm \\\n", + "0 [[1, 3, 0.97], [1, 4, 0.4], [1, 6, 0.16], [2, ... 24.329998 \n", + "1 [[1, 2, 0.93], [1, 3, 0.24], [2, 4, 0.55], [2,... 12.461396 \n", + "2 [[1, 2, 0.41], [1, 3, 0.6], [1, 4, 0.28], [1, ... 23.108302 \n", + "3 [[1, 2, 0.84], [1, 3, 0.68], [1, 4, 0.51], [1,... 19.236133 \n", + "4 [[1, 4, 0.59], [2, 4, 0.08], [2, 5, 0.41], [2,... 15.203894 \n", + "\n", + " worker_id_y \\\n", + "0 pid1777360_25-08-21__00_59_graphs \n", + "1 pid1777360_25-08-21__00_59_graphs \n", + "2 pid1777360_25-08-21__00_59_graphs \n", + "3 pid1777360_25-08-21__00_59_graphs \n", + "4 pid1777360_25-08-21__00_59_graphs \n", + "\n", + " edgelist_list edgelist_list_len \\\n", + "0 [[1, 3, 0.97], [1, 4, 0.4], [1, 6, 0.16], [2, ... 11 \n", + "1 [[1, 2, 0.93], [1, 3, 0.24], [2, 4, 0.55], [2,... 9 \n", + "2 [[1, 2, 0.41], [1, 3, 0.6], [1, 4, 0.28], [1, ... 13 \n", + "3 [[1, 2, 0.84], [1, 3, 0.68], [1, 4, 0.51], [1,... 12 \n", + "4 [[1, 4, 0.59], [2, 4, 0.08], [2, 5, 0.41], [2,... 7 \n", + "\n", + " num_connected_comp n_layers graph_id only_qaoa_circ \\\n", + "0 1 4 pid1777360_25-08-21_^_2 False \n", + "1 1 5 pid1777360_25-08-21_^_5 False \n", + "2 1 6 pid1777360_25-08-21_^_3 False \n", + "3 1 4 pid1777360_25-08-21_^_1 False \n", + "4 1 5 pid1777360_25-08-21_^_4 False \n", + "\n", + " has_emb token_seq_round_d2 \\\n", + "0 True [bos, (1, 3), 0.97, (1, 4), 0.4, (1, 6), 0.16,... \n", + "1 True [bos, (1, 2), 0.93, (1, 3), 0.24, (2, 4), 0.55... \n", + "2 True [bos, (1, 2), 0.41, (1, 3), 0.6, (1, 4), 0.28,... \n", + "3 True [bos, (1, 2), 0.84, (1, 3), 0.68, (1, 4), 0.51... \n", + "4 True [bos, (1, 4), 0.59, (2, 4), 0.08, (2, 5), 0.41... \n", + "\n", + " token_int_seq_round_d2 label \\\n", + "0 [1, 18, 671, 10, 775, 16, 1926, 11, 1153, 5, 1... train \n", + "1 [1, 6, 164, 18, 914, 5, 1624, 13, 406, 7, 1470... train \n", + "2 [1, 6, 1919, 18, 1238, 10, 1337, 8, 1293, 16, ... train \n", + "3 [1, 6, 96, 18, 163, 10, 1111, 8, 1034, 16, 916... train \n", + "4 [1, 10, 95, 5, 984, 17, 1919, 13, 164, 19, 164... train \n", + "\n", + " token_int_seq_round_d2_sw \n", + "0 [[[1, 18, 671, 10, 775, 16, 1926, 11, 1153, 5,... \n", + "1 [[[1, 6, 164, 18, 914, 5, 1624, 13, 406, 7, 14... \n", + "2 [[[1, 6, 1919, 18, 1238, 10, 1337, 8, 1293, 16... \n", + "3 [[[1, 6, 96, 18, 163, 10, 1111, 8, 1034, 16, 9... \n", + "4 [[[1, 10, 95, 5, 984, 17, 1919, 13, 164, 19, 1... " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pickle\n", + "import pandas as pd\n", + "\n", + "with open(f'{path_save}/combined_res_tok_shf_df.pkl', 'rb') as f:\n", + " data = pickle.load(f)\n", + "\n", + "if isinstance(data, pd.DataFrame):\n", + " pd.set_option('display.max_columns', None)\n", + " display(data) # Show the whole DataFrame\n", + "\n", + " # Specify the row index you want to view\n", + " #row_index = 0 # Change this to the desired row number\n", + " #print(f\"Row {row_index}:\")\n", + " #print(data.iloc[row_index])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "711e4692", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value at row 0, column 'token_seq_round_d2':\n", + "['bos', (1, 3), 0.97, (1, 4), 0.4, (1, 6), 0.16, (2, 3), 0.54, (2, 4), 0.87, (2, 6), 0.53, (3, 4), 0.98, (3, 5), 0.23, (3, 6), 0.27, (4, 5), 0.21, (5, 6), 0.59, 'end_of_graph', 'new_layer_p', 14, -0.79, 0.02, 'new_layer_p', 35, -0.79, -0.02, 'new_layer_p', 66, -0.79, 0.08, 'new_layer_p', 64, -0.79, 1.97, 'eos']\n" + ] + } + ], + "source": [ + "row_index = 0 # Change this to the desired row number\n", + "column_name = \"token_seq_round_d2\" # Change this to your column name\n", + "\n", + "print(f\"Value at row {row_index}, column '{column_name}':\")\n", + "print(data.iloc[row_index][column_name])" + ] + }, + { + "cell_type": "markdown", + "id": "ba379a4c", + "metadata": {}, + "source": [ + "## 3- GPT model training:\n", + "\n", + "Here, the model is trained from scratch. \n", + "\n", + "We are perfoming training on small datapoints we generated for `n_nodes =8` and they are stored in `save_dir_3k.zip` inside the `~/nanoGPT/data` folder. We have 2398 samples for training; and 599 samples for evaluation. Unzip the file for this experiment. The purpose of this small experiment is to only explain how QAOA-GPT works, and therefore, we are not expecting to produce results from QAOA-GPT as shown in the paper. User who is interested to employ QAOA-GPT for producing more accurate results as shown in the paper should perfom training on large data size. \n", + " \n", + "- The file used for training is called `train_pad_gemb_ar_eval.py`. \n", + "\n", + "- Output `ckpt` files will be stored inside nanoGPT folder. Output folder name is defined in the config file. For this experiment it is called `out-save_dir_3k`\n", + "\n", + "\n", + "To do: During the training the GPT model is calling ADAPT-QAOA each `eval_ar_every` defined in the config file. It calculates the approximation ratio from the circuits that GPT synthesize using evaluation samples. This part where we call ADAPT-QAOA will be expensive if user has large data for evalaution and so parallelization can accelerate training. The current version is not parallelized yet.\n" + ] + }, + { + "cell_type": "markdown", + "id": "fb4f5bde", + "metadata": {}, + "source": [ + "\n", + "\n", + "The following Python code shows how we train the GPT model using the prepared data:\n", + "\n", + "```python\n", + "import os\n", + "\n", + "current_dir = os.getcwd()\n", + "nanoGPT_dir = os.path.join(current_dir, 'nanoGPT')\n", + "sample_data = 'save_dir_3k'\n", + "save_dir_data = os.path.join(nanoGPT_dir, 'data', sample_data)\n", + "\n", + "\n", + "# Check if the directory exists\n", + "if not os.path.exists(save_dir_data):\n", + " print(f\"Directory {save_dir_data} does not exist!\")\n", + " # Uncomment to create the directory if it doesn't exist\n", + " # os.makedirs(save_dir_data, exist_ok=True)\n", + "\n", + "# Check if the config file exists\n", + "config_file = os.path.join(save_dir_data, 'train_adapt_gpt_config.py')\n", + "if not os.path.exists(config_file):\n", + " print(f\"Config file {config_file} does not exist!\")\n", + "\n", + "# If both directory and config file exist, change directory and run\n", + "if os.path.exists(save_dir_data) and os.path.exists(config_file):\n", + " %cd $nanoGPT_dir\n", + " !python train_pad_gemb_ar_eval.py $config_file\n", + "```\n", + "\n", + "To execute this code, remove the code block formatting and convert the cell back to a code cell.\n", + "\n", + "Note: The error message `Error in eval_ansatz:..` you observe during the training comes from the generated bad samples during the evaluation. " + ] + }, + { + "cell_type": "markdown", + "id": "f3f2b177", + "metadata": {}, + "source": [ + "## 4- QAOA-GPT inference model\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "Once trained, QAOA-GPT can generate quantum circuits for new unseen graphs. \n", + "- User Input: A user supplies an input graph, typically as an edgelist.\n", + "- Processing Input: The system automatically computes a fixed-length graph embedding (using FEATHER) and tokenizes the graph structure.\n", + "- Circuit Generation: Both the graph embedding and the tokenized graph are passed to the QAOA-GPT model.\n", + "- Output: QAOA-GPT generates a QAOA quantum circuit for the given input. These circuits are then ready for execution on a quantum device or classical simulator.\n", + "\n", + "This entire process bypasses the need for iterative optimization of variational algorithms, significantly speeding up circuit generation.\n", + "\n", + "Below, we will generate new graphs and we will use the trained QAOA-GPT to directly synthesize circuits and execute them." + ] + }, + { + "cell_type": "markdown", + "id": "7da9250c", + "metadata": {}, + "source": [ + "### Generate circuits from the trained QAOA-GPT model above.\n", + "\n", + "### Note: \n", + "you might encounter an error with PyTorch's CUDA dependencies when running the cell below. If you restart the notebook, and only run this cell again, the issue will be resolved. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c486c80e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.10/dist-packages/IPython/core/magics/osm.py:417: UserWarning: This is now an optional IPython functionality, setting dhist requires you to install the `pickleshare` library.\n", + " self.shell.db['dhist'] = compress_dhist(dhist)[-100:]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/cudaq/cuda-quantum/docs/sphinx/applications/python\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "#current_dir = os.getcwd()\n", + "current_dir = '/home/cudaq/cuda-quantum/docs/sphinx/applications/python'\n", + "%cd {current_dir}\n", + "\n", + "from qaoa_gpt_src.model_interface import QAOA_GPT\n", + "import pandas as pd\n", + "import numpy as np\n", + "import networkx as nx\n", + "import random\n", + "from tqdm import tqdm\n", + "from collections import defaultdict, Counter\n", + "import json\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "markdown", + "id": "3cf58bbf", + "metadata": {}, + "source": [ + "Note: to call the trained `QAOA_GPT` to synthesize circuits, user needs to provide `model_ckpt`. This is the checkpoint file user wants to employ for the inference. The ckpt files can be found in the output directory from the GPT model training. During the training process, the checkpoint files are saved with: \n", + "- the format `ckpt_{iter}_gemb__ar_{approx_ratio}__er_{energy_ratio}.pt` (with AR/ER values)\n", + "- the format `ckpt_{iter}_gemb.pt` (basic checkpoint). \n", + "\n", + "User should use the last or their best checkpoint file for inference. Below, we will use `ckpt-3500_gemb__ar_0_96254__er_0_00605.pt`.\n", + "\n", + "User will need to unzip the `out-save_dir_3k.zip`. This folder has the inferenece model pre-trained with 3K data points.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0b064ff9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading config from: /home/cudaq/cuda-quantum/docs/sphinx/applications/python/nanoGPT/data/save_dir_3k/train_adapt_gpt_config.py\n", + "Initiating nanoGPT model with padding support\n", + "number of parameters: 11.60M\n", + "Pool type: qaoa_mixer\n", + "Using graph embeddings: True\n", + "Number of nodes: 8\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "# Get current directory for relative paths\n", + "#current_dir = os.getcwd()\n", + "current_dir = '/home/cudaq/cuda-quantum/docs/sphinx/applications/python'\n", + "nanogpt_dir = os.path.join(current_dir, 'nanoGPT')\n", + "\n", + "# Define the input and output directories and checkpoint file\n", + "gpt_input_dir = 'save_dir_3k'\n", + "gpt_output_dir = 'out-save_dir_3k'\n", + "ckpt_file = 'ckpt_3500_gemb__ar_0_96205__er_0_00336.pt'\n", + "\n", + "# Use relative paths instead of absolute paths\n", + "path = os.path.join(current_dir, 'nanoGPT')\n", + "qaoa_gpt_n8_obj = QAOA_GPT(\n", + " model_ckpt = os.path.join(nanogpt_dir, gpt_output_dir, ckpt_file),\n", + " data_dir = os.path.join(nanogpt_dir, 'data', gpt_input_dir),\n", + " config_file = os.path.join(nanogpt_dir, 'data', gpt_input_dir, 'train_adapt_gpt_config.py'),\n", + " temp_folder = 'temp_data',\n", + " device = 'cuda' # You may want to change to 'cpu' if CUDA is not available\n", + ")\n", + "\n", + "# Print the values\n", + "print(f\"Pool type: {qaoa_gpt_n8_obj.pool_type}\")\n", + "print(f\"Using graph embeddings: {qaoa_gpt_n8_obj.use_graph_emb}\")\n", + "print(f\"Number of nodes: {qaoa_gpt_n8_obj.n_nodes}\")" + ] + }, + { + "cell_type": "markdown", + "id": "84a1b39e", + "metadata": {}, + "source": [ + "### Generate random graphs\n", + "\n", + "We will generate `n_graphs` with `n_nodes = 8`. We choose 8 nodes because QAOA-GPT model in this tutorial is trained on graphs with 8 nodes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7acaa57a", + "metadata": {}, + "outputs": [], + "source": [ + "def add_weights_to_nx_graph(nx_graph):\n", + " for u, v in nx_graph.edges():\n", + " cur_weight = round(random.uniform(0, 1), 2)\n", + " while cur_weight == 0:\n", + " cur_weight = round(random.uniform(0, 1), 2)\n", + " nx_graph[u][v]['weight'] = cur_weight\n", + " return nx_graph" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dd8b9021", + "metadata": {}, + "outputs": [], + "source": [ + "tqdm.pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "1126a691", + "metadata": {}, + "outputs": [], + "source": [ + "n_graphs = 200\n", + "n_nodes = 8" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1ad5d9a7", + "metadata": {}, + "outputs": [], + "source": [ + "graphs_edgelist_list_dict = dict()\n", + "\n", + "er_graphs_edgelist_list_dict = dict()\n", + "for i in range(n_graphs):\n", + " p = random.randrange(6,9) / 10\n", + " cur_graph = nx.erdos_renyi_graph(\n", + " n=n_nodes,\n", + " p=p\n", + " )\n", + " er_graphs_edgelist_list_dict[f'er_graph_{i}'] = add_weights_to_nx_graph(cur_graph)\n", + "\n", + "graphs_edgelist_list_dict.update(er_graphs_edgelist_list_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "88cef046", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "EdgeDataView([(0, 1, {'weight': 0.9}), (0, 3, {'weight': 0.53}), (0, 4, {'weight': 0.93}), (0, 6, {'weight': 0.22}), (0, 7, {'weight': 0.45}), (1, 6, {'weight': 0.82}), (2, 3, {'weight': 0.75}), (2, 5, {'weight': 0.41}), (3, 4, {'weight': 0.05}), (3, 5, {'weight': 0.34}), (3, 6, {'weight': 0.6}), (3, 7, {'weight': 0.56}), (4, 5, {'weight': 0.41})])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graphs_edgelist_list_dict['er_graph_2'].edges(data=True)" + ] + }, + { + "cell_type": "markdown", + "id": "ed703c87", + "metadata": {}, + "source": [ + "### Generate circuit with QAOA-GPT model" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "370622c9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Preparing graphs...: 0%| | 0/200 [00:00 1.0 = more random, in predictions\n", + " top_k=200, # retain only the top_k most likely tokens, clamp others to have 0 probability\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "06a52395", + "metadata": {}, + "source": [ + "Note: the warning above `Restricted license - for non-production use only - expires 2026-11-23` comes from `gurobipy` that is used to calculate the optimum classical value. \n", + "\n", + "To do: we will update this function later and allow different options to estimate the optimum classical value. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "87b53c38", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
graphn_edgesq_circuitsadapt_circuitadapt_full_argraph_prefixenergy_gurobilabelgraph_w_pygraph_weight_norm
0[(1, 2), 0.3, (1, 3), 0.8, (1, 4), 0.04, (1, 5...22[[new_layer_p, 1, -0.52, 0.41, new_layer_p, 1,...[]Noneer_graph_10-7.21test_interactive[[1, 2, 0.3], [1, 3, 0.8], [1, 4, 0.04], [1, 5...1.0
1[(1, 2), 0.59, (1, 3), 0.54, (1, 4), 0.05, (1,...22[[new_layer_p, 1, -0.52, 0.46, new_layer_p, 1,...[]Noneer_graph_16-6.54test_interactive[[1, 2, 0.59], [1, 3, 0.54], [1, 4, 0.05], [1,...1.0
2[(1, 2), 0.74, (1, 3), 0.06, (1, 4), 0.07, (1,...22[[new_layer_p, 1, -0.49, 0.35, new_layer_p, 1,...[]Noneer_graph_35-8.80test_interactive[[1, 2, 0.74], [1, 3, 0.06], [1, 4, 0.07], [1,...1.0
\n", + "
" + ], + "text/plain": [ + " graph n_edges \\\n", + "0 [(1, 2), 0.3, (1, 3), 0.8, (1, 4), 0.04, (1, 5... 22 \n", + "1 [(1, 2), 0.59, (1, 3), 0.54, (1, 4), 0.05, (1,... 22 \n", + "2 [(1, 2), 0.74, (1, 3), 0.06, (1, 4), 0.07, (1,... 22 \n", + "\n", + " q_circuits adapt_circuit \\\n", + "0 [[new_layer_p, 1, -0.52, 0.41, new_layer_p, 1,... [] \n", + "1 [[new_layer_p, 1, -0.52, 0.46, new_layer_p, 1,... [] \n", + "2 [[new_layer_p, 1, -0.49, 0.35, new_layer_p, 1,... [] \n", + "\n", + " adapt_full_ar graph_prefix energy_gurobi label \\\n", + "0 None er_graph_10 -7.21 test_interactive \n", + "1 None er_graph_16 -6.54 test_interactive \n", + "2 None er_graph_35 -8.80 test_interactive \n", + "\n", + " graph_w_py graph_weight_norm \n", + "0 [[1, 2, 0.3], [1, 3, 0.8], [1, 4, 0.04], [1, 5... 1.0 \n", + "1 [[1, 2, 0.59], [1, 3, 0.54], [1, 4, 0.05], [1,... 1.0 \n", + "2 [[1, 2, 0.74], [1, 3, 0.06], [1, 4, 0.07], [1,... 1.0 " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qaoa_gpt_circ_df[:3]" + ] + }, + { + "cell_type": "markdown", + "id": "6ab1c3d4", + "metadata": {}, + "source": [ + "### Evaluate circuits." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ba15acbf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ">>> eval_adapt_gpt_circ_cudaq CALLED <<<\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Processing graphs: 0%| | 0/200 [00:00 0)]\n", + "\n", + "print(\"\\nAfter filtering:\")\n", + "print(\"Filtered DataFrame shape:\", filtered_df.shape)\n", + "#print(\"Sample of filtered adapt_gpt_energies:\")\n", + "#print(filtered_df['adapt_gpt_energies'].head())\n", + "\n", + "# Calculate metrics with the filtered data\n", + "filtered_df['best_adapt_gpt_ar'] = filtered_df.apply(\n", + " lambda x: max([val / x['energy_gurobi'] for val in x['adapt_gpt_energies']]) \n", + " if len(x['adapt_gpt_energies']) > 0 else np.nan,\n", + " axis=1\n", + ")\n", + "\n", + "filtered_df['avg_adapt_gpt_ar'] = filtered_df.apply(\n", + " lambda x: np.mean([val / x['energy_gurobi'] for val in x['adapt_gpt_energies']]) \n", + " if len(x['adapt_gpt_energies']) > 0 else np.nan,\n", + " axis=1\n", + ")\n", + "\n", + "# Create best_adapt_gpt_energy column\n", + "filtered_df['best_adapt_gpt_energy'] = filtered_df.apply(\n", + " lambda x: max(x['adapt_gpt_energies']) if len(x['adapt_gpt_energies']) > 0 else np.nan,\n", + " axis=1\n", + ")\n", + "\n", + "# Add this code to check the adapt_gpt_energies column structure\n", + "list_count = 0\n", + "for val in filtered_df['adapt_gpt_energies']:\n", + " if len(val) > 1:\n", + " #print(f\"Example list with multiple values: {val[:5]}... (length: {len(val)})\")\n", + " list_count += 1\n", + " if list_count >= 3:\n", + " break\n", + "\n", + "print(f\"Difference between best and avg AR: {(filtered_df['best_adapt_gpt_ar'] - filtered_df['avg_adapt_gpt_ar']).abs().mean()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "416275e5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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6+tLnz59mDp1KgEBAcXyeD3ce++9Slm7du3KY489pvx25swZp7IfO3aMH3/8kSNHjnDp0iUyMjKQZZnQ0FBat27N2LFj6dChg5K+T58+JCYmOl3PsTwdO3Zk0aJF1+WC/ueff7J06VKOHDlCRkYGXl5e1K9fn379+jF69Gi8vLxuqPyyLLN48WK+//57zp49W2I9mzt3Lh999BEAERERbNiwAY2mMAzQqVOnuPvuu5Xtn376idatW5c7L5s2bWL37t1O97A0SqsvUPZUiKLHjRkzhvfff58DBw7g5eVF//79mTJlCj4+PqxatYoFCxZw+vRpAgICuOuuu3juueec2pGiZGdn8+GHH/LHH3+QmppKnTp1GDNmDA888ECxdrS87/Oi9eGvv/5i7dq1LFmyhISEBHr27HldHfXlac+mT59erAP1k08+4ZNPPgHgzTffdPqGuRYNGza87g6mXbt2MXfuXA4dOoTBYKBz585MmTLlmsetXbuW+fPnc/LkSaWNeO6553jmmWdKfWYAEhIS+Prrr9myZQsXL17EZrNRu3Zt+vTpwyOPPEJwcLBT+tTUVD7//HP++ecfEhMTsVgsBAQEULNmTVq1asWQIUNo06bN9d0YwS2HMLoFAhdhtVo5dOiQU29yjRo1nNLk5eXxr3/9i61btzrtT0tL4++//+bvv/9m/PjxTkGYin58AFgsFnJyckhISGDdunWKYXGzjBgxQjG616xZw6xZs5zmRDoaYXfddReenp6kpKRwzz33kJqa6nSuy5cvc/nyZfbt20e9evXKZXQvW7ZM+X9ERATPP/88Tz75JABHjhzhxIkTisEwcOBAXn/9dYxGI0ajkfXr1zt9PP7111+K8eTn50e/fv0A+8fS9OnTWb58udO1zWYzO3bsYMeOHaxbt47333+/VPfUuXPnsnv37mL7T58+zTfffFNsf8E92bBhA0899RSTJk1yuu6ECROcznf69GleeeWVMkeVK6IcJbF27VoyMjKU7bfeeouxY8diNpsVj4NrBcCZOXMm27dvdyr/V199xZ49e1i8eHGp820nTpzodNz58+eZN28eR44c4eOPPy6xAyE3N5cxY8aU2NF15swZHnnkES5duuS0//Lly6xZs4Y1a9bw4IMP8uKLLwLQpEkTJk+erMx/fO+99+jVqxdRUVHMmzdPMbiDgoKYPXt2uTs0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WWRZJjc3V9QTFSE0UR9CE/UhNFEfQhP1ITRRH9VFE2F0q56yRzj37dtH06ZNmThxYoVczWw2l/uYxo0b07hxY/bv3++032Qy0alTJxo3bsyOHTsqJH8VwW+//UbTpk2ZNWtWsd927NihlKdx48Z07tyZxx57jBMnTpT7Ounp6UyePJl27drRoUMHXnjhBXJycso85ty5czz55JN07tyZdu3a8eyzz3Lp0iWnNH369HHKY+PGjfnss8+c0mzatIlRo0bRtm1bOnfuzFNPPcX58+dLvOaePXto1qwZQ4cOddpvuzrKXYCXQYemEgfcO0UHK//fGafeed2yLHPhwgW3b/yrE0IT9SE0UR9CE/UhNFEfQhP1UV00EUa3m7NkyRLGjh3Lrl27SEpKclk+atasybJly5z2rVmzBm9vbxflqHSWLFnChAkT+P3338nPzy8xzerVq9m8eTNffPEFJpOJxx9/HJPJVGLa0pgyZQqnT59m4cKFzJ8/n927d/Of//yn1PS5ubk88sgjSJLE119/zffff4/ZbOapp57CZrM5pX366afZvHmz8jd27Fjlt4SEBP7973/TuXNnli9fzhdffEFaWhpPPfVUsWtmZmYybdo0unTpUvw3oxmz1X5dL4MWfWVa3EC4vydRIfbn5WRSFum55bvfAoFAIBAIBAKBGhFGtxuTk5PDqlWrGD16NL179+aXX35Rfps8eTLPPvusU3qz2UynTp349ddfAcjOzmby5Mm0adOG7t2789VXX/Hoo4/yxhtvlDsvw4YN4/fffycvL0/Zt3TpUoYNG1Ys7cWLF3nmmWfo0KEDHTt25F//+pfTKOzBgwcZP348nTp1on379owdO5YjR444naNx48b8/PPPPPnkk7Ru3Zp+/fqxbt26a+YzISGBffv2MXHiROrVq8dff/1VYrqQkBBq1KhB8+bNeeihh7h48SKxsbHXeTfgzJkzbNq0iddee43WrVvToUMHZs6cye+//15q58jevXtJTExk9uzZygj27NmzOXr0KNu3b3dK6+PjQ40aNZQ/x86NI0eOYLPZePbZZ4mKiqJ58+Y88sgjHDt2rJgnw8svv8zdd99NmzZtnPZbbTKpOYVGb6hv1cwr73x1tFuWYVd8WqVfTyAQCAQCgcBksbHh+GVm/3Gc9zddYvYfx9lw/DImi+3aBwsE14Ewut2YP/74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06asX7+egwcPcujQIR5//HGH46RFixb07t2bmTNnsm/fPg4cOMDzzz+Pn5+frde2d+/edO7cmQcffJAtW7Zw8uRJdu3axdy5c/n7779L3W9kZCRPPvkkn3/+OXPnzuXYsWMkJiayYMEC3nzzTR555BFatRIvNZYsWcLQoUNL/H/Gjh1LWloamzdvJiAggFmzZrFx40aef/55Dh06xMmTJ/n+++95+umnGTp0KNdddx35+fmsWLGC66+/vtTt7d271yHKd3H++9//YrFYGDNmDHFxcRw7doz4+HhWrFhBfHz8ZVOOVYdNmzZV+FgBeP/999m+fTtHjhxhxowZ1K1b1xZ4D4ST+tJLL7F3717279/P008/TefOnUukYitK7969adWqFU888QT//PMP+/btY/r06fTo0cM2VP2///0vjRo14v7772fGjBlYrVZef/31SrVVURQmTpzIxx9/THp6Oo0aNcLHx4dFixaRlJTExo0bef/99x3WiYqKQlEUNm3axIULFxwCDxbSrFkzrrnmGp5//nl27tzJoUOHePLJJ0ukenM20umWOCBvxtpDaqI9pCbaw2M0CWlUuQBqAF1uh1u/hpHzSx/6qlE8RpNahNREe6iqypIlS+jdu3epw4KHDh3K/v37bam3Ro4cydGjR2nQoAFXXnmlQ91HHnmEBx54gI8++ojhw4dzzz33sGnTpsv2PM+YMYOQkBBuvfVWpk6dSr9+/Rzmb4MYgl6vXj1uv/12HnroIcaNG0dgYCC+vr6AcJA+/vhjunfvztNPP82wYcN47LHHOHXqVLk5lu+66y7mz5/Pzp07GTt2LNdeey2vvfYar732GlOnTgWwtb9o4LZCgoODueqqq2zO5rBhw/jiiy9ITk7m9ttvZ9iwYXz++edMnTqVuXPnoigKP//8M+np6bae/aK0aNGCmJiYEs5rUaKjo1m+fDm9e/fmrbfeYtSoUYwZM4Yvv/ySSZMm2XrTa4LKHCsgUs298sorjB49mtTUVD744AOH6QZ+fn7ce++9PP7444wfP56AgADmzp1brg2KovD+++8TEhLChAkTuOuuu2jSpIltvR9++IHffvuNN954A4PBQEBAAG+++Sbff/89v/76a6Xae9NNN9mmFISFhfHaa6+xdu1ahg8fzieffFIij3uDBg34z3/+w1tvvUXv3r156aWXSt3uq6++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bMbGxrC1tVU6i9q7d2/s2rWryjaquxVYXQrQ9u3bixO6Xb9+Hf379wcAxMbGKi3TGFq0aAFTU1MUFBSgoKAAd+7cEWc2rxhPOXt7e/HvlJQU5OTkiJPWxcTEVFm+rvtUEAT4+PjAx8cHAFBSUoKVK1dix44dyMzMRHR0NO8JT0SkRVh0ExER1ZKenh7efvttzJs3r9plHBwccPPmTQDA/PnzMXToUPz6669Ks1o3pB07dsDa2hqtW7dWulbZx8cHhoaGcHNzg4uLC27fvo3Lly9j/vz5GD58OGQyGdLS0nD9+nWcOHGiyqzjdTVq1Cix6P7yyy/x+PFj6OnpYdWqVeIy5RPG1Ye7d+/i6tWrSm3Gxsbo2rUr9PX10b9/fxw5cgQAMG/ePLz77rtIT0/Hzp07q2zLysoKnp6eiI6ORlFRET766CNMnToV8fHxKm+TVtd9OmfOHDRt2hQ9evRAq1atUFpaqjRPQMXbqRERkeZj0U1ERFQHvr6+CA8Pr3Yis9deew3//e9/AQAXL17ExYsXIZPJ4ODgUO069cnW1hZLlixRajM1NcWHH34IoOzAwfLly8XbWx08eBAHDx6s9zimT5+Os2fP4urVq0hLS8NHH32k9HqvXr0wffr0enu/7777rsqEaG3atMGpU6cAAB988AHOnTuH/Px8xMfH47333gNQdrZd1bXlISEhmDJlCuRyOS5cuIALFy4AKLuvesVbsgF136d5eXn47bffsH///iqvWVtb48UXX6xT34mISNp4yzAiIqI6MDAwwKxZs6p93dvbG5988glatWoFIyMjuLu7Y/PmzejevXujxLdw4UK89dZbsLGxgZGREXr06IGdO3eKw6kBoHPnzjhw4AAmT56Mdu3awdDQEObm5nBxccHkyZPFW3k9DyMjI2zbtg3BwcFwdXWFiYkJjI2N4eLiguDg4Ea9RzdQVlzv3LkTvXr1gpGREWxsbPDWW2/hs88+U7m8h4cHtmzZIs423qZNG3zyyScYP368uIyJiYn4d1326RtvvIGRI0fC3t4epqamkMlkaNmyJUaPHo09e/bU+yR9RESkXnpCbWdiISIiItIRgiCovIb9ww8/FIeph4eHY8iQIY0dGhERaRgOLyciIiKqJC0tDV988QUmT54MV1dXFBUV4dixY+I13ZaWlujbt6+aoyQiIk3AopuIiIhIhfPnz+P8+fNV2g0NDfH1119Xezs3IiKiilh0ExEREVViaWmJiRMn4tq1a3jw4AHkcjlsbW3Rq1cvvPnmm3Bzc1N3iEREpCF4TTcRERERERFRA+Hs5UREREREREQNhEU3ERERERERUQNh0U1ERERERETUQFh0ExERERERETUQFt1EREREREREDYRFNxEREREREVEDYdFNRERERERE1EBYdBMRERERERE1EBbdRERERERERA3k/wG/2OZ1PptmfQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average QAOA-GPT best approximation ratio: 0.9663\n", + "Average QAOA-GPT mean approximation ratio: 0.9584\n", + "Difference between best and avg AR: 0.0079\n" + ] + } + ], + "source": [ + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Set the style\n", + "sns.set_style(\"whitegrid\")\n", + "plt.figure(figsize=(10, 6))\n", + "\n", + "# Group by n_edges and calculate mean for both metrics\n", + "grouped_best = filtered_df[['n_edges', 'best_adapt_gpt_ar']].groupby('n_edges').mean().reset_index()\n", + "grouped_avg = filtered_df[['n_edges', 'avg_adapt_gpt_ar']].groupby('n_edges').mean().reset_index()\n", + "\n", + "# Create the plot with two lines\n", + "plt.plot(\n", + " grouped_best['n_edges'], \n", + " grouped_best['best_adapt_gpt_ar'], \n", + " 'o-',\n", + " color='#1f77b4',\n", + " linewidth=2,\n", + " markersize=8,\n", + " alpha=0.8,\n", + " label='Best QAOA-GPT Approx Ratio'\n", + ")\n", + "\n", + "plt.plot(\n", + " grouped_avg['n_edges'], \n", + " grouped_avg['avg_adapt_gpt_ar'], \n", + " 's--',\n", + " color='#ff7f0e',\n", + " linewidth=2,\n", + " markersize=8,\n", + " alpha=0.8,\n", + " label='Average QAOA-GPT Approx Ratio'\n", + ")\n", + "\n", + "# Enhance the plot appearance\n", + "plt.grid(True, linestyle='--', alpha=0.7)\n", + "plt.xlabel('Number of Edges', fontsize=12, fontweight='bold')\n", + "plt.ylabel('Approximation Ratio', fontsize=12, fontweight='bold')\n", + "plt.title('Best vs Average Approximation Ratio by Number of Edges', fontsize=14, fontweight='bold')\n", + "\n", + "# Add a horizontal line at y=0.97 (near-optimal approximation)\n", + "plt.axhline(y=0.97, color='red', linestyle='--', alpha=0.5, label='Approx. Ratio (0.97)')\n", + "\n", + "# Show the overall averages\n", + "best_avg = filtered_df['best_adapt_gpt_ar'].mean()\n", + "avg_avg = filtered_df['avg_adapt_gpt_ar'].mean()\n", + "\n", + "# Add text boxes for the averages\n", + "plt.text(\n", + " 0.02, 0.95, f'Avg. Best AR: {best_avg:.4f}',\n", + " transform=plt.gca().transAxes,\n", + " bbox=dict(boxstyle=\"round,pad=0.4\", facecolor='white', edgecolor='#1f77b4', alpha=0.8)\n", + ")\n", + "\n", + "plt.text(\n", + " 0.02, 0.87, f'Avg. Mean AR: {avg_avg:.4f}',\n", + " transform=plt.gca().transAxes,\n", + " bbox=dict(boxstyle=\"round,pad=0.4\", facecolor='white', edgecolor='#ff7f0e', alpha=0.8)\n", + ")\n", + "\n", + "# Add legend and adjust layout\n", + "plt.legend()\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Print some statistics\n", + "print(f\"Average QAOA-GPT best approximation ratio: {best_avg:.4f}\")\n", + "print(f\"Average QAOA-GPT mean approximation ratio: {avg_avg:.4f}\")\n", + "print(f\"Difference between best and avg AR: {(best_avg - avg_avg):.4f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7fe2866a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd8FNX6x/HP1vRsCDUk9N4iRUBERQEFQSxYsMC1Y0FRr4pgwYqIol4VCyLCTyxXpShBRBAL6rWLCiJKFRJKIL1ssm1+f4xZWJJAggkh4ft+vXiZnZmdPTMnG/fZc87zWAzDMBARERERERGRKmet6QaIiIiIiIiI1FUKukVERERERESqiYJuERERERERkWqioFtERERERESkmijoFhEREREREakmCrpFREREREREqomCbhEREREREZFqoqBbREREREREpJoo6BYRERERERGpJgq6RUREpFpNnDiRgQMH1tjrL1y4kA4dOpCamlot53/uuefo0KFDtZy7KtT0/RcROdbZa7oBIiL/xMKFC5k0aVLItvj4eNq2bcs111zDgAEDquV13W43r7zyCn369KFv374Vft6OHTt46aWX+PLLL0lPTycmJoaePXty9dVX07Nnz3Kf9/nnnzN27FgaNmzIqlWrsFrL/s40KyuLl19+mU8++YQdO3YQERFBt27dGD16NKeddlq553/jjTd46KGHSE5O5t13363w9ZQIBAIsXryY999/n3Xr1pGfn09sbCydOnViyJAhnHfeeTidzuDx+wcoFouFBg0a0L59e6677jr69u3Lc889x4wZMw75un369GHevHll7ivrd2N/b7/9Nt27d6/4RUop+fn5zJ07l+XLl7N9+3b8fj/NmzdnwIAB/Otf/6Jx48Y13cSj0qeffsrrr7/O2rVrKSwspGnTpgwcOJCxY8dSr169wzrn7t27eeeddxg8eDCdOnWq4hYffcaMGcN3331X5r5WrVqxbNmyI9wiEZHyKegWkTph/PjxJCUlYRgGGRkZLFq0iLFjx/LSSy8dNNg8XG63mxkzZnDTTTdVOOj+8ccfGTt2LAAXXnghbdq0Ye/evSxatIhLL72UyZMnc+mll5b53MWLF5OYmEhaWhrffPMNJ554YqljNm/ezBVXXEFmZiYjR46kW7du5ObmkpKSwvXXX89VV13FXXfdVeb5U1JSSExM5Ndff+Wvv/6iRYsWFbwTUFRUxLhx4/jyyy/p0aMHV199NfXr1ycnJ4fvvvuOBx98kF9++YVHH3005Hn9+/fnnHPOwTAMUlNTeeutt7j88suZOXMmp59+Os2bNw8eW1hYyAMPPMDpp5/O6aefHtzeoEGDQ7av5HfjQPufXypv+/btXHHFFezcuZOhQ4cyatQoHA4Hf/zxB/Pnz+fjjz/mo48+qulmAnDOOecwfPjwkC9+asq0adN49dVX6dixI9dccw1xcXH89ttvvP7663zwwQfMnTuX1q1bV/q86enpzJgxg8TExFJB98MPP4xhGFV1CUeNJk2a8O9//7vU9piYmBpojYhI+RR0i0idcMopp9CtW7fg4wsuuID+/fuzZMmSagm6KysnJ4dbbrmF8PBw3nrrrZCA78orr+Tqq6/mkUceoXPnzqVGXwsLC/nkk0/497//zcKFC0lJSSkVdHu9Xm655RZyc3N54403OO6444L7rrjiCu644w5effVVunXrxrBhw0Keu337dlavXs2MGTOYPHkyKSkp3HTTTRW+tkcffZQvv/ySu+++m8svvzxk31VXXcXWrVv56quvSj2vZcuWnHPOOcHHp59+OmeffTavvfYas2fPpmPHjsF9mZmZPPDAA3To0CHkORVx4O9GTSksLCQyMrKmm1ElfD4fN910ExkZGbz22mscf/zxIftvu+02Zs2aVUOtK81ms2Gz2Wq6GSxZsoRXX32VYcOGMX369GCbLrzwQkaOHMm//vUvbrnlFhYtWoTdXnUf0RwOR5Wd62gSExNT6b8H1cXtdhMREVHTzRCRo5TWdItInRQbG0tYWFipD66BQIC5c+cyfPhwunXrxoknnsjkyZPJyckJOW7NmjVcffXV9O3bl+TkZAYOHBicqpyamkq/fv0AmDFjBh06dKBDhw4899xz5bbn7bffZs+ePdx5552lRljDw8N57LHHAHj++edLPXfFihUUFRUxdOhQhg0bxvLlyykuLg45Zvny5fz5559ce+21IQE3mAHHQw89RGxsbJltTElJweVyMWDAAIYMGUJKSkq513GgnTt3Mn/+fE4++eRSAXeJli1bctlllx3yXB06dKBevXrVtu62PKmpqXTo0IHZs2fz9ttvM3jwYLp27cr555/Pr7/+Wur4TZs2MX78ePr06UO3bt0YOXIkK1euDDmmZA3xd999xwMPPEC/fv1Cljq88cYbDBo0iOTkZC644AJ++OEHxowZw5gxYwAoKCige/fuPPLII6Vef9euXXTq1ImZM2eWeT1er5c+ffqUObU+Pz+fbt26MW3atOC2efPmMXz4cI477jh69+7NyJEjD/k7sHz5ctavX8/1119fKuAGiI6O5rbbbjvoOWbPns3FF18cfI+NHDmyzCnBX331FZdccgnHH388PXr0YMiQITz11FMhxxzqGspb0/35558zevRoevToQc+ePTn//PNDnvfDDz8wfvx4Tj31VLp27cqAAQN49NFHKSoqOui1lWfGjBm4XC4efvjhUl8CJCcnc8011/Dnn3+GzBAYM2YMZ511FmvXruXiiy8O/j166623gsd8++23XHDBBQBMmjQp+Ddp4cKFQOk13fv/zpf8Lh533HFcddVV7Ny5E8MweP755znllFNITk7mhhtuIDs7O6S95f3NGzhwIBMnTgw+Lrn3P/zwA4888ggnnHACxx9/PJMnT8bj8ZCbm8uECRPo3bs3vXv35vHHH6/SUfmS9fZ//fUXEydO5Pjjj6dXr15MmjQJt9td6vj333+fkSNHkpycTJ8+fbjtttvYuXNnyDH798lll13GcccdF/ydzMrK4s4776Rnz54cf/zx3HXXXaxfvz6kPxYsWECHDh1Yt25dqdd/6aWX6NSpE7t3766yeyAiNU8j3SJSJ+Tn55OZmQlARkYG8+bNo7CwkLPPPjvkuMmTJ7No0SJGjhzJmDFjSE1N5Y033mDdunW89dZbOBwOMjIyuPrqq6lXrx5jx44lNjaW1NRUVqxYAZhrxh944IFS050Plkjpk08+ISwsrNQoc4lmzZrRq1cvvv76a4qLiwkLCwvuS0lJoW/fvjRs2JDhw4fz5JNP8sknn3DmmWeGnB/g3HPPLfP8MTExDBo0iEWLFpWaPp6SksLpp5+O0+nkrLPO4q233uLXX38lOTm53OspsWrVKvx+f6n7fDhycnLIzc2t1NT2itj/d6OExWIptXZ2yZIlFBQUMGrUKCwWC6+88go333wzH3/8cXCkcMOGDVxyySU0btyYa6+9lsjISD788EPGjRvHc889FzL1HeDBBx8kPj6ecePGUVhYCMCbb77JQw89xPHHH88VV1xBWloa48aNIzY2liZNmgAQFRXF4MGD+fDDD5k0aVJIgLZkyRIM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/XqKioqYNm0arVu3pnnz5kybNo3du3cTHR3NRx999I+C2rPPPps1a9bw2muvsWnTJkaMGEFsbCzr1q1jwYIFxMXF8cwzz5Tq40aNGjFr1izS0tJo2bIlS5cu5ffff+fhhx8OHtu8eXNiY2P573//S1RUFJGRkSQnJx/2GuxDufDCC7n//vu5+eabOfHEE1m/fj1ffvllqSoF1SkvL4/333+/zH2VLWfXvHlzbr31Vp588knS0tIYPHgwUVFRpKam8vHHH3PRRRcdMqv44MGDSU5OZtq0aWzbto3WrVuH5AMoazbCueeey7Rp0wDq/GwhkWOZgm4RqRP2LxcUFhZG69ateeCBB0plrH3ooYfo2rUr//3vf3n66aex2WwkJiZy9tlnB2va9unThzVr1rB06VL27t1LTEwMycnJTJ8+PeQD7COPPMLDDz/M1KlT8Xq93HTTTeUG3WCW41q8eDEzZ85k2bJlpKenB0c7586dG5LgrSRjdXlBccm+5557jvXr19OxY0fatGnD4sWLefnll/nkk09YuHAh4eHhdO3alRdffDHkXIsXLyYsLCykpNr+rFYrp556KikpKWRlZR30g3RERASvvPIK77//Pu+//z6zZ88mPz+fmJgYOnbsyP333895551X7vOrW3mlpKZOnVrpgKRt27YsWLCAGTNmsGjRIrKzs4mPj6dz586MGzeuQucYPXo0hmEwZ84cpk2bRseOHXnxxRd55JFHypwK3aBBA/r378/nn39e6UCiZ8+eJCQksHPnzjK/PBk1ahQpKSnMmTOHwsJCmjRpwpgxY8pconCgFi1a8N577zF37lxWrFgRzDDeokULLrzwQsaMGVPuc/v168eUKVOYNWsWjz76KElJSdxxxx2kpaWFBN0DBw4kLS2NBQsWBH8P+/Tpw8033xxc136413DhhRdSv359Xn75ZV544QXsdjutW7cOlphzOBy89NJLPPLII8ycOZOwsDBOP/10Lrvssn9Un/yee+6hb9++vPnmm8ycORO3201CQgKXXXYZ1157bZml0VwuF4899hiPPPII77zzDg0aNGDy5MkhWbIdDgePPfYYTz31FA888AA+n++wfscr6qKLLiI1NZX58+fzxRdf0KtXL+bMmVNmib7qsmvXLiZMmFDmvsPpo7Fjx9KyZUvmzp3L888/D5i5J/r373/Qv8UlbDYb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g/z555/cfffdxMbGMmrUqDKfs379eq699lquv/56pk2bxu7du7n//vsJBALcddddweOmTJnCl19+yYQJE2jfvj05OTnk5OSEnOuWW24hIyODKVOm0Lx5c/bs2RNS8s7lcnHDDTfQunVrHA4Hn376KXfffTf169fn5JNPrvL7ISIiIiJS3TTSXccUFBSwdOlSLrnkEk499VQWLVoU3Hf77bdz6623hhzv9Xrp27dvcDQxPz+f22+/ne7du3PSSScxd+7cw57mvGzZMtq2bcvYsWP54Ycf2LlzZ8j+iRMncuONNzJjxgxOOOEEevbsyeTJk0NKvI0ZM4aHHnqIhx56iF69etG3b1/+85//hEwrHjhwIM8//zwTJkwIngPgo48+Yvjw4cER1VdffTX4nBdeeIGTTz6ZrKys4LaxY8cyZsyYStc9j4uLo2HDhrRv357rrruO/Px8fvnll+D+9957j5EjRwZHiW+//XYyMjIAc7T8X//6FwC9e/emQ4cOTJw4MXjt+9/3nJwcJkyYQO/evTnuuOO45ppr2Lp1a6XaunjxYrxeL48++ijt2rVj+PDhjBkzhjlz5pT7nKVLl9KhQwduuukmWrRoQZ8+fbjzzjt54403yM/PB2DTpk289dZbvPDCCwwaNIhmzZrRtWvXkMziq1at4vvvv+fll1/mxBNPJCkpiR49etCrV6/gMX379uX000+nTZs2NG/enMsvv5wOHTrw448/Vuo6RURERKRmBQIGm/fk88v2bDbvyScQOHaXBSrormM+/PBDWrduTevWrTn77LNZsGBBMEAdMWIEn376KQUFBcHjv/zyS4qKihg8eDAAjz32GKtXr+bFF1/k1Vdf5YcffuC33347rLbMnz+fs88+m5iYGE455RQWLlxY6pivv/6aTZs2MW/ePJ566ilWrFjB888/H3LMokWLsNlsvPvuu9xzzz3MnTuXd999N+SYV199lY4dO/Lee+9x4403snbtWm699VaGDRtGSkoKN910E88880ywDddddx2JiYnce++9ALzxxhusXr2aadOmYbUe3tuiqKgo+OWFw+EIbvf5fNxyyy0sXryY559/nrS0tGBgnZCQwHPPPQeYX1J8+eWX3HPPPWWef+LEiaxdu5YXX3yRt99+G8MwGDt2LF6vN3hMhw4dyrzPJX7++WeOP/74kJkAJ510Elu2bCk1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//zzbNmyhUWLFvHGG29w1VVXAbBr1y4efPDBYDA8depUZs6cyc8//xw8x4QJE3jyyScPffP3Y7FYGDNmDC+//DJut5umTZvicDiYN28e27dvZ+XKlaWC+8TERCwWC5999hmZmZkhQWmJli1bMmjQIO677z5++OEH1q9fz5133knjxo0ZNGhQ8LihQ4eGrNk/0IgRI3A4HNxzzz1s2LCBpUuX8tprr3HllVcGj1mxYgVDhw4Ned4rr7zCH3/8EVyPPWvWLO69997gFxgnnngiXbp04e6772bdunWsXbuWyZMn079//+Do91lnnUVcXByTJk1i48aNfP/99zzxxBOcf/75hIeHAzBz5ky++uortm/fzqZNm3j11VdZvHhxME+BiIiIiBy9YsLthDmsuD1lDya5PX7CHFZiwg+YXfrnnzByJJx8Mnz7LeTmQh2qXqOgu46YP38+J554YplJtIYMGcLatWtZv349YCY127hxI40bNw4p1wRmhuzu3btz/fXXc+WVV9KzZ0/atGkTkr16z5495a5H3rt3L59//jlnnHFGqX1Wq5XBgweHfAHQr18/WrRowWWXXcatt97KwIEDufnmm0Oed+6551JUVMSFF17IQw89xL/+9a9ya0qX6NKlC//5z39YunQpI0aM4Nlnn2X8+PGMHDkSwzC455576NatG6NHjwbMpHOXXHIJd955ZzDo3blzZzBRWGWcd955+Hw+Xn/9deLj43nsscdYtmwZw4YNY9asWSG1rQEaN27MzTffzJNPPsmJJ57Iww8/XOZ5p06dSpcuXbj++usZNWoUhmHw8ssvh6wf37JlC3l5eeW2LSYmhtmzZ5OamsrIkSN57LHHuPHGG0PuZ15eHlu2bAl53qpVq7jssss4//zz+fzzz3n++eeDGe/B7NsXX3yRevXqcdlll3HdddfRpk0bnnrqqeAxUVFRvPrqq+Tl5XH++edzxx13cNpppwUzyIOZZ+DBBx9k+PDhXHLJJSxfvpwnnniCCy+88BB3XURERERqWsv6UbRtFM3OHHepBMqGYbAzx027RjG0rB9lbtyzB26+Gbp0gUWLwGqFsWNh40Y45ZQauILqYTEOvBvHOL/fz88//0z37t0POn3ZMAxycnJwuVyl1itUt08++YSCgoLDTvxVGYWFhZxyyincddddVR74TJw4kdzc3INO6x4zZgwdO3Yst4zW4TAMA7/fj81mO+J9J//M/n23du1avv/+++DsBTm61eTfTDl86rfaS31Xe6nvai/1nakke3lmgYcEVwQRThtuj5+dOW7io5yMH9SOrokuM+Bu1w5KytYOHw7TppkB+BF2uH1X0dhRidQkxLp169i8eTPJycnk5eUFS0PtP4VZRERERESkLF0TXYwf1I4FP6WyMT2f3bkBwhxWkpPiGNkz0Qy4ARo2hCFDzKnl06dDHY43FHTXQtX9zdmrr77Kli1bcDgcdOnShTfeeCNY+kvkaFFSgkxEREREji5dE110Tohla0YBeUU+YsLttPz1O6yjxsIbb8DflWp4+WWIiTGnlddhCrprobCwMHbv3l0tQUfnzp1ZuHBhlZ6zPI899tghj5k3b94RaInURgUFBSG5BkRERETk6GG1WsyyYL//DjdOgL+rKfHQQzB7tvnzIarn1BV1+yuFOiopKYnc3NxS9ZZFjhV+v5+tW7cG64SLiIiIyFFm92644Qbo1s0MuG02GDcOpk6t6ZYdcQq6a6HExETi4uJYvnw5mzdvDqm5LFKXGYbB7t27WbFiBYWFhXTs2LGmmyQiIiIiB3rySWjbFl56Cfx+OOcc+O03mDEDGjWq6dYdcZpeXgvZbDbOOussVqxYwccffwwQUtdaDk3rgWunQCBAIBAgKiqKM844g0bH4B9tERERkaNedjbk50Pv3maStDpU/utwKOiupSIjIznnnHPIyclh9+7dGu2upMLCQiIjI2u6GVJJNpsNq9VK27Zt9UWTiIiIyNFixQqIizODbIAJE6BrV7jwwjqfJK0iFHTXci6XC9cxkoCgqqiGYu1V0nfqNxEREZGjwJo1ZoC9bBn07Qtffw0Wi5mRfNSomm7dUUNfO4iIiIiIiEjF7dgB11wD3bubAbfDAf36QXFxTbfsqKSRbhERERERETm0/HxzjfYTT0BhobntwgvNjORt2tRs245iCrpFRERERETk0BYvhgcfNH/u188MwE88sWbbVAso6BYREREREZHSDAN27YKEBPPxxRfDokXmf0eONNdvyyEp6BYREREREZFQq1fDnXfC77/Dn39CVJSZifzdd2u6ZbWOEqmJiIiIiIiIaft2uPxy6NULVq6EvXvNrORy2BR0i4iIiIiIHOtyc+Gee6B9e3jtNXNq+SWXwB9/wODBNd26Wk3Ty0VERERERI5lGRnQuTOkp5uPTznFTJLWu3fNtquO0Ei3iIiIiIjIsax+fRgwADp0gPfeg88+U8BdhRR0i4iIiIiIHEu+/x6GDIFt2/Zte+klWLMGzjlHWcmrmIJuERERERGRY8HWrXDppdCnDyxfDvffv29ffDw4HDXWtLpMa7pFRERERETqsuxsePRReOYZ8HjMkex//QseeqimW3ZMUNAtIiIiIiJSV730kpmVPDPTfDxwoJkkrUePmm3XMURBt4iIiIiISF2VmmoG3J07wxNPwJlnas32EaagW0REREREpK74+msIC4OePc3Hd90FrVrB5ZeDXeFfTVAiNRERERERkdpu0ya46CI48US48UYwDHN7TAxcfbUC7hqkoFtERERERKS2ysiA226DTp3g3XfNqeNdu4LbXdMtk7/p6w4REREREZHapqgIZsyAKVPM7OQAQ4fC449Dt2412jQJpaBbRERERESktlm0CO680/w5OdlMknbGGTXbJimTgm4REREREZHaICMD6tc3f77oInjjDTj/fLPmts1Ws22TcinoFhEREREROZr98YeZhfyHH+DPPyEy0gyylyyp6ZZJBSiRmoiIiIiIyNFozx646Sbo0gXefx927YLPPqvpVkklKegWERERERE5mrjdMHUqtGkDzz8Pfj+cdRb8+isMG1bTrZNK0vRyERERERGRo0V2tpkYbft283HPnjB9Opx2Wo02Sw6fgm4REREREZGjRVwc9Olj/vzoo3DppWDVBOXaTL0nIiIiIiJSU9atg5Ej941sA7zwgpk8bfRoBdx1gEa6RUREREREjrRdu+D+++GVVyAQgNhYmDvX3NeoUY02TaqWgm4REREREZEjpaAAnnoKpk0zfwY47zy4++6abZdUGwXdIiIiIiIiR8Lrr5v1tnfsMB/36QNPPgknnVSz7ZJqpQUCIiIiIiIiR8K6dWbA3aoV/Pe/8M03CriPAXUy6PZ4PJxzzjl06NCB33//vaabIyIiIiIix6Jff4Vfftn3eOJEeOYZ+P13GDUKLJaaa5scMXUy6H788cdppOQDIiIiIiJSE9LS4KqroHt3uOEGMAxze2wsjB8PYWE12jw5supc0P3555/z1Vdfcdddd9V0U0RERERE5FiSlwf33Qft2sGcOWaw3azZvoRpckyqU4nU9u7dy3333cfzzz9PeHh4TTdHRERE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7r3dggVeRfI//9l7XFYG3/wmfOUrgzosERERERkcjuOwbNkyLMti+PDhBAIBZWb7geu62LaNZVm6nwWmp7nrzH6vW7eOZcuWMW7cOExz5+qQK+jem91xB1x9tdd72+/3Au1rr4XKysEemYiIiIgMkmw2i+M4jBgxgqKiosEezl5DQXfh2tbchcNh/H4/77//PtlsllAotFOvr6B7b3bEEV7A/b//C9/+NowdO9gjEhEREZE9xM5m7UT2Jf3xOVHQvbfI5+GnP4VEAr72Ne/Y0UfDO+/AhAmDOzYREREREZF9lL7eKnSuC08/DVOmwEUXecvHm5o2Pa+AW0RERERku+69916OPPJIJkyYwF//+tfBHk7BePzxxznkkEMG7frHHnssDz744KBdv7cUdBeyefPg+OPh5JNh0SJvr/asWVBVNdgjExERERHpV1dddRUTJkxgwoQJTJ48mY985CPcd9995PP5XXrdJUuWcN9993HTTTfxwgsv8MEPfnCXx3rvvffyiU98YpdfZ0934okn8ufOgs0DaFvB/WOPPcZZZ5014NffVVpeXohWr4Yrr4SHHvIeB4NwySVe0bSyskEdmoiIiIjIQDn66KO57bbbyGazPP/889x00034/X4uuuiiPr+WbdsYhsGKFSsAOO644/aZAmidhcN8vl0LB0Oh0E4XF+sPFRUVg3btvlCmuxBls/Db33o//9//wbvvwu23K+AWERERkb1aIBCgqqqK2tpaPv3pT3PkkUfyt7/9DfCqst9+++0cffTRTJ06lU9+8pO88sorXb/bmS197rnnOPHEEznwwAO55ppruPjiiwHYf//9mbDZ1sxHH32Uj3/84xx44IGccMIJ/PKXv+w2ljVr1vD1r3+dQw89lKlTpzJjxgzmz5/P448/zn333cc777zTlZl//PHHe3w/8+fP53Of+xyHHXYYBx98MOeccw5vvfVWt3MmTJjAI488wgUXXMCUKVM47rjj+NOf/tT1fGNjIxMmTODpp5/mU5/6FAceeCAnn3wyr776atc5r7zyChMmTOD555/njDPO4MADD+T1118nm81yyy23cMQRR3DggQdy9tlnM3/+fAAymQwnnXQS3/rWt7peZ8WKFUybNo3HHnus2z3t1Jnhf+yxx/jQhz7EtGnTuOGGG7Btmzlz5nDUUUdxxBFH8MMf/rDbe/zZz37GKaecwtSpUznmmGO44YYbSCQSXWO/+uqr6ejo6Lqf9957L7D18vJVq1bxxS9+kWnTpjF9+nQuueQS1q9fv9X4nnzySY499lgOPvhgvva1rxGPx3ucn/6iTHchGjkSvv99OOggGMQ9FCIiIiKyl9kY6PTIsmDzrOb2zjVNCIe3f25xcd/Ht4VgMEhbWxsAN910Ew0NDXz3u9+lurqaZ599lgsuuICnnnqKUaNGAZBOp5kzZw633HILZWVlVFdXc+ihh3L11VfzwgsvdL3u73//e+6++26uu+46Jk6cyKJFi/jWt75FUVERp59+OolEgnPOOYehQ4fygx/8gKqqKt566y0cx+HEE0+koaGBf/3rX/zsZz8DoKSkpMfxJxIJTjvtNL75zW8C8NOf/pQLL7yQP//5z0Qika7z7r77bi677DKuvfZafve73/H1r3+dcePGMXaz7kR33HEH11xzDfX19fzsZz/j4osv5rnnnqO8vLzrnLvuuosrr7ySESNGUFpayh133MGf//xnZs2aRW1tLffffz8XXHABf/nLXygrK2P27Nl88pOf5JhjjuHDH/4wl19+OUcddRRnnnnmNudkxYoV/POf/+T+++9nxYoVzJw5k5UrVzJ69Ggeeugh/vvf/3LNNddw5JFHctBBBwFgGAbXXnstdXV1rFy5khtvvJE777yTG264gWnTpnHNNddwzz33dH3Z0FOrO8dx+NKXvkRRUREPPfQQtm1z44038rWvfY2HOlcIbxzfc889x49+9CPa29u59NJLmTNnDjNnztzme9plrnSTz+fd//znP24+n9/ueY7juK2tra7jOLtpZNJfNHeFS3NXuDR3hUnzVrg0d4Vrd8xdKpVy3377bTeVSm39pFemt+c/J57Y/dyiom2fe8wx3c8dMmTrc/royiuvdL/4xS+6ruvdp3//+9/u5MmT3VmzZrlNTU3uxIkT3TVr1nT7nc9+9rPuXXfd5bqu686dO9cdP368u2jRom7nPPvss+748eO7HTv++OPdp556qtux73//++5ZZ53luq7r/vrXv3anTZvmtra2dj3vOI6by+Vcx3Hce+65xz311FP7/B5t23anTZvm/u1vf+s6Nn78ePe6667rdt4nP/lJ9/rrr3dd13VXrlzpjh8/3v3xj3/c9Xwul3M/+MEPuj/5yU9c13Xdl19+2R0/frz77LPPdp2TSCTcAw44wP3973/fdSybzbof+MAH3Dlz5nQdmzNnjnvYYYe5N910k3vUUUe5LS0tXc/NnTvXPfjgg7se33PPPe5BBx3kdnR0dB07//zz3Q9/+MOubdtdxz72sY91G++W/vjHP7qHHnroNq/T6cMf/rD7s5/9zHVd133hhRfciRMnuqtWrep6fvHixe748ePdN998c5vju/32291PfvKTXXO3pe19XnobOyrTLSIiIiIiBeEf//gH06ZNI5fL4bouJ598Ml/96ld59dVXsW2bE044odv52WyWss22YPr9/m5LyHuSTCZZsWIF1157bbel1fl8vitjvWjRIiZNmtTttXfG+vXr+d73vserr77Khg0bcByHVCrFqlWrup03bdq0bo+nTp3KokWLtnmOz+dj8uTJLF26tNs5Bx54YNfPK1asIJfLMX369K5jfr+fKVOmsGTJkq5j559/Pn/96195+OGHmTNnTrfMeU9qa2u7ZemHDBmCZVnd+l0PGTKEDRs2dD1+8cUX+fGPf8zSpUuJx+PYtk0mkyGVShHefMXEdixZsoRhw4ZRU1PTday+vp7S0lKWLl3KlClTehxfdXV1t7EMBAXdIiIiIiLi2d7eVsvq/ri5edvnmluUjlq+fKeHtLnDDjuMG264Ab/fT3V1dVchsGQyiWVZzJ07F2uLcW6+FDkUCu2wWFoymQTg5ptv7lr+3KkzcOyv4mFXXnklbW1tXHvttQwfPpxAIMBZZ51FLpfrl9ffUm8D2M1t2LCB5cuXY1kW77///g7P37I4m2EYPR5zHAfw9qRfdNFFnH322Xzta18jGo3y+uuvc+2115LL5XZqzH0ZH9A1loGiQmoiIiIiIuIpLt72ny0Dze2du2Wg1NM5OyEcDjNy5EiGDx/eLXiaOHEitm3T0tLCyJEju/2p6mM73SFDhlBdXc3KlSu3eq0RI0YAXnGzRYsWde0n35Lf7+9VIDdv3jzOPfdcjjnmGMaNG0cgEKC1tXWr8954441uj998881u+7m3PCefz/PWW28xZsyYbV57v/32w+/3M2/evK5juVyOBQsWUF9f33XsmmuuYfz48cyaNYvZs2d3y4L3h7feegvXdbnqqquYOnUqo0ePpnmLL3T8fj+2bW/3dcaOHcuaNWtYvXp117GGhgba29u3ule7mzLdIiIiIiJS0EaPHs0pp5zCFVdcwVVXXcXEiRNpbW3lpZdeYsKECXzoQx/q0+vNnDmTW265hZKSEo4++miy2SwLFy6kvb2dz33uc5x00kn86Ec/4stf/jJf//rXqa6u5q233mLIkCEcfPDB1NbW0tjYyKJFixg6dCiRSIRAILDVdUaNGsXvf/97DjzwQOLxOHfccUePWfQ//elPTJ48mYMPPpinnnqK+fPnc+utt3Y755FHHmHUqFGMGTOGn//858RiMWbMmLHN91hUVMTZZ5/NHXfcQTQaZfjw4dx///2k0+muQmm//OUveeONN/j9739PTU0Nzz//PJdddhm/+c1venw/O2PkyJHkcjkeeughjj32WF5//XV+/etfdzuntraWZDLZNZ/hcHirDPiRRx7J+PHjueyyy7jmmmuwbZsbbriBQw89tNuy+sGgTLeIiIiIiBS82267jdNOO41Zs2bx8Y9/nC996UssWLCg2x7f3vrkJz/JLbfcwuOPP84pp5zCueeeyxNPPEFdXR3gtS776U9/SmVlJRdeeCGnnHIKc+bM6Vp+/rGPfYyjjz6az3zmMxxxxBH84Q9/6PE6t956K7FYjNNPP50rrriCc889l8rKyq3O++pXv8ozzzzDqaeeypNPPsldd93VLRsN8I1vfIOf/OQnfOITn+D111/nhz/84Q77WF922WV87GMf44orruD000/n/fff5/777ycajbJkyRLuuOMOrr/++q57eP3119Pa2srdd9/d53u6Lfvvvz9XX301c+bM4eSTT+app57i61//erdzpk+fzqc+9SkuvfRSjjjiCO6///6tXscwDH7wgx9QWlrKOeecw3nnnceIESP47ne/229j3VmG67ruYA9iT2LbNm+88QZTp07daj/I5lzXJRaLEY1Gd7gvRPYsmrvCpbkrXJq7wqR5K1yau8K1O+YunU6zbNkyRo8e3W97k8WbO9u2sSyrX+duwoQJfP/73+f444/v8fnGxkaOO+44nnzySSZOnNhv192XbG/utvd56W3sqEy3iIiIiIiIyABR0C0iIiIiIiIyQFRITUREREREZA/17rvvbvf5urq6HZ4jg0uZbhEREREREZEBUjCZ7h/+8Ic8//zzLFq0CL/fz3/+85+tzrnllluYN28e7733HmPHjuV3v/vdIIxURERERERExFMwme5cLscJJ5zA2Wefvd3zZsyYwYknnrib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ISlpaGhg0qRJCAkJgb6+Ptq0aYM9e/YAAEaMGCHn6KgmV69eRWpqKvr06QN1dXVcvXoVa9euxahRo3gNjE/c+wezmjVrBuDd701eB+PTlpqaihs3bqBXr17Q1NTEo0ePEBwcDAMDgwaf5QYAhfLy8vIPGOf/Cz4+PoiJialS/uOPP8LKygrAuwtfXLp0qUqd06dPo127djKPkaonTd9lZmbCz88Ply5dgpqaGhwcHODl5YUmTXiM6lOydOlSJCQkoLCwEB07doSLiwvGjBkj77BICjdu3MDmzZvx+++/QywWw8TEBHPnzoWNjY28QyMpeHl5ITMzs1H3K6WPTywWIygoCD///DOKiopgbm6O7777DiYmJvIOjWpw8+ZNrFq1ChkZGSguLka7du0wevRoODs780TAZ+bJkycYPHgwYmNj0bVrV3mHQ7W4c+cO/P39cefOHbx58wa6uroYMGAA5s6dCz09vQa3y6SbiIiIiIiISEZ4yzAiIiIiIiIiGWHSTURERERERCQjTLqJiIiIiIiIZIRJNxEREREREZGMMOkmIiIiIiIikhEm3UREREREREQywqSbiIiIiIiISEaYdBMRERERERHJCJNuIiL6Ily8eBEikQivXr2qsU50dDQsLS0/YlSfjiNHjsDFxaXe77O1tcW+ffs+fEDvefLkCUQiEW7fvv3B2hSJRDh16tQHa68hQkJCMHr0aLnGUJsJEybg5MmT8g6DiOiL1kTeARAR0ZchJycHu3btQkJCAp49e4bmzZvDwMAAo0aNgoODA9TU1OQdIkaOHAkbG5ta64hEomrLg4KC8M0338giLJn766+/EBwcjODgYInygoIChIWF4eTJk8jMzISmpiZMTEwwefJkDB06FAoKCh8tRn19fSQmJqJFixYfbZ0A8PTpU2zZsgXnzp1DXl4edHV1MXjwYMybN6/esYhEImzbtg1DhgwRylxcXDB16tQPHfYHM2fOHKxduxZDhw6FoiLPxRARyQKTbiIiarTHjx/DyckJzZs3h6enJ0QiEVRUVHDnzh1ERkZCT08PgwcPblDb5eXlKC0tRZMmjf+XpaqqClVV1TrrrV27FgMGDJAo09TUbPT6ayMWi6GsrCyTtk+cOAENDQ306tVLKHv16hUmT56M169fY+HChTA1NYWSkhIuX76MwMBA9OnTR+afuTIlJSXo6up+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average approximation ratio: 0.9663\n", + "Min approximation ratio: 0.9178\n", + "Max approximation ratio: 0.9877\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Create a new column with the best energy from each row's adapt_gpt_energies\n", + "filtered_df['best_adapt_gpt_energy'] = filtered_df.apply(\n", + " lambda x: x['adapt_gpt_energies'] if isinstance(x['adapt_gpt_energies'], (int, float))\n", + " else min(x['adapt_gpt_energies']),\n", + " axis=1\n", + ")\n", + "\n", + "# Create the scatter plot\n", + "plt.figure(figsize=(10, 6))\n", + "plt.scatter(filtered_df['energy_gurobi'], filtered_df['best_adapt_gpt_energy'], alpha=0.6)\n", + "\n", + "# Add a diagonal line representing y=x (perfect approximation)\n", + "max_val = max(filtered_df['energy_gurobi'].max(), filtered_df['best_adapt_gpt_energy'].max())\n", + "min_val = min(filtered_df['energy_gurobi'].min(), filtered_df['best_adapt_gpt_energy'].min())\n", + "plt.plot([min_val, max_val], [min_val, max_val], 'r--', label='Perfect approximation')\n", + "\n", + "# Add labels and title\n", + "plt.xlabel('Gurobi Energy (Classical Optimum)')\n", + "plt.ylabel('Best QAOA-GPT Energy')\n", + "plt.title('Best QAOA-GPT Energy vs Classical Optimum Energy')\n", + "plt.grid(True, alpha=0.3)\n", + "plt.legend()\n", + "\n", + "# Add a text box with the average approximation ratio\n", + "avg_ratio = filtered_df['best_adapt_gpt_ar'].mean()\n", + "plt.annotate(f'Avg. Approx. Ratio: {avg_ratio:.4f}', \n", + " xy=(0.05, 0.95), xycoords='axes fraction',\n", + " bbox=dict(boxstyle=\"round,pad=0.3\", fc=\"white\", ec=\"gray\", alpha=0.8))\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Print some statistics\n", + "print(f\"Average approximation ratio: {avg_ratio:.4f}\")\n", + "print(f\"Min approximation ratio: {filtered_df['best_adapt_gpt_ar'].min():.4f}\")\n", + "print(f\"Max approximation ratio: {filtered_df['best_adapt_gpt_ar'].max():.4f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "d40e2632", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average number of new_layer occurrences per circuit: 7.21\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAcsRJREFUeJzt3Xd8FHX+x/H37qYRCCUh9A4mdAgtNA1NQXo5sYGoKIKKHp4eoHciKoieeFIsCIgUQRCCdKRIsdAUEFCQHppACCWBhJTd+f3BL3usCZCQHTbZvJ6PRx6PyczszGd2J5t97/c737EYhmEIAAAAAAC4ndXTBQAAAAAA4K0I3QAAAAAAmITQDQAAAACASQjdAAAAAACYhNANAAAAAIBJCN0AAAAAAJiE0A0AAAAAgEkI3QAAAAAAmITQDQAAAACASQjdAAC327Jli8LDwzVhwgRPl+IWsbGxGjp0qKKiolSjRg2Fh4crPj7e02UhlwgPD1ffvn09XYZT3759FR4e7pZtRUdHKzw8XNHR0W7ZXm42bNgwhYeH68SJE54uBYCX8fF0AQC80/DhwxUdHa2iRYvq+++/l5+fn6dLyrP69u2rrVu3ys/PTytXrlTZsmUzrNOhQwcdOXJEf/zxhwcq9H7Dhg3Tjz/+qE6dOqlixYqyWCzy9/e/4fonTpxQ27Zt1bJlS02dOvUOVgp3SEpK0rx587RmzRodOHBACQkJKliwoKpWrarWrVvrb3/7m4KDgz1dZrZER0dr+PDheuedd9SzZ09Pl+OUXtfN9OjRQ2PGjLlDFbnHX7/0sNlsKlq0qOrUqaN+/fqpefPmHqoMgCcQugG43eXLl7Vy5UpZLBZdvHhRa9asUceOHT1dVp6XkpKiDz/8UP/5z388XUq+kpKSop9++knNmzfX2LFjPV0OTLZv3z49++yzOnnypMqWLas2bdqoePHiunz5snbu3KmxY8dq0qRJ+v777xUYGChJWr58uQoUKODhyv/n3XffVVJSklu2de+996pevXoqUaKEW7Z3I82aNVPDhg0zXVajRg1T922WokWLqk+fPpKk5ORkHTx4UOvXr9f69es1duxYde7c2cMVArhTCN0A3G7FihVKTEzUE088oenTp2v+/PmEbjeoUKGCli5dqv79+6t69eqeLiffOHfunBwOh+mhA553+vRpPfnkk7pw4YKGDRumxx57TDabzWWd33//XW+++abS0tKc86pWrXqnS72pMmXKuG1bQUFBCgoKctv2bqR58+YaMGCA6fu5k4oVK6bBgwe7zFu2bJleeuklffDBB4RuIB/hmm4Abjd//nz5+PjoqaeeUmRkpDZt2qSTJ086lyclJSkiIkLt2rW74Ta6dOmiunXr6vLly855hmFo/vz5euihh9SgQQPVq1dPPXv21Pz58zM8fsKECQoPD9eWLVsUHR2tHj16qF69es7rLhMSEvTZZ5+pT58+atmypWrXrq2WLVvqn//8p44dO5ZpTefPn9e///1vNWvWTPXq1VOvXr20evXqm17zuG/fPg0ZMsS5j9atW+utt97ShQsXsvx8pvv73/8uh8Oh999/P0vrX/8c/FVmNZ84cULh4eEaNmyYDh06pGeeeUaNGjVS48aN9dJLL+n8+fOSpB07dqhfv35q0KCBGjdurNdee02JiYk3rOPnn39W3759FRERoUaNGmnw4MGKiYnJdN24uDiNHj1a9957r2rXrq3IyEgNHjxY+/fvz7BumzZt1KZNG8XHx+vNN99UVFSUatasmaVrTxMTEzV+/Hh16NBBderUUZMmTTRgwAD98ssvLuv17dtXrVu3liQtXLhQ4eHhzufIXbJzLv73v/9VeHi4li9fnum25s+fr/DwcE2aNMll/vHjx/Xaa6+pVatWzu0PGzbM5e8yXfr1yWfOnNE///lPtWjRQtWrV3eeR0ePHtXw4cPVpk0b1a5dW02aNFHXrl01atQoGYZxy+O9/rz8+uuv1aVLF9WpU0d33323Ro8e7fI3f72s/i399Tx+7rnnFBkZmaVrdf/73/8qLi5OzzzzjJ544okMgVuSatasqVmzZqlQoUIZnrPrpV8ffPz4cX3++efq2LGjateu7XLuxMXFacyYMWrfvr3q1q2rJk2a6IEHHnC5JOFm4yNcf6zX++s13cOGDXN24R4+fLjzPM7Kdd83en9LP+Zz585p6NChioyMVN26ddW7d+9M33PcJSUlRTNnzlT//v0VFRWl2rVrq1mzZnr++ef1+++/3/Bxa9as0ZNPPqnIyEjVqVNHbdq00SuvvJLpe4thGJoxY4Y6dOjgPNcmTpwoh8OR4/o7duyowMBAnTx50vmemi6r70tr1qxReHh4hktXvvjiC4WHh+uee+5xmZ+cnKw6derkqnEH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DofDbR+au3fvrtTUVI0dO9blvtL79u3TwoULFRQUdNN7yLtbds/FdA899JDS0tL04osvyjCMTAfkateuncqUKaNp06Zp27ZtGZanpqZmGn5vJL0V+K/Sv4y6fiyHW/nmm29crlE1DEMffPCB7Ha780s66c78LaUbMmSIgoOD9emnn2rGjBmZBut9+/apb9++OWplrlu3rurUqaNt27Zl+PuQXL8oq1y5sgoWLKjvvvtOFy9edM4/d+6cPvnkkyzvM/2yntOnT9923blB2bJldenSJZdzwW6369133820N1L65TejRo1yef6ka1+e3Kg3jlksFouee+45SdK4ceOc82/nfSm99Xry5MmyWq0u161HRkZq8+bN2r17txo0aHDT2+UBMB/dywHk2ObNm3XixIlbDqjUs2dP7dixQ/Pnz3cGwtDQUDVr1kw//vijrFarGjZsqHLlymV47EMPPaRff/3VeW/v5s2bq0SJEoqLi9Phw4f166+/auzYsZk+NjN9+/bVW2+9pe7du6tDhw5KS0vTTz/9JMMwVL169QwDaT399NNatWqVvvrqKx04cECNGjXS6dOntWLFCrVu3Vrr1q1z6boYHBysDz74QC+++KK6deumu+++W1WqVFFKSopOnjyprVu3KiIiQlOnTs1SvZkJCAjQ4MGD9a9//UtXrlzJsLx+/fpq0KCBNm/erAcffFCNGjXSqVOntHbtWrVu3VqrV6++7X1nVcuWLfX2229rw4YNuuuuu3TgwAGtW7dOxYoV02uvveay7tixY9WvXz8NGTJE06dPV82aNRUQEKBTp05p586dOn/+vMsXNrfr6aef1oYNG7Ro0SIdOnRIzZo1c95f2G6366233nJb1+L9+/dr2LBhmS6rUqWKBgwYkO1zMd3dd9+tsmXL6uTJkwoNDc20+7ifn5/GjRunp59+Wn369FHTpk0VFhYmi8WiU6dO6eeff1bRokWd153eyqJFizR37lw1btxY5cuXV6FChXTw4EFt3LhRRYsWdbk/+K20bNlSDz30kDp27Kjg4GBt2rRJe/bsUf369V0uMbgTf0vpSpUqpc8//1zPPfecRo0apS+++ELNmjVT8eLFdfnyZe3atUu7d+9WoUKFcjyA4fvvv6++ffvq3//+txYtWqT69esrOTlZBw8e1N69e52tm35+furbt68+/fRT9ezZU23a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SIsWLdLkyZP1r3/9K5vP8o39/PPP+u677/TII49Ikj777DMNHDhQTz31lGbPnq1HHnlEly5d0pQpU/Tqq69qxowZLo+/dOmSBgwYoPvvv1+dOnXSihUr9MYbb8jX11d/+9vfJLnvtcnMhAkTNHHiRDVv3lwPP/ywjhw5ojlz5mj37t2aM2eOfH199eqrr+qbb77R6tWr9cYbbygwMDDDFxDZdfz4ca1Zs0YdOnRQuXLldO7cOc2dO1d9+vTRsmXLVLJkSRUsWFDt2rXTihUrNHz4cNlsNufjly5dKsMw1KVLF0lSUlKS+vTpozNnzuihhx5S6dKltWPHDn3wwQeKjY3Va6+95rL/6OhoJScnq3fv3vLz81ORIkU0b948vf3222rfvr0ee+wxJScn648//tCvv/7q3A8AIBcxAAAw2e7du42wsDDjxx9/NAzDMBwOh3HPPfcYb7/9tnOd77//3ggLCzO+++47l8c+/fTTRtu2bZ2/f/PNN0b16tWNbdu2uaw3Z84cIywszPjll1+c88LCwozq1asbBw4cyFBTUlKSy+8pKSlG586djccee8w5b8+ePUZYWJgxatQol3WHDRtmhIWFGePHj3fOe/XVV40WLVoY58+fd1l3yJAhRsOGDTPs769at25tDBgwwLn9OnXqGGfOnDEMwzA2b95shIWFGStWrHCuP3ToUKN169YZtjN+/HgjLCzMZV5YWJhRu3Zt4/jx4855X331lREWFma0aNHCSEhIcM4fO3asERYW5rJunz59jLCwMOPzzz93zktOTja6detmNGvWzEhJSTEMw32vzV/FxcUZtWrVMp588knDbrc758+aNcsICwsz5s+fn+H44+LibrndrKybnJzssk/DMIzjx48btWvXNiZOnOicl37+btiwwWXdLl26GH369HH+/tFHHxn169c3jhw54rLe+++/b9SoUcM4deqUcx9hYWFGgwYNMtQ3aNAgo1OnTrc8PgBA7kD3cgCA6ZYsWaLixYsrMjJSkmSxWNSxY0ctX75cdrtdktS0aVMVK1ZMy5cvdz7u0qVL+umnn1wGw1q5cqWqVq2qKlWq6Pz5886fpk2bSro20vf1GjdurGrVqmWoKSAgwGU/CQkJatiwoX7//Xfn/PTuzumtw+n69Onj8rthGFq1apXatGkjwzBc6mrZsqUSEhL022+/Zfn5evbZZ2W32/XZZ59l+TG30qxZM5UrV875e7169SRJ9913nwoVKuScX7duXUnXWniv5+PjowcffND5u5+fnx588EHFxcU5j81dr81f/fTTT0pNTdVjjz0mq/V/H10eeOABFSpUSBs2bMjSc3A7/Pz8nPu02+26cOGCAgMDVblyZZdzpXnz5ipRooRLF+/9+/frjz/+UNeuXZ3zVq5cqYYNG6pw4cIuz1Hz5s1lt9u1bds2l/3fd999Cg4OdplXuHBhnT59Wrt27TLjkAEAbkb3cgCAqex2u5YtW6bIyEiXa4Xr1q2rzz//XJs2bVLLli3l4+Oj++67T0uXLlVKSor8/Py0atUqpaamuoTumJgYHTp0SM2aNct0f+kDaaW7Pmheb926dfrkk0+0d+9el+uNLRaLc/rUqVOyWq0ZtlGxYkWX38+fP6/4+HjNnTtXc+fOzXR/2bktVfny5dW1a1fNmzcv067st6N06dIuv6cH7VKlSrnMTx+A7PprpSWpRIkSCgwMdJlXqVIlSdLJkydVv359t702f3Xq1ClJUpUqVVzm+/n5qXz58jp58mSWtnM7HA6HZsyYodmzZ+vEiRPOL4kkqWjRos5pq9WqLl26aM6cOUpKSlKBAgW0ZMkS+fv7q0OHDs71YmJi9Mcff9zwOfrreZLZc/T000/rp59+0gMPPKCKFSuqRYsW6ty5sxo2bJjDowUAmIHQDQAw1ebNmxUbG6tly5Zp2bJlGZYvWbJELVu2lCR16tRJc+fO1caNG9WuXTutXLlSVapUUfXq1Z3rOxwOhYWFZTrompQxRF7fop3u559/1qBBg9S4cWONGDFCoaGh8vX11YIFC7R06dJsH2P6AFhdu3ZVjx49Ml0nu9cWDxo0SIsXL9bkyZPVrl27DMuv/3LgeteHwutdf51xVuYbhpHFSv/HHa9NbvPpp59q3Lhx6tWrl1588UUVKVJEVqtVo0ePzvAcde/eXVOnTtWaNWvUuXNnLV26VK1atXIZSd3hcKhFixZ66qmnMt1f+hcZ6TJ7jqpWraqVK1dq/fr1+v7777Vq1SrNnj1bzz33nF544YWcHzQAwK0I3QAAUy1ZskQhISF6/fXXMyxbvXq1c/C0gIAANW7cWKGhoVq+fLkaNGigzZs3a+DAgS6PqVChgvbt26dmzZrdMHjeyrfffit/f39NnTrVZdC2BQsWuKxXpkwZORwOnThxwiUM/fUe48HBwSpYsKAcDoeaN29+WzX9VYUKFdS1a1fNnTvX2RX8eoULF87QGi39r1XY3c6ePavExESX1u6jR49KknMANHe8NpkpU6aMJOnw4cMqX768c35KSopOnDjhtuc8M99++60iIyM1evRol/nx8fEqVqyYy7ywsDDVrFlTS5YsUalSpXTq1KkMg+FVqFBBiYmJOa45MDBQHTt2VMeOHZWSkqLBgwfr008/1TPPPCN/f/8cbRsA4F5c0w0AMM3Vq1e1atUqtWrVSh06dMjw8+ijj+rKlSv67rvvJF3rotuhQwetW7dOixcvVlpamkvXckm6//77debMGc2bNy/T/SUmJt6yLpvNJovF4tIqfOLECa1du9ZlvfQW+NmzZ7vMnzVrVobttW/fXt9++63279+fYX/Z6Vp+vUGDBiktLU1TpkzJsKxChQpKSEhwuXXV2bNntXr16tva162kpaW5dJ1PSUnR3LlzFRwcrFq1aklyz2uTmebNm8vX11czZ850aV2eP3++EhISFBUVdVvbzQqbzZahRXvFihU3vKVbt27d9OOPP2r69OkqWrSo7rnnHpfl999/v3bs2JHp7dHi4+OVlpZ2y5ouXLjg8rufn5+qVq0qwzCUmpp6y8cDAO4sWroBAKb57rvvdOXKlUzvJy1J9evXV3BwsBYvXuwM1/fff79mzpyp8ePHKywsTFWrVnV5TLdu3bRixQqNGDFCW7ZsUYMGDWS323X48GGtXLlSU6ZMUZ06dW5aV1RUlKZNm6annnpKnTt3VlxcnGbPnq0KFSo4710tSbVr11b79u01ffp0Xbx40XnLsPQW3utbc//xj39oy5Yt6t27tx544AFVq1ZNly5d0m+//aZNmzZp69at2X7+0lu7Fy5cmGFZx44d9f777+v5559X3759dfXqVc2ZM0eVK1fO1qBtWVWiRAlNnjxZJ0+eVKVKlbR8+XLt3btXb731lnx9fSW557XJTHBwsJ555hlNnDhRTz31lNq0aaMjR45o9uzZqlOnjstAZbfjiy++yNCN22q1auDAgWrVqpU++ugjDR8+XBEREdq/f7+WLFni0uJ+vc6dO+s///mPVq9erYcfftj53KTr37+/vvvuOw0cOFA9evRQrVq1lJSUpP379+vbb7/V2rVrMwyc9lf9+/dX8eLF1aBBA4WEhOjw4cOaNWuWoqKiXAbFAwDkDoRuAIBpFi9eLH9/f7Vo0SLT5VarVa1atdKSJUt04cIFFStWTA0aNFDp0qX1559/ZmjlTn/MRx99pC+++EKLFi3S6tWrVaBAAZUrV059+/ZV5cqVb1lXs2bNNGrUKE2ePFmjR49WuXLl9PLLL+vkyZMuoVuS3n33XRUvXlzLli3T6tWr1bx5c/33v/9Vhw4dXLqmFy9eXF9//bU++ugjrV69WnPmzFHRokVVrVo1vfzyy9l85v4n/druv16rXaxYMU2cOFFjxozRf/7zH5UrV04vvfSSYmJiTAndRYoU0ZgxY/T2229r3rx5Kl68uF5//XX17t3buY47XpsbGTx4sIKDgzVr1iy98847KlKkiHr37q2XXnopQ7DNrkmTJmWYZ7PZNHDgQA0cOFBJSUlasmSJli9frpo1a2rSpEkaO3ZsptsqXry4WrRooQ0bNqhbt24ZlhcoUEAzZ87UpEmTtHLlSn3zzTcqVKiQKlWqpMGDB7tc/30jDz74oJYsWaJp06YpMTFRpUqVUt++ffXss89m/+ABAKazGLczUgoAAPnY3r171b17d/3nP//JcSsrvM9zzz2n/fv3m9bVHwCQt3BNNwAAN3H16tUM86ZPny6r1arGjRt7oCLkZmfPnr1hKzcAIH+iezkAADcxZcoU7dmzR02bNpXNZtPGjRu1ceNGPfjggxnufY386/jx49q+fbvmz58vHx8fPfjgg54uCQCQSxC6AQC4iYiICP3444/6+OOPlZiYqNKlS2vw4MEZbmWG/G3btm0aPny4ypQpozFjxig0NNTTJQEAcgmu6QYAAAAAwCRc0w0AAAAAgEkI3QAAAAAAmITQDQAAAACASQjdAAAAAACYhNANAAAAAIBJCN0AAAAAAJiE0A0AAAAAgEkI3QAAAAAAmITQDQAAAACASf4PO2YtjI6GD2kAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Calculate average number of layers per circuit for each row\n", + "filtered_df['avg_layers_per_circuit'] = filtered_df['q_circuits'].apply(\n", + " lambda x: np.mean([str(circuit).count('new_layer_p') for circuit in x]) if isinstance(x, list)\n", + " else str(x).count('new_layer_p')\n", + ")\n", + "\n", + "# Calculate the overall average (mean of means)\n", + "mean_of_means = filtered_df['avg_layers_per_circuit'].mean()\n", + "print(f\"Average number of new_layer occurrences per circuit: {mean_of_means:.2f}\")\n", + "\n", + "# Visualize the distribution of average layers per row\n", + "\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(filtered_df['avg_layers_per_circuit'], bins=15, alpha=0.7, \n", + " color='skyblue', edgecolor='black')\n", + "plt.axvline(mean_of_means, color='red', linestyle='--', \n", + " label=f'Mean: {mean_of_means:.2f}')\n", + "plt.title('Average Number of Layers per Circuit in Each Row', fontsize=14)\n", + "plt.xlabel('Average Number of Layers', fontsize=12)\n", + "plt.ylabel('Frequency', fontsize=12)\n", + "plt.grid(True, alpha=0.3)\n", + "plt.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/FEATHER b/docs/sphinx/applications/python/qaoa_gpt_src/FEATHER new file mode 160000 index 00000000000..045db7963fb --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/FEATHER @@ -0,0 +1 @@ +Subproject commit 045db7963fb367eb9aed6439ceea96320e8ac7cc diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa.py b/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa.py new file mode 100644 index 00000000000..c7a2c58a411 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa.py @@ -0,0 +1,435 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import cudaq +import numpy as np +from scipy.optimize import minimize +import random +from qaoa_gpt_src.adapt_qaoa_pool import all_pool, qaoa_mixer, qaoa_single_x, qaoa_double +from qaoa_gpt_src.hamiltonian_graph import term_coefficients, term_words + + +def adapt_qaoa_run(hamiltonian, + qubits_num, + pool='all_pool', + gamma_0=0.01, + norm_threshold=1e-3, + energy_threshold=1e-5, + approx_ratio=1.0, + true_energy=0.0, + optimizer='BFGS', + parameter_shift=False, + max_iter=10, + seed_adapt=None, + verbose=False): + + E_prev = 0.0 + energy_list = [] + pool_list_index = [] + + # Get the coefficients and pauli words of the Hamiltonian + ham_coeffs = term_coefficients(hamiltonian) + ham_words = term_words(hamiltonian, qubits_num) + + # Get the pool of operators + if pool == 'all_pool': + pools = all_pool(qubits_num) + elif pool == 'qaoa_mixer': + pools = qaoa_mixer(qubits_num) + elif pool == 'qaoa_single_x': + pools = qaoa_single_x(qubits_num) + elif pool == 'qaoa_double_ops': + pools = qaoa_double(qubits_num) + else: + raise ValueError( + "Invalid pool name. Choose from 'all_pool', 'qaoa_mixer', 'qaoa_single_x', or 'qaoa_double'." + ) + + if verbose: + #print(f"Hamiltonian: {hamiltonian}") + #print(f"coefficients: {ham_coeffs}") + #print(f"words: {ham_words}") + print(f"Number of hamiltoninian terms: {hamiltonian.term_count}") + print(f"Pool size: {len(pools)}") + + pool_word = [] + for i in range(len(pools)): + temp = [] + for term in pools[i]: + temp.append(term.get_pauli_word(qubits_num)) + pool_word.append(temp) + #print(f"Pool words: {pool_word}") + + # Generate the commutator operator [H,Ai] + com_op = [] + for i in range(len(pools)): + op = pools[i] + com_op.append(hamiltonian * op - op * hamiltonian) + + ########################################### + # Get the initial state (psi_ref) + + @cudaq.kernel + def initial_state(qubits_num: int): + qubits = cudaq.qvector(qubits_num) + h(qubits) + + state = cudaq.get_state(initial_state, qubits_num) + + #print(state) + ############################################### + + # Circuit to compute the energy gradient with respect to the pool + @cudaq.kernel + def grad(state: cudaq.State, ham_words: list[cudaq.pauli_word], + ham_coeffs: list[complex], gamma_0: float): + q = cudaq.qvector(state) + + for i in range(len(ham_coeffs)): + exp_pauli(gamma_0 * ham_coeffs[i].real, q, ham_words[i]) + + # The qaoa circuit using the selected pool operator with max gradient + + @cudaq.kernel + def kernel_qaoa(qubits_num: int, ham_words: list[cudaq.pauli_word], + ham_coeffs: list[complex], + mixer_pool: list[list[cudaq.pauli_word]], + gamma: list[float], beta: list[float], num_layer: int): + + qubits = cudaq.qvector(qubits_num) + + h(qubits) + + for p in range(num_layer): + for i in range(len(ham_coeffs)): + exp_pauli(gamma[p] * ham_coeffs[i].real, qubits, ham_words[i]) + + for word in mixer_pool[p]: + exp_pauli(beta[p], qubits, word) + + beta = [] + gamma = [] + + mixer_pool = [] + mixer_pool_str = [] + layer = [] + + istep = 1 + + for iter in range(max_iter): + + if verbose: + print('Step: ', istep) + + # compute the gradient and find the mixer pool with large values. + # If norm is below the predefined threshold, stop calculation + + gradient_vec = [] + for op in com_op: + op = op * -1j + gradient_vec.append( + cudaq.observe(grad, op, state, ham_words, ham_coeffs, + gamma_0).expectation()) + + # Compute the norm of the gradient vector + norm = np.linalg.norm(np.array(gradient_vec)) + if verbose: + print('Norm of the gradient: ', norm) + + if norm <= norm_threshold: + if verbose: + print('\n', 'Final Result: ', '\n') + if verbose: + print('Norm of the gradient is below the threshold', norm) + if verbose: + print('Final mixer_pool: ', mixer_pool_str) + if verbose: + print('Number of layers: ', len(layer)) + if verbose: + print('Number of mixer pool in each layer: ', layer) + if verbose: + print('Final Energy: ', E_current) + if verbose: + print('Ratio of the energy: ', ratio) + + break + + else: + temp_pool = [] + temp_index = [] + tot_pool = 0 + + max_grad = np.max(np.abs(gradient_vec)) + + for i in range(len(pools)): + if np.abs(gradient_vec[i]) == max_grad: + tot_pool += 1 + temp_pool.append(pools[i]) + temp_index.append(i) + + if verbose: + print('Total number of pool with max gradient: ', tot_pool) + # Set the seed for the random number generator + # This ensures that the random choices are reproducible + # in each step of the iteration. + if seed_adapt is not None: + random.seed(seed_adapt) + + layer.append(1) + random_mixer = random.choice(temp_pool) + + # Save the mixer pool of the current step + for i in range(len(temp_index)): + if temp_pool[i] == random_mixer: + pool_list_index.append(temp_index[i]) + + pool_added = [] + pool_added_str = [] + for term in random_mixer: + pool_added.append( + cudaq.pauli_word(term.get_pauli_word(qubits_num))) + pool_added_str.append(term.get_pauli_word(qubits_num)) + + #mixer_pool = mixer_pool + [random_mixer.get_pauli_word(qubits_num)] + mixer_pool.append(pool_added) + mixer_pool_str.append(pool_added_str) + + if verbose: + print('Mixer pool at step', istep) + if verbose: + print(mixer_pool_str) + + num_layer = len(layer) + if verbose: + print('Number of layers: ', num_layer) + + beta_count = layer[num_layer - 1] + init_beta = [0.0] * beta_count + beta = beta + init_beta + gamma = gamma + [gamma_0] + theta = gamma + beta + + def cost(theta): + + #theta = theta.tolist() + gamma = theta[:num_layer] + beta = theta[num_layer:] + + energy = cudaq.observe(kernel_qaoa, hamiltonian, qubits_num, + ham_words, ham_coeffs, mixer_pool, gamma, + beta, num_layer).expectation() + return energy + + if parameter_shift: + + def parameter_shift(theta): + + parameter_count = len(theta) + epsilon = np.pi / 4 + # The gradient is calculated using parameter shift. + grad = np.zeros(parameter_count) + theta2 = theta.copy() + + for i in range(parameter_count): + theta2[i] = theta[i] + epsilon + exp_val_plus = cost(theta2) + theta2[i] = theta[i] - epsilon + exp_val_minus = cost(theta2) + grad[i] = (exp_val_plus - exp_val_minus) / (2 * epsilon) + theta2[i] = theta[i] + return grad + + if optimizer == 'COBYLA': + result_vqe = minimize(cost, + theta, + method='COBYLA', + options={ + 'rhobeg': 1.0, + 'maxiter': 10000, + 'disp': False, + 'tol': 1e-6 + }) + E_current = result_vqe.fun + theta = result_vqe.x.tolist() + if verbose: + print('Optmized Energy: ', result_vqe.fun, flush=True) + if verbose: + print('Optimizer exited successfully: ', + result_vqe.success, + flush=True) + + elif optimizer == 'BFGS': + if parameter_shift: + result_vqe = minimize(cost, + theta, + method='BFGS', + jac=parameter_shift, + tol=1e-5) + E_current = result_vqe.fun + theta = result_vqe.x.tolist() + if verbose: + print('Optmized Energy: ', result_vqe.fun, flush=True) + if verbose: + print('Optimizer exited successfully: ', + result_vqe.success, + flush=True) + else: + result_vqe = minimize(cost, + theta, + method='BFGS', + jac='2-point', + options={'gtol': 1e-4}) + E_current = result_vqe.fun + theta = result_vqe.x.tolist() + if verbose: + print('Optmized Energy: ', result_vqe.fun, flush=True) + if verbose: + print('Optimizer exited successfully: ', + result_vqe.success, + flush=True) + + elif optimizer == 'L-BFGS-B': + if parameter_shift: + result_vqe = minimize(cost, + theta, + method='L-BFGS-B', + jac=parameter_shift, + tol=1e-5) + E_current = result_vqe.fun + theta = result_vqe.x.tolist() + if verbose: + print('Optmized Energy: ', result_vqe.fun, flush=True) + if verbose: + print('Optimizer exited successfully: ', + result_vqe.success, + flush=True) + else: + result_vqe = minimize(cost, + theta, + method='L-BFGS-B', + jac='2-point', + tol=1e-5) + E_current = result_vqe.fun + theta = result_vqe.x.tolist() + if verbose: + print('Optmized Energy: ', result_vqe.fun, flush=True) + if verbose: + print('Optimizer exited successfully: ', + result_vqe.success, + flush=True) + + energy_list.append(E_current) + + if verbose: + print('Result from the step ', istep) + if verbose: + print('Optmized Energy: ', result_vqe.fun) + + dE = np.abs(E_current - E_prev) + E_prev = E_current + + if verbose: + print('dE= :', dE) + + ratio = E_current / true_energy + if verbose: + print('Ratio of the energy: ', ratio) + + gamma = theta[:num_layer] + beta = theta[num_layer:] + + if dE <= energy_threshold: + if verbose: + print('\n', 'Final Result: ', '\n') + if verbose: + print('dE below the threshold is satisfied: ', dE) + if verbose: + print('Final mixer_pool: ', mixer_pool_str) + if verbose: + print('Number of layers: ', len(layer)) + if verbose: + print('Number of mixer pool in each layer: ', layer) + if verbose: + print('Final Energy= ', E_current) + if verbose: + print('Ratio of the energy: ', ratio) + + break + + elif ratio >= approx_ratio: + if verbose: + print('\n', 'Final Result: ', '\n') + if verbose: + print('Approximation ratio is satisfied', ratio) + if verbose: + print('Final mixer_pool: ', mixer_pool_str) + if verbose: + print('Number of layers: ', len(layer)) + if verbose: + print('Number of mixer pool in each layer: ', layer) + if verbose: + print('Final Energy= ', E_current) + if verbose: + print('Ratio of the energy: ', ratio) + break + + else: + + # Compute the state of this current step for the gradient + state = cudaq.get_state(kernel_qaoa, qubits_num, ham_words, + ham_coeffs, mixer_pool, gamma, beta, + num_layer) + if verbose: + print('State at step ', istep) + #print(state) + istep += 1 + if verbose: + print('\n') + + if iter == max_iter - 1: + if verbose: + print('\n', 'Final Result: ', '\n') + if verbose: + print( + 'Maximum number of iterations reached without satisfying the convergence criteria.' + ) + if verbose: + print('Final mixer_pool: ', mixer_pool_str) + if verbose: + print('Number of layers: ', len(layer)) + if verbose: + print('Number of mixer pool in each layer: ', layer) + if verbose: + print('Final Energy= ', E_current) + + if verbose: + print('\n', 'Sampling the Final ADAPT QAOA circuit', '\n') + # Sample the circuit + count = cudaq.sample(kernel_qaoa, + qubits_num, + ham_words, + ham_coeffs, + mixer_pool, + gamma, + beta, + num_layer, + shots_count=5000) + if verbose: + print('The most probable max cut: ', count.most_probable()) + if verbose: + print('All bitstring from circuit sampling: ', count) + + return (energy_list, mixer_pool_str, pool_list_index, gamma, beta, ratio, + str(count.most_probable()), len(layer), result_vqe.success) diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa_pool.py b/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa_pool.py new file mode 100644 index 00000000000..705ccfe5e05 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/adapt_qaoa_pool.py @@ -0,0 +1,61 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import cudaq +from cudaq import spin + + +def qaoa_mixer(n): + + term = spin.x(0) + + for i in range(1, n): + term += spin.x(i) + + pool = [term] + return pool + + +def qaoa_single_x(n): + + pool = [] + + for i in range(n): + pool.append(cudaq.SpinOperator(spin.x(i))) + + return pool + + +def qaoa_double(n): + + pool = [] + + for i in range(n - 1): + for j in range(i + 1, n): + pool.append( + cudaq.SpinOperator(spin.x(i)) * cudaq.SpinOperator(spin.x(j))) + pool.append( + cudaq.SpinOperator(spin.y(i)) * cudaq.SpinOperator(spin.y(j))) + pool.append( + cudaq.SpinOperator(spin.y(i)) * cudaq.SpinOperator(spin.z(j))) + pool.append( + cudaq.SpinOperator(spin.z(i)) * cudaq.SpinOperator(spin.y(j))) + + return pool + + +def all_pool(qubits_num): + return (qaoa_single_x(qubits_num) + qaoa_mixer(qubits_num) + + qaoa_double(qubits_num)) diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/custom_feather.py b/docs/sphinx/applications/python/qaoa_gpt_src/custom_feather.py new file mode 100644 index 00000000000..e18e0f57eeb --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/custom_feather.py @@ -0,0 +1,154 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import numpy as np +import networkx as nx +import sys +import os +import math +import glob + +# Add the FEATHER module path +#feather_path = '/home/cudaq/qaoa-gpt-cudaq-feather' +# Change from absolute path to current directory +feather_path = os.path.dirname(os.path.abspath(__file__)) + +if feather_path not in sys.path: + sys.path.append(feather_path) + +# Import the original FEATHER implementation +try: + from FEATHER.src.feather import FEATHER as OrigFeatherNode # For node-level embedding + from FEATHER.src.feather import FEATHERG as OrigFeatherGraph # For graph-level embedding +except ImportError as e: + print(f"Import error: {e}") + print("Current sys.path:", sys.path) + print("Checking if files exist:") + print(glob.glob(os.path.join(feather_path, 'FEATHER/src/*'))) + + +class CustomFeatherGraph: + """Custom wrapper for the original FEATHER implementation with multiple node features""" + + def __init__( + self, + theta_max=2.5, # Default from original implementation + eval_points=25, # Default from original implementation + order=5, # Default from original implementation + pooling="mean", # Default from original implementation + seed=42 # Add seed parameter with default value + ): + self.theta_max = theta_max + self.eval_points = eval_points + self.order = order + self.pooling = pooling + self.seed = seed + self.embedding = None + + def _extract_features(self, graph): + """ + Extract node features: + 1. Log degree + 2. Clustering coefficient + """ + # Number of nodes in the graph + num_nodes = graph.number_of_nodes() + + # Initialize feature matrix + features = np.zeros((num_nodes, 2)) # 2 features per node + + # Calculate clustering coefficients for all nodes at once + clustering_coeffs = nx.clustering(graph) + + # Extract log degree and clustering coefficient for each node + for node in range(num_nodes): + # Log of degree + 1 (to avoid log(0)) + features[node, 0] = math.log(graph.degree(node) + 1.0) + + # Clustering coefficient (default to 0 if not available) + features[node, 1] = clustering_coeffs.get(node, 0.0) + + return features + + def fit(self, graphs): + """ + Fit the model and generate embeddings for a list of graphs + + Args: + graphs: List of networkx graphs to embed + """ + # Set random seed + np.random.seed(self.seed) + + # Process each graph to ensure it meets FEATHER requirements + processed_graphs = [] + for graph in graphs: + # Ensure the graph has self-loops and integer node labels + graph = nx.convert_node_labels_to_integers(graph) + graph = graph.copy() + for node in graph.nodes(): + if not graph.has_edge(node, node): + graph.add_edge(node, node) + processed_graphs.append(graph) + + # Initialize embedding list + all_embeddings = [] + + # Process each graph individually to use multiple features + for graph in processed_graphs: + # Extract node features (log degree and clustering coefficient) + node_features = self._extract_features(graph) + + # Create edge list for FEATHER + edge_list = [(int(u), int(v)) for u, v in graph.edges()] + + # Create FEATHER node-level embedding model + feather_node = OrigFeatherNode(theta_max=self.theta_max, + eval_points=self.eval_points, + order=self.order) + + # Fit and transform to get node embeddings + feather_node.fit(graph, node_features) + node_embeddings = feather_node.get_embedding() + + # Pool node embeddings to get graph embedding + if self.pooling == "mean": + graph_embedding = np.mean(node_embeddings, axis=0) + elif self.pooling == "max": + graph_embedding = np.max(node_embeddings, axis=0) + elif self.pooling == "min": + graph_embedding = np.min(node_embeddings, axis=0) + else: + graph_embedding = np.mean(node_embeddings, + axis=0) # Default to mean + + all_embeddings.append(graph_embedding) + + # Convert to numpy array + self.embedding = np.array(all_embeddings) + + # Print embedding dimension for verification + if len(processed_graphs) > 0: + print(f"Generated embedding dimension: {self.embedding.shape[1]}") + + # Expected dimension calculation + expected_dim = 2 * self.eval_points * 2 * self.order # 2 features * eval_points * 2 (sin/cos) * order + print(f"Expected dimension: {expected_dim}") + + return self + + def get_embedding(self): + """Return the graph embeddings""" + return self.embedding diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/generate_adapt_qaoa_data.py b/docs/sphinx/applications/python/qaoa_gpt_src/generate_adapt_qaoa_data.py new file mode 100644 index 00000000000..98a74459925 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/generate_adapt_qaoa_data.py @@ -0,0 +1,391 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import os +import json +import socket +import time +import numpy as np +import pandas as pd +from datetime import datetime + +import cudaq +from qaoa_gpt_src.adapt_qaoa import adapt_qaoa_run +from qaoa_gpt_src.hamiltonian_graph import term_coefficients, term_words, max_cut_ham +from qaoa_gpt_src.graph_functions import generate_random_graph, graph_to_edgelist, edgelist_to_graph, graph_to_adj_m, add_rand_weights_to_graph, norm_elist_weights + +from qaoa_gpt_src.max_cut_classical_sol import brute_force_max_cut, one_exchange, simulated_annealing_maxcut + +# Set the target to NVIDIA GPU +####################################################### +# Set the target to NVIDIA GPU with double precision support +# cudaq.set_target("nvidia") # Uncomment this line if you want to use single precision +# For double precision, use the following line: +cudaq.set_target("nvidia", option="fp64") +####################################################### + + +def ensure_dirs(output_dir): + for sub in ['hams', 'res', 'graphs', 'traces']: + os.makedirs(os.path.join(output_dir, sub), exist_ok=True) + + +####################################################### + + +# Scale the weights of the edges in the edge list by a given coefficient +def scale_elist_weights(e_list, coef): + """ + Scales the weights of each edge in the edge list by the given coefficient. + + Args: + e_list (list of tuples): Each tuple is (node1, node2, weight). + coef (float): The scaling coefficient. + + Returns: + list of tuples: New edge list with scaled weights. + """ + return [(node1, node2, weight * coef) for (node1, node2, weight) in e_list] + + +###################################################### +def generate_data_max_cut(output_dir='adapt_results', + graphs_number=1, + graphs_input_json="N/A", + n_nodes=8, + weighted=True, + use_negative_weights=False, + use_brute_force=True, + use_simulated_annealing=True, + use_one_exchange=True, + op_pool='all_pool', + init_gamma: list[float] = [0.01], + scaling_coef=1.0, + norm_weights=False, + norm_coef=1.0, + trials_per_graph=1, + optimizer='BFGS', + approx_ratio=0.97, + max_iter=10, + norm_threshold=1e-3, + energy_threshold=1e-9, + multi_gamma=False, + p_init=0.3, + p_final=0.9, + seed_g=None, + seed_weight=None, + seed_adapt=None, + verbose=True): + """ + Generates data for ADAPT-QAOA on the Max-Cut problem. + Args: + output_dir (str): Directory to save the results. + graphs_number (int): Number of graphs to generate or process. + graphs_input_json (str): Path to a JSON file containing graph data. If "N/A", generates random graphs. + n_nodes (int): Number of nodes in each graph. + weighted (bool): Whether to use weighted edges. + use_negative_weights (bool): Whether to allow negative weights in the graph. + use_brute_force (bool): Whether to compute the brute force solution. + use_simulated_annealing (bool): Whether to compute the simulated annealing solution. + use_one_exchange (bool): Whether to compute the one exchange solution. + op_pool (str): Type of operator pool to use ('all_pool' or 'qaoa_mixer' or 'qaoa_single_x' or 'qaoa_double_ops'). + init_gamma (list): Initial gamma values for ADAPT-QAOA trials. + scaling_coef (float): Coefficient to scale edge weights. + norm_weights (bool): Whether to normalize edge weights. + norm_coef (float): Coefficient for normalizing edge weights if norm_weights is True. + trials_per_graph (int): Number of trials per graph for ADAPT-QAOA. + optimizer (str): Optimizer to use for ADAPT-QAOA ('BFGS', 'L-BFGS-B', 'COBYLA'). + approx_ratio (float): Approximation ratio threshold for early stopping. + max_iter (int): Maximum number of iterations for the adapt-qaoa iteration. + norm_threshold (float): Threshold for gradients norm in ADAPT-QAOA for early stopping. + energy_threshold (float): Threshold for energy convergence in ADAPT-QAOA. + multi_gamma (bool): Whether to run multiple trials with different initial gamma values to generate multiple validated circuit. + Useful if you want to check for more validated circuits after approx ratio achieved with one gamma. + p_init (float): Initial probability for Erdos-Renyi graph generation. + p_final (float): Final probability for Erdos-Renyi graph generation. + the probability will be randomly selected between p_init and p_final. + seed_g (int): Random seed for for Erdos-Renyi graph generation reproducibility. + seed_weight (int): Random seed for edge weight generation reproducibility. + seed_adapt (int): Random seed for ADAPT-QAOA reproducibility. + verbose (bool): Whether to print detailed logs during execution. + + Returns: + None: The function saves results to the specified output directory. + + """ + + ensure_dirs(output_dir) + + pid = os.getpid() + hostname = socket.gethostname() + ts_string = datetime.now().strftime("%y-%m-%d__%H_%M") + + results_df = pd.DataFrame() + hams_df = pd.DataFrame() + graphs_df = pd.DataFrame( + columns=['graph_num', 'g_method', 'edgelist_json', 'H_frob_norm']) + traces_df = pd.DataFrame() + + # Load graphs from JSON if provided + if graphs_input_json != "N/A": + with open(graphs_input_json, 'r') as f: + json_graphs_dict = json.load(f) + graphs_number = len(json_graphs_dict) + graph_names_list = list(json_graphs_dict.keys()) + else: + graphs_number = graphs_number + + # Loop through the number of graphs to generate or process + if verbose: + print(f"Generating or processing {graphs_number} graphs...") + + graph_rows = [] + result_rows = [] + + for graph_num in range(graphs_number): + + if graphs_input_json == "N/A": + cur_graph_name = f"Graph_{graph_num+1}" + g_unweighted, g_method = generate_random_graph( + n_nodes, p_init=p_init, p_final=p_final, seed_g=seed_g, methods=["erdos_renyi"]) + if weighted: + g = add_rand_weights_to_graph(g_unweighted, + seed_weight=seed_weight, neg_weights=use_negative_weights) + else: + g = g_unweighted + else: + cur_graph_name = graph_names_list[graph_num] + cur_graph_elist = json_graphs_dict[cur_graph_name]["elist"] + n_nodes = json_graphs_dict[cur_graph_name]["n_nodes"] + g = edgelist_to_graph(cur_graph_elist, num_vertices=n_nodes) + g_method = "input_file" + + if verbose: + print(f"Processing {cur_graph_name}...") + if verbose: + print(f"Graph method: {g_method}") + if verbose: + print(f"Graph edgelist: {graph_to_edgelist(g)}") + + e_list = graph_to_edgelist(g) + + # update e_list to change index to 1 based for tokenization later. + e_list_mod = [ + (node1 + 1, node2 + 1, weight) for (node1, node2, weight) in e_list + ] + edgelist_json = json.dumps(e_list_mod) + + if scaling_coef != 1.0: + e_list = scale_elist_weights(e_list, scaling_coef) + + if norm_weights: + e_list, norm_coef = norm_elist_weights(e_list) + + ############################################### + # Build up the problem hamiltonian + + spin_ham = max_cut_ham(e_list) + #if verbose: print(f"Problem Hamiltonian: {spin_ham}") + + h_frob_norm = np.linalg.norm(spin_ham.to_matrix()) + if verbose: + print(f"Frobenius norm of the Hamiltonian: {h_frob_norm}") + + # Store the graph data + graph_rows.append({ + 'graph_num': graph_num + 1, + 'g_method': g_method, + 'edgelist_json': edgelist_json, + 'H_frob_norm': h_frob_norm + }) + + # After the loop: + graphs_df = pd.DataFrame( + graph_rows, + columns=['graph_num', 'g_method', 'edgelist_json', 'H_frob_norm']) + + ############################################ + # Classical solutions of max-cut problem + if use_brute_force: + # Brute Force + brute_force_cut_value, partition, binary_vector = brute_force_max_cut( + g) + + if use_simulated_annealing: + # Simulated Annealing + sa_partition, sa_cut_value, sa_binary_vector = simulated_annealing_maxcut( + g) + + if use_one_exchange: + # one_exchange + one_exchange_cut_value, one_exchange_partition, one_exchange_binary_vector = one_exchange( + g) + ################################################ + + # Quantum solutions of max-cut problem using ADAPT-QAOA + if verbose: + print(f"Preparing to run ADAPT-QAOA for graph {graph_num+1}...") + + for gamma in init_gamma: + for trial_num in range(trials_per_graph): + + if verbose: + print( + f"Running ADAPT-QAOA for graph {graph_num+1}, trial {trial_num+1}..." + ) + if verbose: + print(f"Using initial gamma: {gamma}") + + # Run ADAPT-QAOA + if verbose: + print("Running ADAPT-QAOA...") + + # Run the ADAPT-QAOA algorithm + qubits_num = len(g.nodes) + pool = op_pool + g0 = gamma + + if use_simulated_annealing: + true_energy = sa_cut_value # Use the simulated annealing cut value as the true energy + classical_cut = sa_binary_vector + elif use_one_exchange: + true_energy = one_exchange_cut_value + classical_cut = one_exchange_binary_vector + elif use_brute_force: + true_energy = brute_force_cut_value + classical_cut = binary_vector + else: + true_energy = -999.0 + classical_cut = "N/A" + + start_time = time.time() + + # Run the adapt_qaoa function + adapt_qaoa_result = adapt_qaoa_run( + spin_ham, + qubits_num, + pool=pool, + gamma_0=g0, + norm_threshold=norm_threshold, + energy_threshold=energy_threshold, + approx_ratio=approx_ratio, + true_energy=true_energy, + optimizer=optimizer, + max_iter=max_iter, + seed_adapt=seed_adapt, + verbose=verbose) + + end_time = time.time() + elapsed_time = end_time - start_time + + if isinstance(adapt_qaoa_result, tuple): + adapt_qaoa_result = list( + adapt_qaoa_result) # Convert to list + adapt_qaoa_result[2] = [ + int(i) + 1 for i in adapt_qaoa_result[2] + ] # Modify the third element indexes to be 1-based + adapt_qaoa_result = tuple( + adapt_qaoa_result + ) # Convert back to tuple (if required) + else: + adapt_qaoa_result[2] = [ + int(i) + 1 for i in adapt_qaoa_result[2] + ] # Modify directly if not a tuple + + if verbose: + print( + f"ADAPT-QAOA completed in {elapsed_time:.2f} seconds.") + print('Energy list: ', adapt_qaoa_result[0]) + print('Mixer pool as pauli word: ', adapt_qaoa_result[1]) + print('Mixer pool as index: ', adapt_qaoa_result[2]) + print('gamma list: ', adapt_qaoa_result[3]) + print('beta list: ', adapt_qaoa_result[4]) + print('Approx. ratio: ', adapt_qaoa_result[5]) + print('Max cut: ', adapt_qaoa_result[6]) + print('Number of layers: ', adapt_qaoa_result[7]) + print('Optimizer success flag: ', adapt_qaoa_result[8]) + print('\n') + + # Prepare the results for saving + result_rows.append({ + 'method': 'ADAPT-QAOA', + 'graph_name': cur_graph_name, + 'graph_num': graph_num + 1, + 'trial_num': trial_num + 1, + 'n_nodes': n_nodes, + 'init_gamma': gamma, + 'optimizer': optimizer, + 'pool_type': op_pool, + 'edge_weight_scaling_coef': scaling_coef, + 'edge_weight_norm_coef': norm_coef, + 'energy_list': adapt_qaoa_result[0], + 'true_energy': true_energy, + 'mixer_pool_pauli_word': adapt_qaoa_result[1], + 'mixer_pool_index': adapt_qaoa_result[2], + 'gamma_coef': adapt_qaoa_result[3], + 'beta_coef': adapt_qaoa_result[4], + 'approx_ratio': adapt_qaoa_result[5], + 'cut_adapt': adapt_qaoa_result[6], + 'cut_classical': classical_cut, + 'num_layers': adapt_qaoa_result[7], + 'optimizer_success_flag': adapt_qaoa_result[8], + 'elapsed_time': elapsed_time + }) + + results_df = pd.DataFrame( + result_rows, + columns=[ + 'method', 'graph_name', 'graph_num', 'trial_num', + 'n_nodes', 'init_gamma', 'energy_list', 'true_energy', + 'optimizer', 'pool_type', 'edge_weight_scaling_coef', + 'edge_weight_norm_coef', 'mixer_pool_pauli_word', + 'mixer_pool_index', 'gamma_coef', 'beta_coef', + 'approx_ratio', 'cut_adapt', 'cut_classical', + 'num_layers', 'optimizer_success_flag', 'elapsed_time' + ]) + # Early stopping: End of trial if approximation ratio is reached + if adapt_qaoa_result[5] >= approx_ratio: + if verbose: + print( + f"Approximation ratio {adapt_qaoa_result[5]} reached, stopping early." + ) + break + # Early stopping: End of graph processing if approximation ratio is reached + if adapt_qaoa_result[5] >= approx_ratio and not multi_gamma: + if verbose: + print( + f"Approximation ratio {adapt_qaoa_result[5]} reached for graph {graph_num+1}, stopping further trials." + ) + # uncomment if you do not want to check for more validated circuits + break + + #write results to files + if verbose: + print("Writing results to files...") + + # Save results DataFrame to CSV + results_df.to_csv(os.path.join(output_dir, 'res', + f'pid{pid}_{ts_string}_results.csv'), + index=False) + + # Save graphs DataFrame to CSV + graphs_df.to_csv(os.path.join(output_dir, 'graphs', + f'pid{pid}_{ts_string}_graphs.csv'), + index=False) + + if verbose: + print("Data generation completed successfully.") + + return diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/graph_functions.py b/docs/sphinx/applications/python/qaoa_gpt_src/graph_functions.py new file mode 100644 index 00000000000..7bb1e80f439 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/graph_functions.py @@ -0,0 +1,111 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import networkx as nx +import numpy as np +import random + + +def graph_to_adj_m(g): + """Convert a NetworkX graph to an adjacency matrix (numpy array).""" + return nx.to_numpy_array(g) + + +def graph_to_edgelist(g): + """Return a weighted edge list: (src, dst, weight) for all edges.""" + return [(u, v, d.get('weight', 1.0)) for u, v, d in g.edges(data=True)] + + +def edgelist_to_graph(edgelist, num_vertices=0): + """Create a weighted undirected graph from an edge list.""" + if num_vertices == 0: + num_vertices = max(max(src, dst) for src, dst, _ in edgelist) + 1 + g = nx.Graph() + g.add_nodes_from(range(num_vertices)) + for src, dst, w in edgelist: + g.add_edge(src, dst, weight=w) + return g + + +def generate_random_graph(n, p_init, p_final, seed_g, methods=None): + """ + Generate a connected random graph using specified methods with random parameters. + Returns: (graph, method) + """ + if methods is None: + methods = [ + "erdos_renyi", "barabasi_albert", "watts_strogatz", + "random_regular", "bipartite" + ] + method = random.choice(methods) + while True: + if method == "erdos_renyi": + p = random.uniform(p_init, p_final) + G = nx.erdos_renyi_graph(n, p, seed_g) + elif method == "barabasi_albert": + m = random.randint(1, n - 1) + G = nx.barabasi_albert_graph(n, m) + elif method == "watts_strogatz": + k = random.randint(2, n - 1) + p = random.uniform(p_init, p_final) + G = nx.watts_strogatz_graph(n, k, p) + elif method == "random_regular": + d = random.randint(2, n - 1) + if n * d % 2 == 0: + G = nx.random_regular_graph(d, n) + else: + continue + elif method == "bipartite": + n1 = random.randint(2, n - 1) + n2 = n - n1 + G = nx.complete_bipartite_graph(n1, n2) + else: + raise ValueError(f"Unknown method: {method}") + + if G.number_of_edges() > 0 and nx.is_connected(G): + break + return G, method + + +def add_rand_weights_to_graph(g, seed_weight, neg_weights=False): + """ + Add random weights to all edges in the graph. + Returns a new weighted graph. + """ + if seed_weight is not None: + random.seed(seed_weight) + + g_weighted = nx.Graph() + g_weighted.add_nodes_from(g.nodes()) + for u, v in g.edges(): + w = round(random.random(), 2) + while w == 0.0: + w = round(random.random(), 2) + if neg_weights: + w *= random.choice([-1, 1]) + g_weighted.add_edge(u, v, weight=w) + return g_weighted + + +def norm_elist_weights(e_list): + """ + Normalize edge weights so their sum of absolute values is 1. + Returns: (normalized_edge_list, total_weight) + """ + total_weight = sum(abs(weight) for _, _, weight in e_list) + scaled_weighted_edge_list = [ + (u, v, weight / total_weight) for u, v, weight in e_list + ] + return scaled_weighted_edge_list, total_weight diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/hamiltonian_graph.py b/docs/sphinx/applications/python/qaoa_gpt_src/hamiltonian_graph.py new file mode 100644 index 00000000000..553fc62bead --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/hamiltonian_graph.py @@ -0,0 +1,59 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +from cudaq import spin +import cudaq + + +def max_cut_ham(edges): + """ + Generate a Hamiltonian for the Max-Cut problem. + Args: + edges: List of edges in the graph. + weight: List of weights for each edge. + Returns: + Hamiltonian for the Max-Cut problem. + """ + ham = 0.0 + + for edge in range(len(edges)): + + qubitu = edges[edge][0] + qubitv = edges[edge][1] + weight = edges[edge][2] + # Add a term to the Hamiltonian for the edge (u,v) + ham += 0.5 * (weight * spin.z(qubitu) * spin.z(qubitv) - + weight * spin.i(qubitu) * spin.i(qubitv)) + + return ham + + +# Collect coefficients from a spin operator so we can pass them to a kernel +def term_coefficients(ham: cudaq.SpinOperator) -> list[complex]: + result = [] + for term in ham: + result.append(term.evaluate_coefficient()) + return result + + +# Collect Pauli words from a spin operator so we can pass them to a kernel +def term_words(ham: cudaq.SpinOperator, qubits_num) -> list[str]: + # Our kernel uses these words to apply exp_pauli to the entire state. + # we hence ensure that each pauli word covers the entire space. + + result = [] + for term in ham: + result.append(term.get_pauli_word(qubits_num)) + return result diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/max_cut_classical_sol.py b/docs/sphinx/applications/python/qaoa_gpt_src/max_cut_classical_sol.py new file mode 100644 index 00000000000..652d57fdb8b --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/max_cut_classical_sol.py @@ -0,0 +1,140 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import random +import numpy as np +import networkx as nx + + +# Compute the max cut of a graph and its value using brute force, simulated annealing, or one-exchange algorithm. +# The brute force method is exact but computationally expensive for large graphs. +# Simulated annealing provides a probabilistic approach that can yield good results in reasonable time. +# One-exchange is a heuristic method provided by NetworkX. +def brute_force_max_cut(graph): + """ + Computes the Max-Cut of a weighted graph using a brute-force approach. + + Args: + graph (nx.Graph): The input weighted graph. + + Returns: + tuple: A tuple containing: + - float: The value of the Max-Cut. + - tuple: A tuple of two sets representing the partition of nodes + that achieves the Max-Cut. + """ + nodes = list(graph.nodes) + max_cut_value = -1 + best_partition = (set(), set()) + + for i in range(1, 2**len(nodes) - 1): + binary_representation = bin(i)[2:].zfill(len(nodes)) + group1 = { + nodes[j] + for j, bit in enumerate(binary_representation) + if bit == '1' + } + group2 = set(nodes) - group1 + + cut_value = 0 + for u, v, data in graph.edges(data=True): + if (u in group1 and v in group2) or (u in group2 and v in group1): + cut_value += data.get( + 'weight', 1) # Use 1 as default weight if not specified + + if cut_value > max_cut_value: + max_cut_value = cut_value + best_partition = (group1, group2) + + # convert to binary representation + binary_vector_1 = [0] * len(nodes) + for node in best_partition[0]: + binary_vector_1[node] = 1 + + binary_vector_2 = [0] * len(nodes) + for node in best_partition[1]: + binary_vector_2[node] = 1 + + binary_vector = (''.join(str(bit) for bit in binary_vector_1), + ''.join(str(bit) for bit in binary_vector_2)) + + return (-1 * max_cut_value), best_partition, binary_vector + + +def simulated_annealing_maxcut(graph, + initial_temp=1000, + cooling_rate=0.95, + iterations=1000): + nodes = list(graph.nodes) + current_solution = {node: random.choice([0, 1]) for node in nodes} + + def cut_value(solution): + return sum(data['weight'] + for u, v, data in graph.edges(data=True) + if solution[u] != solution[v]) + + current_value = cut_value(current_solution) + best_solution = current_solution.copy() + best_value = current_value + temp = initial_temp + + for _ in range(iterations): + node = random.choice(nodes) + new_solution = current_solution.copy() + new_solution[node] = 1 - new_solution[node] # flip side + new_value = cut_value(new_solution) + + delta = new_value - current_value + if delta > 0 or random.random() < np.exp(delta / temp): + current_solution = new_solution + current_value = new_value + if new_value > best_value: + best_solution = new_solution + best_value = new_value + + temp *= cooling_rate + + set1 = [node for node in best_solution if best_solution[node] == 0] + set2 = [node for node in best_solution if best_solution[node] == 1] + + binary_vector_1 = [0] * len(nodes) + for node in set1: + binary_vector_1[node] = 1 + + binary_vector_2 = [0] * len(nodes) + for node in set2: + binary_vector_2[node] = 1 + + binary_vector = (''.join(str(bit) for bit in binary_vector_1), + ''.join(str(bit) for bit in binary_vector_2)) + + return (set(set1), set(set2)), (-1 * best_value), binary_vector + + +def one_exchange(graph): + + curr_cut_size, partition = nx.approximation.one_exchange(graph, + weight='weight') + # convert to binary representation + binary_vector_1 = [0] * len(graph.nodes) + for node in partition[0]: + binary_vector_1[node] = 1 + binary_vector_2 = [0] * len(graph.nodes) + for node in partition[1]: + binary_vector_2[node] = 1 + binary_vector = (''.join(str(bit) for bit in binary_vector_1), + ''.join(str(bit) for bit in binary_vector_2)) + + return (curr_cut_size * -1), partition, binary_vector diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/model_interface.py b/docs/sphinx/applications/python/qaoa_gpt_src/model_interface.py new file mode 100644 index 00000000000..fe95c144309 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/model_interface.py @@ -0,0 +1,207 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +import pickle +from contextlib import nullcontext +import torch +import tiktoken +from nanoGPT.model_pad_gemb import GPTConfig as GPTConfig_gemb +from nanoGPT.model_pad_gemb import GPT as GPT_gemb + +from nanoGPT.model_pad import GPTConfig as GPTConfig_nogemb +from nanoGPT.model_pad import GPT as GPT_nogemb + +import pandas as pd +import json +from tqdm import tqdm +import random +import numpy as np +from matplotlib import pyplot as plt +from collections import defaultdict +from pathlib import Path + +from qaoa_gpt_src.util import (generate_circ_from_df, eval_adapt_gpt_circ_cudaq, + prepare_model_input) + +dtype_str_to_torch_dict = { + "float32": torch.float32, + "float": torch.float32, + "float16": torch.float16, + "half": torch.float16, + "bfloat16": torch.bfloat16, + "float64": torch.float64, + "double": torch.float64, +} + +class QAOA_GPT(): + + def __init__( + self, + model_ckpt, + config_file, + data_dir, + device, # examples: 'cpu', 'cuda', 'cuda:0', 'cuda:1', etc. + n_nodes='infer', + temp_folder='adapt_gpt_temp_data', + ): + + config_fpath = Path(config_file) + assert config_fpath.is_file() + + print(f"Loading config from: {config_fpath}") + config_vars = {} + with open(config_fpath) as f: + exec(f.read(), config_vars) + + self.pool_type = config_vars['pool_type'] + self.use_graph_emb = config_vars['use_graph_emb'] + + if 'n_nodes' not in config_vars: + if n_nodes == 'infer': + raise AttributeError( + """Number of nodes is not found in the provided config. + You need to supply it as an argument in AdaptGPT constructor: + AdaptGPT(..., n_nodes=,...) + """) + else: + assert type(n_nodes) == int + self.n_nodes = n_nodes + else: + self.n_nodes = config_vars['n_nodes'] + + #self.out_dir = Path(out_dir) + self.data_dir = Path(data_dir) + self.model_ckpt = Path(model_ckpt) + self.temp_folder = Path(temp_folder) + + self.seed = 1337 + self.init_from = 'resume' # either 'resume' (from an out_dir) or a gpt2 variant (e.g. 'gpt2-xl') + self.device = device + if self.device == 'cuda': + if torch.cuda.is_available() and torch.cuda.is_bf16_supported(): + self.dtype = 'bfloat16' + else: + self.dtype = 'float16' + + self.compile = False # use PyTorch 2.0 to compile the model to be faster + + torch.manual_seed(self.seed) + torch.cuda.manual_seed(self.seed) + torch.backends.cuda.matmul.allow_tf32 = True # allow tf32 on matmul + torch.backends.cudnn.allow_tf32 = True # allow tf32 on cudnn + self.device_type = 'cuda' if 'cuda' in self.device else 'cpu' # for later use in torch.autocast + ptdtype = dtype_str_to_torch_dict[self.dtype] + #ptdtype = { + # 'float32': torch.float32, + # 'bfloat16': torch.bfloat16, + # 'float16': torch.float16 + #}[self.dtype] + self.ctx = nullcontext( + ) if self.device_type == 'cpu' else torch.amp.autocast( + device_type=self.device_type, dtype=ptdtype) + + self.meta = pd.read_pickle(f'{data_dir}/meta.pkl') + + if self.use_graph_emb: + self.gptconfig = GPTConfig_gemb + self.gpt = GPT_gemb + else: + self.gptconfig = GPTConfig_nogemb + self.gpt = GPT_nogemb + + self.model = self.open_model(self.model_ckpt) + + return None + + def open_model( + self, + model_fpath, + ): + # init from a model saved in a specific directory + out_path = Path(model_fpath) + checkpoint = torch.load(out_path, map_location=self.device) + gptconf = self.gptconfig(**checkpoint['model_args']) + model = self.gpt(gptconf) + state_dict = checkpoint['model'] + unwanted_prefix = '_orig_mod.' + for k, v in list(state_dict.items()): + if k.startswith(unwanted_prefix): + state_dict[k[len(unwanted_prefix):]] = state_dict.pop(k) + model.load_state_dict(state_dict) + model.eval() + model.to(self.device) + + return model + + def generate_circ_from_nx( + self, + graphs_container, + calculate_classic_maxcut=True, + n_samples_per_batch=50, # max number of distinct graphs in a batch + num_samples=5, # number of samples to draw + max_new_tokens=150, # number of tokens generated in each sample + temperature=0.1, # 1.0 = no change, < 1.0 = less random, > 1.0 = more random, in predictions + top_k=200, # retain only the top_k most likely tokens, clamp others to have 0 probability + ): + graphs_nx_df, feather_par_emb, emb_graph_id_to_idx_dict = prepare_model_input( + graphs_container, + n_nodes=self.n_nodes, + calculate_classic_maxcut=calculate_classic_maxcut, + ) + + if self.device == 'cpu': + emb_dtype = "float" + else: + emb_dtype = self.dtype + + gc_df = generate_circ_from_df( + graphs_nx_df, + graph_emb_np=feather_par_emb, + emb_graph_id_to_idx_dict=emb_graph_id_to_idx_dict, + meta=self.meta, + model=self.model, + device=self.device, + ctx=self.ctx, + n_samples_per_batch=n_samples_per_batch, + num_samples=num_samples, + max_new_tokens=max_new_tokens, + temperature=temperature, + top_k=top_k, + token_seq_col='token_seq_round_d2', + normalize_weights_flag=False, + emb_dtype=dtype_str_to_torch_dict[emb_dtype], + ) + + return gc_df + + def eval_circ_df_cudaq(self, qaoa_gpt_circ_df, adapt_gpt_path='.'): + qaoa_gpt_circ_eval_df = eval_adapt_gpt_circ_cudaq( + qaoa_gpt_circ_df, + n_nodes=self.n_nodes, + temp_folder=self.temp_folder, + pool_type=self.pool_type) + + output_columns_list = [ + "graph_prefix", "graph", "n_edges", "q_circuits", + "adapt_gpt_energies" + ] + + if "true_energy" in qaoa_gpt_circ_df.columns: + output_columns_list.append("true_energy") + + if "energy_gurobi" in qaoa_gpt_circ_df.columns: + output_columns_list.append("energy_gurobi") + + return qaoa_gpt_circ_eval_df[output_columns_list] diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/prepare_tokens.py b/docs/sphinx/applications/python/qaoa_gpt_src/prepare_tokens.py new file mode 100644 index 00000000000..8e4b2831c7f --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/prepare_tokens.py @@ -0,0 +1,764 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025. # +# ============================================================================= # + +#from karateclub.feathergraph import FeatherGraph +from qaoa_gpt_src.custom_feather import CustomFeatherGraph as FeatherGraph +import pandas as pd +from pathlib import Path +from tqdm import tqdm +import json +import networkx as nx +import numpy as np +from collections import Counter +import random +import argparse +from joblib import Parallel, delayed +from itertools import islice +from networkx import convert_node_labels_to_integers +import ast + +tqdm.pandas() + + +def open_df_from_res_csv(fname): + #df_list = [] + try: + cur_df = pd.read_csv(fname) + cur_df['worker_id'] = fname.stem + #df_list.append(cur_df) + except Exception as e: + print(f'{e} (file: {fname})') + cur_df = None + return cur_df + + +def generate_tokens(results_fpath_str: str, + save_path_str: str, + config_path_temp: str, + n_nodes: int, + rounding_digits=2, + min_block_size=128, + max_block_size=256, + val_frac=0.1, + test_frac=0.1, + max_abs_param_val=10, + perform_coef_mod_range=True, + apply_sliding_window=True, + n_workers=1, + skip_only_qaoa_circ=False, + allowed_graph_generators_list=['all'], + approx_ratio_thr=0.97, + debug_limit=0, + target_val_size=10, + verbose=True): + """ + Args: + results_fpath_str. Path to the directory with ADAPT-QAOA results. Should contain 'res' and 'graphs' subdirectories. + save_path_str. Path to the directory where the generated tokens will be saved. + config_path_temp: path to the template config file for training the model. + n_nodes. Number of nodes in the graphs. + rounding_digits. Number of digits to round the coefficients to. + min_block_size. Minimum size of the sliding window for tokenization (min sequence length in sliding window). + max_block_size. Maximum size of the sliding window for tokenization (nanoGPT block size). + val_frac. Fraction of the data to be used for validation. + test_frac. Fraction of the data to be used for testing. + max_abs_param_val. Maximum absolute value of the coefficients (gamma and beta params). + perform_coef_mod_range. Whether to perform coefficient modulation to the range [-max_abs_param_val, max_abs_param_val]. + (Wrap beta to [0; pi] range; true (default)) + apply_sliding_window. Whether to apply sliding window for tokenization. (Apply sliding window to generate training samples) + n_workers. Number of workers to use for parallel processing. + skip_only_qaoa_circ. Whether to skip circuits that only use QAOA mixer (sum_i X(i)). + allowed_graph_generators_list. List of allowed graph generators to filter the results. + Default: all. Should be separated with ; . Allowed values: erdos_renyi;barabasi_albert;watts_strogatz;random_regular;bipartite + approx_ratio_thr. Threshold for the approximation ratio to filter the results. + debug_limit. Limit the number of results to process for debugging purposes. + tragtet_val_size. control the target size of the validation set, + regardless of the overall dataset size or class distribution. + verbose. Whether to print verbose output. + + Returns: + None. The function saves the generated tokens and metadata to the specified directory. + + """ + + results_fpath_list = [Path(el) for el in results_fpath_str.split(';')] + save_path = Path(save_path_str) + + for results_fpath in results_fpath_list: + assert results_fpath.exists() and results_fpath.is_dir( + ), "Results path is invalid." + + if verbose: + print(f"Results paths: {results_fpath_list}") + + ########################################## + ## ADAPT-QAOA results + ########################################### + if verbose: + print("Opening ADAPT-QAOA results...") + df_list = [] + df_list_all = [] + + for cur_dataset_res_path in results_fpath_list: + cur_dataset_res_flist = sorted( + cur_dataset_res_path.joinpath('res').glob('*.csv')) + if debug_limit: + cur_dataset_res_flist = cur_dataset_res_flist[:debug_limit] + # for fname in tqdm(cur_dataset_res_flist, desc='Opening ADAPT results'): + # try: + # cur_df = pd.read_csv(fname) + # cur_df['worker_id'] = fname.stem + # df_list.append(cur_df) + # except Exception as e: + # print(f'{e} (file: {fname})') + df_list = Parallel(n_jobs=n_workers)( + delayed(open_df_from_res_csv)(fname) for fname in tqdm( + cur_dataset_res_flist, + desc=f'Opening ADAPT results ({cur_dataset_res_path.stem})')) + df_list_all += df_list + df_list = [df for df in df_list_all if df is not None] + + if verbose: + print("df_list len:", len(df_list)) + + full_run_df = pd.concat(df_list) + full_run_df['prefix'] = full_run_df['worker_id'].apply(lambda x: x[:-15]) + + #################################### + ## Graphs + #################################### + if verbose: + print("Opening graphs results...") + df_list = [] + df_list_all = [] + for cur_dataset_res_path in results_fpath_list: + cur_dataset_res_flist = sorted( + cur_dataset_res_path.joinpath('graphs').glob('*.csv')) + if debug_limit: + cur_dataset_res_flist = cur_dataset_res_flist[:debug_limit] + # for fname in tqdm(cur_dataset_res_flist, desc='Opening graphs'): + # cur_df = pd.read_csv(fname) + # cur_df['worker_id'] = fname.stem + # df_list.append(cur_df) + df_list = Parallel(n_jobs=n_workers)( + delayed(open_df_from_res_csv)(fname) for fname in tqdm( + cur_dataset_res_flist, + desc=f'Opening graphs ({cur_dataset_res_path.stem})')) + df_list_all += df_list + + for df in df_list: + if df is not None: + if 'g_method' not in df.columns: + #print("Graphs were generated with older version of ADAPT-GPT preprocessor. Most likely, they are ER.") + df['g_method'] = "erdos_renyi" + df_list = [df for df in df_list_all if df is not None] + if verbose: + print("df_list len:", len(df_list)) + + full_run_graphs_df = pd.concat(df_list) + full_run_graphs_df['edgelist_list'] = ( + full_run_graphs_df['edgelist_json'].progress_apply( + lambda x: json.loads(x))) + full_run_graphs_df['edgelist_list_len'] = ( + full_run_graphs_df['edgelist_list'].progress_apply(lambda x: len(x))) + full_run_graphs_df['num_connected_comp'] = full_run_graphs_df[ + 'edgelist_list'].progress_apply(lambda x: len( + list(nx.connected_components(nx.Graph([edge[:2] for edge in x]))))) + full_run_graphs_df['prefix'] = full_run_graphs_df['worker_id'].apply( + lambda x: x[:-14]) + if verbose: + print("Graphs count:") + if verbose: + print(full_run_graphs_df['g_method'].value_counts()) + + ################################################### + ## Merge adapt-qaoa results and graphs results + ################################################### + if verbose: + print("Merging ADAPT-QAOA results and graphs results...") + + if verbose: + print(f"Number of rows in full_run_df: {len(full_run_df)}") + print("Columns in full_run_df:", full_run_df.columns) + print("Sample rows from full_run_df:") + print(full_run_df.head()) + + combined_res_df = pd.merge(left=full_run_df, + right=full_run_graphs_df, + left_on=['prefix', 'graph_num'], + right_on=['prefix', 'graph_num']) + + if verbose: + print("Unique prefixes in full_run_df:", full_run_df['prefix'].unique()) + print("Unique prefixes in full_run_graphs_df:", + full_run_graphs_df['prefix'].unique()) + + print("Unique graph_num in full_run_df:", + full_run_df['graph_num'].unique()) + print("Unique graph_num in full_run_graphs_df:", + full_run_graphs_df['graph_num'].unique()) + + ############################################### + # Add derived columns + ############################################### + + # convert energy_list from string to list + combined_res_df["energy_list"] = combined_res_df["energy_list"].apply( + lambda x: ast.literal_eval(x) if isinstance(x, str) else x) + + combined_res_df["n_layers"] = combined_res_df["energy_list"].apply(len) + + combined_res_df['graph_id'] = (combined_res_df['prefix'] + '_^_' + + combined_res_df['graph_num'].astype(str)) + + # convert mixer_pool_index from string to list of ints + combined_res_df['mixer_pool_index'] = combined_res_df[ + 'mixer_pool_index'].apply( + lambda x: [int(i) for i in ast.literal_eval(x)] + if isinstance(x, str) else x) + + combined_res_df['only_qaoa_circ'] = combined_res_df[ + 'mixer_pool_index'].progress_apply( + lambda x: all(e == n_nodes + 1 for e in x)) + + # Filter by allowed graph generators if needed + if allowed_graph_generators_list != ['all']: + if verbose: + print( + f"Filtering graphs based on allowed generators: {allowed_graph_generators_list}" + ) + if verbose: + print(f"N circuits before: {len(combined_res_df)}") + combined_res_df = combined_res_df[combined_res_df['g_method'].isin( + allowed_graph_generators_list)] + if verbose: + print(f"N circuits after: {len(combined_res_df)}") + + if verbose: + print(combined_res_df['g_method'].value_counts()) + + ############################################ + # Graph embedding + ############################################ + if verbose: + print("Applying FEATHER graph embedding...") + + combined_unique_graphs_df = (combined_res_df[['graph_id', 'edgelist_json' + ]].drop_duplicates()) + + def create_weighted_graph_nx(w_elist): + G = nx.Graph() + G.add_weighted_edges_from(w_elist) + return G + + combined_unique_graphs_df['edgelist_py_list'] = combined_unique_graphs_df[ + 'edgelist_json'].progress_apply(lambda x: [(e[0] - 1, e[1] - 1, e[2]) + for e in json.loads(x) + #(e[0]-1, e[1]-1) for e in x + ]) + + #combined_unique_graphs_df['graph_nx'] = ( + # combined_unique_graphs_df['edgelist_py_list'] + # .progress_apply(lambda x: create_weighted_graph_nx(x)) + #) + + combined_unique_graphs_df['graph_nx'] = ( + combined_unique_graphs_df['edgelist_py_list'].progress_apply( + lambda x: convert_node_labels_to_integers( + create_weighted_graph_nx(x)))) + + combined_unique_graphs_w_idx_df = combined_unique_graphs_df.set_index( + 'graph_id') + graphs_nx_dict = combined_unique_graphs_w_idx_df['graph_nx'].to_dict() + graphs_nx_filt_dict = dict([(name, g) + for name, g in tqdm(graphs_nx_dict.items()) + if g.number_of_nodes() == n_nodes]) + graphs_nx_filt_names = list(graphs_nx_filt_dict.keys()) + graphs_nx_filt_list = list(graphs_nx_filt_dict.values()) + + emb_graph_idx_to_id_dict = { + k: v for k, v in enumerate(graphs_nx_filt_names) + } + emb_graph_id_to_idx_dict = { + v: k for k, v in enumerate(graphs_nx_filt_names) + } + + #def get_feather_emb(g_list): + # feather_model = FeatherGraph() + # feather_model.fit(graphs=g_list) + # return feather_model.get_embedding() + def get_feather_emb(g_list): + # Using our custom wrapper with the original FEATHER implementation + feather_model = FeatherGraph(order=5, + eval_points=25, + theta_max=2.5, + seed=42, + pooling="mean") + feather_model.fit(graphs=g_list) + return feather_model.get_embedding() + + def split_list(lst, n): + it = iter(lst) + return [ + list(islice(it, i)) for i in + [len(lst) // n + (1 if x < len(lst) % n else 0) for x in range(n)] + ] + + def embed_nx_w_feather_parallel(graphs_list, n_workers=2): + graphs_chunked_list = split_list(graphs_list, n_workers) + + #graphs_chunked_list=[graphs_list] + + emb_np_list = Parallel(n_jobs=n_workers)( + delayed(get_feather_emb)(g_chunk) + for g_chunk in graphs_chunked_list) + + return np.vstack(emb_np_list) + + feather_par_emb = embed_nx_w_feather_parallel(graphs_nx_filt_list[:], + n_workers=n_workers) + feather_par_emb = feather_par_emb.round(rounding_digits) + #print(f"Graph embedding shape: {feather_par_emb.shape} (n_graphs × dimension)") + #print(f"Graph embedding dimension: {feather_par_emb.shape[1]}") + + combined_res_df['has_emb'] = combined_res_df['graph_id'].apply( + lambda x: True if x in emb_graph_id_to_idx_dict else False) + + ####################################### + # Filtering + ####################################### + + if verbose: + print("Filtering results...") + if verbose: + print( + f"Number of rows in combined_res_df before filtering: {len(combined_res_df)}" + ) + + # Convert gamma_coef from string to list of floats + combined_res_df['gamma_coef'] = combined_res_df['gamma_coef'].apply( + lambda x: [float(coef) for coef in ast.literal_eval(x)] + if isinstance(x, str) else x) + + # convert approx_ratio from string to float + combined_res_df['approx_ratio'] = pd.to_numeric( + combined_res_df['approx_ratio'], errors='coerce') + + combined_res_filt_df = combined_res_df[ + # ( + # combined_res_df['beta_coef'].apply( + # lambda x: all([abs(coef) < max_abs_param_val for coef in x]) + # ) + # ) + # & + (combined_res_df['gamma_coef'].apply( + lambda x: all([abs(coef) < max_abs_param_val for coef in x]))) & + (combined_res_df['approx_ratio'] > approx_ratio_thr)].copy() + + if skip_only_qaoa_circ: + if verbose: + print("Filtering out only QAOA circuits...") + n_only_qaoa_circ = combined_res_filt_df['only_qaoa_circ'].sum() + if verbose: + print( + f"Removing {n_only_qaoa_circ} out of total {len(combined_res_filt_df)}" + ) + combined_res_filt_df = combined_res_filt_df[ + combined_res_filt_df['only_qaoa_circ'] == False] + + # Convert beta_coef and gamma_coef from strings to lists of floats + + combined_res_filt_df['beta_coef'] = combined_res_filt_df['beta_coef'].apply( + lambda x: [float(coef) for coef in ast.literal_eval(x)] + if isinstance(x, str) else x) + + combined_res_filt_df['gamma_coef'] = combined_res_filt_df[ + 'gamma_coef'].apply( + lambda x: [float(coef) for coef in ast.literal_eval(x)] + if isinstance(x, str) else x) + + combined_res_filt_df['mixer_pool_index'] = combined_res_filt_df[ + 'mixer_pool_index'].apply( + lambda x: [int(idx) for idx in ast.literal_eval(x)] + if isinstance(x, str) else x) + + if verbose: + print( + f"Number of rows in combined_res_filt_df after filtering: {len(combined_res_filt_df)}" + ) + + ######################################## + # Tokenization + ######################################## + + if verbose: + print("Tokenizing...") + tokens_list = [] + + ## Special symbols + special_symbols_list = ['pad', 'bos', 'eos', 'new_layer_p', 'end_of_graph'] + tokens_list += special_symbols_list + + ## Edges + all_edges_list = [] + for g in combined_res_filt_df['edgelist_list']: + for e in g: + all_edges_list.append(tuple(e[:2])) + all_edges_set = set(all_edges_list) + + if verbose: + print(f"\tTotal tokens for edges: {len(all_edges_set)}") + tokens_list += list(all_edges_set) + + ## Coeffs + + n_steps = int((max_abs_param_val * 2 / (10**-rounding_digits)) + 1) + + all_coefs_round_set = set([ + round(coef, rounding_digits) + for coef in np.linspace(start=-max_abs_param_val, + stop=max_abs_param_val, + num=n_steps).tolist() + ]) + len(all_coefs_round_set) + tokens_list += list(all_coefs_round_set) + + if verbose: + print(f"\tTotal tokens for coefs: {len(all_coefs_round_set)}") + + ## Operator pool + + ops_list = [] + for l in combined_res_filt_df['mixer_pool_index']: + + ops_list += l + + ops_list = list(set(ops_list)) + if verbose: + print(f"\tTotal tokens for operator pool: {len(ops_list)}") + tokens_list += ops_list + + ###################################### + ## Tokenization + ####################################### + + int_idx_to_token_dict = dict(enumerate(tokens_list)) + token_to_int_idx_dict = {v: k for k, v in int_idx_to_token_dict.items()} + + vocab_size = len(int_idx_to_token_dict) + if verbose: + print(f"\tTotal tokens in vocab: {vocab_size}") + """ + We know that beta coefficients can't exceed pi in QAOA formulation, + but when we were using ADAPT.jl it happens occasionally due to the optimization process. + Since our GPT model requires that all numerical values have distinct tokens, + we are limited to what value range we can represent, so we apply this function to effectively + return beta values into their canonical range. Beta values are pi-periodical, + so if a beta value exceeds the range, we simply return it back to pi-range. + We also experimented with gamma parameters, but it turned out to be more complicated, + and we did not get consistent results, so we don't do gamma modulation at this time. + + """ + + def arth_mod(a, b): + result = a % b + return result if a >= 0 else result - b + + def tokenize_row(row, coef_mod=True): + + tokens_seq_list = ['bos'] + + for edge in row['edgelist_list']: + edge_tuple = tuple(edge[:2]) + edge_weight = edge[2] + tokens_seq_list.append(edge_tuple) + tokens_seq_list.append(edge_weight) + + tokens_seq_list.append('end_of_graph') + + for p in range(row['n_layers']): + tokens_seq_list.append('new_layer_p') + tokens_seq_list.append(row['mixer_pool_index'][p]) + + cur_beta = row['beta_coef'][p] + + if coef_mod: + # cur_beta = cur_beta % (np.pi) + cur_beta = arth_mod(cur_beta, np.pi) + if cur_beta > -max_abs_param_val and cur_beta < max_abs_param_val: + cur_beta_round = round(cur_beta, rounding_digits) + tokens_seq_list.append(cur_beta_round) + else: + return None + + cur_gamma = row['gamma_coef'][p] + + # if coef_mod: + # cur_gamma = cur_gamma % (2*np.pi) + if cur_gamma > -max_abs_param_val and cur_gamma < max_abs_param_val: + cur_gamma_round = round(cur_gamma, rounding_digits) + tokens_seq_list.append(cur_gamma_round) + else: + return None + + tokens_seq_list.append('eos') + + return tokens_seq_list + + combined_res_filt_df[ + f'token_seq_round_d{rounding_digits}'] = combined_res_filt_df.progress_apply( + lambda x: tokenize_row(x, coef_mod=perform_coef_mod_range), + axis=1, + ) + + combined_res_tok_df = combined_res_filt_df.dropna() + combined_res_tok_df[f'token_int_seq_round_d{rounding_digits}'] = ( + combined_res_tok_df[f'token_seq_round_d{rounding_digits}']. + progress_apply(lambda x: [token_to_int_idx_dict[token] for token in x])) + + ######################################################## + # Generating training split for nanoGPT + ######################################################## + if verbose: + print("Generating training, validation and test splits...") + + n = len(combined_res_tok_df) + + combined_res_tok_shf_df = (combined_res_tok_df.sample(frac=1).reset_index( + drop=True)) + + if verbose: + print(f"combined_res_df shape: {combined_res_df.shape}") + print(f"combined_res_tok_df shape: {combined_res_tok_df.shape}") + print(f"combined_res_tok_shf_df shape: {combined_res_tok_shf_df.shape}") + + graph_ids = combined_res_tok_shf_df['graph_id'].drop_duplicates().to_list() + + # Compute the number of graphs for each set + n_total = len(graph_ids) + n_test = int(n_total * + test_frac) # Define test_frac for the size of the test set + n_val = int(n_total * val_frac) # val_frac defines the validation set size + n_train = n_total - n_test - n_val # Remaining will be the training set + + # Split into train, val, and test sets + train_graph_ids_set = set(graph_ids[:n_train]) + val_graph_ids_set = set(graph_ids[n_train:n_train + n_val]) + test_graph_ids_set = set(graph_ids[n_train + n_val:]) + + assert len(train_graph_ids_set.intersection(val_graph_ids_set)) == 0 + assert len(train_graph_ids_set.intersection(test_graph_ids_set)) == 0 + assert len(val_graph_ids_set.intersection(test_graph_ids_set)) == 0 + + def pad_with_zeros(seq, target_len): + pad_len = target_len - len(seq) + if pad_len > 0: + padded_seq = seq + [0] * pad_len + else: + padded_seq = seq + + if len(padded_seq) != max_block_size: + if verbose: + print(f"padded_seq len: {len(padded_seq)}") + return padded_seq + + def sliding_window(numbers, min_block_size, max_block_size): + + if min_block_size != max_block_size: + block_size = random.randint(min_block_size, max_block_size) + else: + block_size = min_block_size + + if block_size >= len(numbers): + window = numbers[:-1] + window_shifted = numbers[1:] + return [[ + pad_with_zeros(window, target_len=max_block_size), + pad_with_zeros(window_shifted, target_len=max_block_size) + ]] + + result_xy_list = [] + result = [] + for i in range(0, len(numbers) - block_size + 1): + window = numbers[i:i + block_size] + result.append(window) + + for x, y in zip(result, result[1:]): + result_xy_list.append([ + pad_with_zeros(x, target_len=max_block_size), + pad_with_zeros(y, target_len=max_block_size) + ]) + + return result_xy_list + + # Assign the 'label' column based on the split + combined_res_tok_shf_df['label'] = 'train' + combined_res_tok_shf_df.loc[ + combined_res_tok_shf_df['graph_id'].isin(val_graph_ids_set), + 'label'] = 'val' + combined_res_tok_shf_df.loc[ + combined_res_tok_shf_df['graph_id'].isin(test_graph_ids_set), + 'label'] = 'test' + + if apply_sliding_window: + print('Applying sliding window...') + combined_res_tok_shf_df[ + f'token_int_seq_round_d{rounding_digits}_sw'] = combined_res_tok_shf_df[ + f'token_int_seq_round_d{rounding_digits}'].progress_apply( + lambda x: sliding_window(x, + min_block_size=min_block_size, + max_block_size=max_block_size)) + + train_data = combined_res_tok_shf_df[combined_res_tok_shf_df['label'] == + 'train'] + val_data = combined_res_tok_shf_df[combined_res_tok_shf_df['label'] == + 'val'] + test_data = combined_res_tok_shf_df[combined_res_tok_shf_df['label'] == + 'test'] + + print( + f"\tNumber of training samples: {len(train_data)}, val samples: {len(val_data)}, test samples: {len(test_data)}" + ) + + if apply_sliding_window: + + train_data_conc = [] + train_data_graph_idx_list = [] + for cur_graph_id, l in zip( + train_data['graph_id'], + train_data[f'token_int_seq_round_d{rounding_digits}_sw']): + if cur_graph_id in emb_graph_id_to_idx_dict: + train_data_conc += l + train_data_graph_idx_list += [ + emb_graph_id_to_idx_dict[cur_graph_id] + ] * len(l) + train_data_conc_np = np.array(train_data_conc, dtype=np.uint16) + + val_data_conc = [] + val_data_graph_idx_list = [] + for cur_graph_id, l in zip( + val_data['graph_id'], + val_data[f'token_int_seq_round_d{rounding_digits}_sw']): + if cur_graph_id in emb_graph_id_to_idx_dict: + val_data_conc += l + val_data_graph_idx_list += [ + emb_graph_id_to_idx_dict[cur_graph_id] + ] * len(l) + val_data_conc_np = np.array(val_data_conc, dtype=np.uint16) + + test_data_conc = [] + test_data_graph_idx_list = [] + for cur_graph_id, l in zip( + test_data['graph_id'], + test_data[f'token_int_seq_round_d{rounding_digits}_sw']): + if cur_graph_id in emb_graph_id_to_idx_dict: + test_data_conc += l + test_data_graph_idx_list += [ + emb_graph_id_to_idx_dict[cur_graph_id] + ] * len(l) + test_data_conc_np = np.array(test_data_conc, dtype=np.uint16) + + #print(f"\tTrain has {len(train_data_conc_np):,} samples") + #print(f"\tVal has {len(val_data_conc_np):,} samples") + #print(f"\tTest has {len(test_data_conc_np):,} samples") + + ####################################### + # Saving + ####################################### + if verbose: + print("Saving data...") + + save_path.mkdir(parents=True, exist_ok=True) + + if apply_sliding_window: + + np.save(save_path.joinpath('train.npy'), train_data_conc_np) + np.save(save_path.joinpath('val.npy'), val_data_conc_np) + np.save(save_path.joinpath('test.npy'), test_data_conc_np) + + combined_res_df.to_pickle(save_path.joinpath('combined_res_df.pkl')) + + combined_res_tok_shf_df.to_pickle( + save_path.joinpath('combined_res_tok_shf_df.pkl')) + + sample_size_per_w_bucket = int( + target_val_size / + len(combined_res_df['edgelist_list_len'].drop_duplicates())) + ''' + val_data_sampled = ( + val_data[ + (~val_data['token_seq_round_d2'].isna()) + ] + .groupby('edgelist_list_len', group_keys=False).apply( + lambda x: x.sample(sample_size_per_w_bucket) if len(x) > sample_size_per_w_bucket else x + ) + .reset_index(drop=True) + ) + ''' + + val_data_sampled = (val_data[( + ~val_data[f'token_seq_round_d{rounding_digits}'].isna())].groupby( + 'edgelist_list_len', group_keys=False).filter(lambda group: len( + group) > sample_size_per_w_bucket).reset_index(drop=True)) + + #print(val_data_sampled.columns) + #print('Checking val_data_sampled...') + #print(val_data_sampled.groupby('edgelist_list_len').groups.keys()) + #print(val_data_sampled.groupby('edgelist_list_len').apply(lambda group: group.drop(columns=['edgelist_list_len']).sample(sample_size_per_w_bucket))) + + val_data_sampled.to_pickle( + save_path.joinpath('combined_res_tok_shf_val_df.pkl')) + + emb_size = feather_par_emb.shape[1] + np.save(save_path.joinpath(f'feather_emb_d{emb_size}.npy'), feather_par_emb) + + meta = { + 'vocab_size': vocab_size, + 'itos': int_idx_to_token_dict, + 'stoi': token_to_int_idx_dict, + 'train_data_graph_idx_list': train_data_graph_idx_list, + 'val_data_graph_idx_list': val_data_graph_idx_list, + 'test_data_graph_idx_list': test_data_graph_idx_list, + 'emb_graph_id_to_idx_dict': emb_graph_id_to_idx_dict, + 'emb_graph_idx_to_id_dict': emb_graph_idx_to_id_dict, + } + + pd.to_pickle(meta, save_path.joinpath('meta.pkl')) + + with open(f'{config_path_temp}/train_adapt_gpt_config_template.py') as f: + config_template_str = f.read() + + pool_type = combined_res_df['pool_type'].iloc[0] + + dataset_name = save_path.stem + config_to_save_str = config_template_str.format( + out_dir=f'out-{dataset_name}', + dataset=dataset_name, + block_size=max_block_size, + use_graph_emb="True", + pool_type=pool_type, + n_nodes=n_nodes, + token_seq_round=f'token_seq_round_d{rounding_digits}', + ) + + with open(save_path.joinpath('train_adapt_gpt_config.py'), 'w') as f: + f.write(config_to_save_str) + + if verbose: + print(f"Data is saved to: {str(save_path.absolute())}") + if verbose: + print("Done!") + + return diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/train_adapt_gpt_config_template.py b/docs/sphinx/applications/python/qaoa_gpt_src/train_adapt_gpt_config_template.py new file mode 100644 index 00000000000..862b6ca8235 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/train_adapt_gpt_config_template.py @@ -0,0 +1,42 @@ +# Train an ADAPT-GPT model +# Based on https://github.com/karpathy/nanoGPT/blob/master/config/train_shakespeare_char.py + +out_dir = '{out_dir}' +eval_interval = 250 # keep frequent because we'll overfit. Determines how often standard loss evaluation occurs +eval_iters = 200 #Determines how many batches are used to calculate validation and training loss during model evaluation +log_interval = 10 # don't print too too often +eval_ar_every = 5000 # how often we do approx ratio evaluation (calling ADAPT-QAOA cudaq). Controls how often the model performs domain-specific evaluation using approximation ratio + +# we expect to overfit on this small dataset, so only save when val improves +always_save_checkpoint = False + +dataset = '{dataset}' +gradient_accumulation_steps = 1 +batch_size = 64 +block_size = {block_size} # context of up to 256 previous characters + +# baby GPT model :) +n_layer = 6 +n_head = 6 +n_embd = 384 +dropout = 0.2 + +learning_rate = 1e-4 # with baby networks can afford to go a bit higher +n_epochs = 70 +max_iters = 30000 +lr_decay_iters = 30000 # make equal to max_iters usually +min_lr = 1e-5 # learning_rate / 10 usually +beta2 = 0.95 # make a bit bigger because number of tokens per iter is small + +warmup_iters = 100 # not super necessary potentially + +graph_emb_dim = 500 # default for FEATHER graph +use_graph_emb = {use_graph_emb} +pool_type = '{pool_type}' +n_nodes = {n_nodes} +token_seq_round = '{token_seq_round}' # rounding digits for token sequence +n_samples = 5 + +# on macbook also add +# device = 'cpu' # run on cpu only +# compile = False # do not torch compile the model diff --git a/docs/sphinx/applications/python/qaoa_gpt_src/util.py b/docs/sphinx/applications/python/qaoa_gpt_src/util.py new file mode 100644 index 00000000000..8fc7ed3aab8 --- /dev/null +++ b/docs/sphinx/applications/python/qaoa_gpt_src/util.py @@ -0,0 +1,758 @@ +#============================================================================== # +# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# The QAOA-GPT implementation in CUDA-Q is based on this paper: # +# https://arxiv.org/pdf/2504.16350 # +# Usage or reference of this code or algorithms requires citation of the paper: # +# Ilya Tyagin, Marwa Farag, Kyle Sherbert, Karunya Shirali, Yuri Alexeev, # +# Ilya Safro "QAOA-GPT: Efficient Generation of Adaptive and Regular Quantum # +# Approximate Optimization Algorithm Circuits", IEEE International Conference # +# on Quantum Computing and Engineering (QCE), 2025 # +# ============================================================================= # + +from pathlib import Path +import subprocess +import sys +from datetime import datetime +import pandas as pd +import torch +from tqdm import tqdm +from collections import defaultdict +import networkx as nx +import numpy as np +from itertools import islice + +from gurobipy import Model, GRB +import gurobipy as gb + +from qaoa_gpt_src.custom_feather import CustomFeatherGraph as FeatherGraph + +import json +from joblib import Parallel, delayed + +import cudaq +from hamiltonian_graph import term_coefficients, term_words, max_cut_ham +from adapt_qaoa_pool import all_pool, qaoa_mixer, qaoa_single_x, qaoa_double + +# Set target +#cudaq.set_target("nvidia") # Set the target to CUDAQ +cudaq.set_target("nvidia", option="fp64") + + +##################################################### +def extract_graph(token_seq): + graph_seq = [] + + for idx, tok in enumerate(token_seq): + graph_seq.append(tok) + if tok == 'end_of_graph': + break + adapt_seq = token_seq[idx + 1:-1] + return graph_seq, adapt_seq + + +###################################################### +def circ_sanity_check(cur_q_circ): + + lr_sep_list = cur_q_circ[0::4] + op_idx_list = cur_q_circ[1::4] + + num_vals = cur_q_circ[2::4] + cur_q_circ[3::4] + + if any([type(el) != int for el in op_idx_list]): + #print('wrong op_idx_list') + return False + + if any([type(el) != str for el in lr_sep_list]): + #print('wrong lr_sep_list') + return False + + if len(cur_q_circ) % 4: + #print('Wrong length') + return False + + return True + + +############################################################ + + +def generate_circ_from_df( + test_run_df, + graph_emb_np, # for models with graph emb + emb_graph_id_to_idx_dict, # for models with graph emb + meta, + model, + device, + ctx, + n_samples_per_batch, + num_samples=5, # number of samples to draw + max_new_tokens=200, # number of tokens generated in each sample + temperature=0.1, # 1.0 = no change, < 1.0 = less random, > 1.0 = more random, in predictions + top_k=200, # retain only the top_k most likely tokens, clamp others to have 0 probability + token_seq_col='token_seq_round_d2', + normalize_weights_flag=False, + emb_dtype=torch.bfloat16, +): + # Batched inference based on number of edges. + # We group graphs with the same number of edges together + # such that we can merge them into a tensor to keep the input length size consistent. + + if graph_emb_np is not None and emb_graph_id_to_idx_dict is not None: + gemb_flag = True + else: + gemb_flag = False + + stoi, itos = meta['stoi'], meta['itos'] + encode = lambda s: [stoi[c] for c in s] + decode = lambda l: [itos[i] for i in l] + + n_edges_to_count_dict = test_run_df['edgelist_list_len'].value_counts( + ).to_dict() + + adapt_gpt_out_list_dict = defaultdict(list) + x_list_dict = defaultdict(list) + graph_emb_dict = defaultdict(list) + y_dict = dict() + + pbar = tqdm(n_edges_to_count_dict.items()) + + for n_edges, n_graphs in pbar: + pbar.set_description( + f"Inference. Current batch: n_edges: {n_edges}, n_graphs: {n_graphs}" + ) + cur_test_run_df = test_run_df[test_run_df['edgelist_list_len'] == + n_edges] + + for row_idx, graph_df_row in cur_test_run_df.iterrows(): + #graph_df_row = test_df.loc[graph_idx] + start, adapt_seq = extract_graph(graph_df_row[token_seq_col]) + start_ids = encode(start) + x = (torch.tensor(start_ids, dtype=torch.long, device=device)[None, + ...]) + x_list_dict[n_edges].append(x) + + if gemb_flag: + cur_graph_idx = emb_graph_id_to_idx_dict[ + graph_df_row['graph_id']] + graph_emb_dict[n_edges].append( + torch.tensor(graph_emb_np[cur_graph_idx], + dtype=emb_dtype, + device=device)) + + adapt_gpt_out_dict = dict() + adapt_gpt_out_dict['graph'] = start[1:-1] + adapt_gpt_out_dict['n_edges'] = graph_df_row['edgelist_list_len'] + adapt_gpt_out_dict['q_circuits'] = [] + adapt_gpt_out_dict['adapt_circuit'] = adapt_seq + adapt_gpt_out_dict['adapt_full_ar'] = graph_df_row['approx_ratio'] + adapt_gpt_out_dict['graph_prefix'] = graph_df_row['graph_id'] + #adapt_gpt_out_dict['true_energy'] = graph_df_row['true_energy'] + if 'true_energy' in graph_df_row: + adapt_gpt_out_dict['true_energy'] = graph_df_row['true_energy'] + if 'energy_gurobi' in graph_df_row: + adapt_gpt_out_dict['energy_gurobi'] = graph_df_row[ + 'energy_gurobi'] + adapt_gpt_out_dict['label'] = graph_df_row['label'] + adapt_gpt_out_list_dict[n_edges].append(adapt_gpt_out_dict) + + cur_batch_torch = torch.vstack(x_list_dict[n_edges]) + + if gemb_flag: + cur_emb_batch_torch = torch.vstack(graph_emb_dict[n_edges]) + + # Calculate total samples and number of mini-batches + total_samples = cur_batch_torch.size(0) + n_batches = (total_samples + n_samples_per_batch - + 1) // n_samples_per_batch # Ensure ceiling division + + # Initialize an empty list for results + y_list = [] + + # Run inference in mini-batches + with torch.no_grad(): + for i in tqdm(range(n_batches), + desc='Internal batch progress', + disable=True): + start_idx = i * n_samples_per_batch + end_idx = min((i + 1) * n_samples_per_batch, total_samples) + + mini_batch = cur_batch_torch[start_idx:end_idx] + mini_batch_repeated = mini_batch.repeat( + num_samples, 1) # Repeat the mini-batch for num_samples + + if gemb_flag: + mini_emb_batch = cur_emb_batch_torch[start_idx:end_idx] + mini_emb_batch_repeated = mini_emb_batch.repeat( + num_samples, 1) # Repeat the mini-batch for num_samples + + with ctx: + if gemb_flag: + y = model.generate(mini_batch_repeated, + mini_emb_batch_repeated, + max_new_tokens, + temperature=temperature, + top_k=top_k) + else: + y = model.generate( + mini_batch_repeated, + #mini_emb_batch_repeated, + max_new_tokens, + temperature=temperature, + top_k=top_k) + + # Collect results from each mini-batch + y_list.append(y.detach().cpu()) + + # Concatenate results from all mini-batches + y_dict[n_edges] = torch.cat(y_list, dim=0) + + ### trimming the records (removing garbage after EOS) + for n_edges, cur_adapt_gpt_out_list in adapt_gpt_out_list_dict.items(): + cur_full_y_tensor = y_dict[n_edges] + + for graph_idx in range(len(cur_adapt_gpt_out_list)): + + cur_y_tensor = cur_full_y_tensor[ + graph_idx::len(cur_adapt_gpt_out_list)] + + for k in range(num_samples): + cur_gen_result = decode(cur_y_tensor[k].tolist()) + cur_circ = [] + circ_flag = 0 + for idx, tok in enumerate(cur_gen_result): + if tok == 'end_of_graph': + circ_flag = 1 + if circ_flag: + cur_circ.append(tok) + if tok == 'eos': + break + cur_adapt_gpt_out_list[graph_idx]['q_circuits'].append( + cur_circ[1:-1]) + + ### flattening the circ list + adapt_gpt_test_samples_list = [] + for n_edges, cur_adapt_gpt_out_list in adapt_gpt_out_list_dict.items(): + adapt_gpt_test_samples_list += cur_adapt_gpt_out_list + + for idx in range(len(adapt_gpt_test_samples_list)): + q_circ_filt_list = [] + for circ in adapt_gpt_test_samples_list[idx]['q_circuits']: + filt_flag = circ_sanity_check(circ) + # if not filt_flag: + # #print(adapt_gpt_test_samples_list[idx]['graph_prefix'], '\n') + # pass + # else: + # q_circ_filt_list.append(circ) + q_circ_filt_list.append(circ) + adapt_gpt_test_samples_list[idx]['q_circuits'] = q_circ_filt_list + + adapt_gpt_test_samples_list[idx]['q_circuits'] = q_circ_filt_list + + for gr_dict in adapt_gpt_test_samples_list: + graph_py_list = [] + + graph_edges_list = gr_dict['graph'][::2] + graph_weights_list = gr_dict['graph'][1::2] + + if normalize_weights_flag: + graph_w_norm = sum(graph_weights_list) + else: + graph_w_norm = 1.0 + + for edge_idx, edge in enumerate(graph_edges_list): + cur_edge = list(edge) + cur_edge += [graph_weights_list[edge_idx] / graph_w_norm] + graph_py_list.append(cur_edge) + + gr_dict['graph_w_py'] = graph_py_list + gr_dict['graph_weight_norm'] = graph_w_norm + + ## make it more error-prone + + adapt_gpt_test_samples_filt_list = [] + + for rec in adapt_gpt_test_samples_list: + pos_flag = 1 + # if len(rec['adapt_circuit']) % 4: + # pos_flag = 0 + # for gpt_circ in rec['q_circuits']: + # if len(gpt_circ) % 4: + # pos_flag = 0 + + if pos_flag: + adapt_gpt_test_samples_filt_list.append(rec) + + adapt_gpt_test_samples_df = pd.DataFrame(adapt_gpt_test_samples_filt_list) + + return adapt_gpt_test_samples_df + + +################################################################################# + + +def elist_to_nx(input_elist, idx_shift=True): + """Convert a list of edges to a NetworkX graph. + + Parameters: + - input_elist: List of edges in the format [(src, dst, weight), ...]. + - idx_shift: If True, shifts node indices from 1-based to 0-based. + + Returns: + - A NetworkX graph object. + """ + + elist = [] + if idx_shift: + for u, v, w in input_elist: + elist.append((u - 1, v - 1, w)) + else: + elist = input_elist + + G = nx.Graph() + G.add_weighted_edges_from(elist) + + return G + + +################################################ +def check_if_nx_graph_is_weighted(graph_nx): + return all('weight' in graph_nx[u][v] for u, v in graph_nx.edges) + + +############################################## +def nx_to_elist(nx_graph, idx_shift=True): + '''Convert a NetworkX graph to a weighted edge list. + + Parameters: + - nx_graph: A NetworkX graph object + + Returns: + - A dictionary with keys "elist" (list of edges) and "n_nodes" (number of nodes). + ''' + + if not isinstance(nx_graph, nx.Graph): + raise TypeError("Input must be a NetworkX graph object.") + + # Check if the graph is weighted + + weighted = check_if_nx_graph_is_weighted(nx_graph) + if not weighted: + raise ValueError( + "Current version of QAOA-GPT does not support unweighted graphs. " + "Weights w are expected to be sampled from U(0,1).") + shifted_elist = [] + for edge_idx, (n1, n2) in enumerate(nx_graph.edges): + cur_e_weight = nx_graph[n1][n2]['weight'] + if idx_shift: + # Shift node indices from 0-based to 1-based + n1 += 1 + n2 += 1 + + shifted_elist.append((n1, n2, cur_e_weight)) + graph_nx_from_edges = nx.from_edgelist(nx_graph.edges) + n_nodes = graph_nx_from_edges.number_of_nodes() + + return {"elist": shifted_elist, "n_nodes": n_nodes} + + +################################################ +def graph_to_edgelist(g): + """Return a weighted edge list: (src, dst, weight) for all edges.""" + return [(u, v, d['weight']) for u, v, d in g.edges(data=True)] + + +######################## +def eval_ansatz(edgelist, q_circuit, n_nodes, pool_type, verbose=False): + """ + Evaluate the ansatz using CUDA-Q. + + Parameters: + - edgelist: List of edges in the graph. + - q_circuit: The quantum circuit to evaluate. + - n_nodes: Number of nodes in the graph. + - pool_type: Type of pooling to use (e.g., 'all_pool'). + + Returns: + - Energy value of the evaluated circuit. + """ + + # Convert edgelist to a format suitable for CUDA-Q + # idx_shift=True means that node indices in edgelist start from 1 + # idx_shift=False means that node indices in edgelist start from 0 + + g = elist_to_nx(edgelist, idx_shift=True) + + e_list = graph_to_edgelist(g) + + # Hamiltonian + spin_ham = max_cut_ham(e_list) + qubits_num = n_nodes + + # Get the coefficients and pauli words of the Hamiltonian + ham_coeffs = term_coefficients(spin_ham) + ham_words = term_words(spin_ham, qubits_num) + + # Get the pool of operators + if pool_type == 'all_pool': + pools = all_pool(qubits_num) + elif pool_type == 'qaoa_mixer': + pools = qaoa_mixer(qubits_num) + elif pool_type == 'qaoa_single_x': + pools = qaoa_single_x(qubits_num) + elif pool_type == 'qaoa_double_ops': + pools = qaoa_double(qubits_num) + else: + raise ValueError( + "Invalid pool name. Choose from 'all_pool', 'qaoa_mixer', 'qaoa_single_x', or 'qaoa_double'." + ) + + op_indeces = [] + beta_coef = [] + gamma_coef = [] + + for j in range(0, len(q_circuit), 4): + op_indeces.append(q_circuit[j + 1] - 1) # Convert to 0-based index + beta_coef.append(q_circuit[j + 2]) + gamma_coef.append(q_circuit[j + 3]) + + temp = [] + for idx in op_indeces: + if idx < len(pools): + temp.append(pools[idx]) + else: + raise ValueError( + f"Operator index {idx} out of range for the pool of size {len(pools)}." + ) + + mixer_pool = [] + mixer_pool_str = [] + + for op in temp: + temp_op = [] + temp_op_str = [] + for term in op: + temp_op.append(cudaq.pauli_word(term.get_pauli_word(qubits_num))) + temp_op_str.append(term.get_pauli_word(qubits_num)) + mixer_pool.append(temp_op) + mixer_pool_str.append(temp_op_str) + if verbose: + print( + f"Using {len(mixer_pool)} operators from the pool: {mixer_pool_str}" + ) + + @cudaq.kernel + def kernel_qaoa(qubits_num: int, ham_words: list[cudaq.pauli_word], + ham_coeffs: list[complex], + mixer_pool: list[list[cudaq.pauli_word]], + gamma: list[float], beta: list[float], num_layer: int): + + qubits = cudaq.qvector(qubits_num) + + h(qubits) + + idx = 0 + for p in range(num_layer): + + for i in range(len(ham_coeffs)): + exp_pauli(gamma[p] * ham_coeffs[i].real, qubits, ham_words[i]) + + for word in mixer_pool[p]: + exp_pauli(beta[p], qubits, word) + + num_layer = len(mixer_pool) + energy_final = cudaq.observe(kernel_qaoa, spin_ham, qubits_num, ham_words, + ham_coeffs, mixer_pool, gamma_coef, beta_coef, + num_layer).expectation() + + return energy_final.real + + +############################# +def process_graph(graph_idx, + adapt_gpt_out_list, + n_nodes, + pool_type, + verbose=False): + + adapt_gpt_out_dict = adapt_gpt_out_list[graph_idx] + edgelist = adapt_gpt_out_dict["graph_w_py"] + adapt_gpt_energies_list = [] + + q_circuits = adapt_gpt_out_dict.get("q_circuits", []) + + if verbose: + print(f"Processing graph {graph_idx} with {len(q_circuits)} circuits.") + + for i in range(len(q_circuits) + 1): + if i < len(q_circuits): + generated_list = q_circuits[i] + if verbose: + print(f"Evaluating circuit {i}: {generated_list}") + else: + if verbose: + print("No more circuits to process, using adapt_circuit.") + # If no circuits left, use the adapt_circuit + if "adapt_circuit" not in adapt_gpt_out_dict: + raise ValueError( + "No circuits available for evaluation and 'adapt_circuit' not found in the output dictionary." + ) + else: + if verbose: + print("Using adapt_circuit for evaluation.") + if verbose: + print(adapt_gpt_out_dict["adapt_circuit"]) + generated_list = adapt_gpt_out_dict["adapt_circuit"] + + E_final = 999 # Default value if sth goes wrong + try: + E_final = eval_ansatz(edgelist, + generated_list, + n_nodes, + pool_type, + verbose=verbose) + except Exception as e: + print(f"Error in eval_ansatz: {e}") + #except Exception: + # pass + + if i < len(q_circuits): + adapt_gpt_energies_list.append(E_final) + else: + adapt_gpt_out_dict["ADAPT_energy_round"] = E_final + + adapt_gpt_out_dict["adapt_gpt_energies"] = adapt_gpt_energies_list + + return + + +########################## + + +def run_circuit_cudaq(input_fpath, + output_fpath, + n_nodes, + pool_type, + verbose=False): + + with open(input_fpath, "r") as f: + adapt_gpt_out_list = json.load(f) + + for graph_idx in tqdm(range(len(adapt_gpt_out_list)), + desc="Processing graphs"): + process_graph(graph_idx, adapt_gpt_out_list, n_nodes, pool_type, + verbose) + + # Save the results to the output file + with open(output_fpath, "w") as f: + json.dump(adapt_gpt_out_list, f) + + return + + +################################################################################ + + +def eval_adapt_gpt_circ_cudaq(adapt_gpt_res_df, + temp_folder, + n_nodes, + pool_type="all_pool", + verbose=False): + print(">>> eval_adapt_gpt_circ_cudaq CALLED <<<") # Add this line + formatted_timestamp = datetime.now().strftime('%Y-%m-%d__%H_%M_%S') + + temp_folder = Path(temp_folder) + + temp_folder.mkdir(parents=True, exist_ok=True) + + prefix = f'adapt_gpt_res_{formatted_timestamp}_df' + in_fname = f'{prefix}.json' + out_fname = f'{prefix}_cudaq.json' + + in_fname_path = temp_folder.joinpath(in_fname).resolve() + out_fname_path = temp_folder.joinpath(out_fname).resolve() + + adapt_gpt_res_df.to_json(in_fname_path, orient='records') + + run_circuit_cudaq(str(in_fname_path), str(out_fname_path), n_nodes, + pool_type, verbose) + + adapt_gpt_res_w_energies_df = pd.read_json(out_fname_path) + + return adapt_gpt_res_w_energies_df + + +################################################################### + + +def gurobi_max_cut_val_from_nx(graph_nx): + + model = Model("Max-Cut") + model.setParam('OutputFlag', False) + model.setParam(GRB.Param.TimeLimit, 10) + variables = {} + for node in graph_nx.nodes: + variables[node] = model.addVar(vtype=GRB.BINARY, name=f"x_{node}") + + objective = 0 + for u, v, w in graph_nx.edges(data="weight"): + objective -= w * ((2 * variables[v] * variables[u]) - + (variables[v] + variables[u])) + + model.setObjective(objective, GRB.MAXIMIZE) + model.optimize() + solution = [variables[node].x for node in graph_nx.nodes] + + return -model.ObjVal + + +######################################################## +# Graph embedding + + +def get_feather_emb( + graphs_nx_df, + n_workers, + n_nodes, + rounding_digits=2, +): + + combined_unique_graphs_df = (graphs_nx_df[['graph_id', 'edgelist_json' + ]].drop_duplicates()) + + def create_weighted_graph_nx(w_elist): + G = nx.Graph() + G.add_weighted_edges_from(w_elist) + return G + + combined_unique_graphs_df['edgelist_py_list'] = combined_unique_graphs_df[ + 'edgelist_json'].apply(lambda x: [(e[0] - 1, e[1] - 1, e[2]) + for e in json.loads(x) + #(e[0]-1, e[1]-1) for e in x + ]) + + combined_unique_graphs_df['graph_nx'] = ( + combined_unique_graphs_df['edgelist_py_list'].apply( + lambda x: create_weighted_graph_nx(x))) + + combined_unique_graphs_w_idx_df = combined_unique_graphs_df.set_index( + 'graph_id') + graphs_nx_dict = combined_unique_graphs_w_idx_df['graph_nx'].to_dict() + graphs_nx_filt_dict = dict([(name, g) + for name, g in graphs_nx_dict.items() + if g.number_of_nodes() == n_nodes]) + graphs_nx_filt_names = list(graphs_nx_filt_dict.keys()) + graphs_nx_filt_list = list(graphs_nx_filt_dict.values()) + + emb_graph_idx_to_id_dict = { + k: v for k, v in enumerate(graphs_nx_filt_names) + } + emb_graph_id_to_idx_dict = { + v: k for k, v in enumerate(graphs_nx_filt_names) + } + + def get_single_thread_feather_emb(g_list): + # Using our custom wrapper with the original FEATHER implementation + feather_model = FeatherGraph(order=5, + eval_points=25, + theta_max=2.5, + seed=42, + pooling="mean") + feather_model.fit(graphs=g_list) + return feather_model.get_embedding() + + def split_list(lst, n): + it = iter(lst) + return [ + list(islice(it, i)) for i in + [len(lst) // n + (1 if x < len(lst) % n else 0) for x in range(n)] + ] + + def embed_nx_w_feather_parallel(graphs_list, n_workers=n_workers): + graphs_chunked_list = split_list(graphs_list, n_workers) + + #graphs_chunked_list=[graphs_list] + + emb_np_list = Parallel(n_jobs=n_workers)( + delayed(get_single_thread_feather_emb)(g_chunk) + for g_chunk in graphs_chunked_list) + + return np.vstack(emb_np_list) + + feather_par_emb = embed_nx_w_feather_parallel(graphs_nx_filt_list[:], + n_workers=n_workers) + feather_par_emb = feather_par_emb.round(rounding_digits) + + return feather_par_emb, emb_graph_idx_to_id_dict + + +######################################################## +def seq_tokenize_graph(elist): + tok_list = ['bos'] + for n1, n2, w in elist: + tok_list += [tuple(sorted([n1, n2])), w] + tok_list.append('end_of_graph') + return tok_list + + +######################################################### +def prepare_model_input( + graphs_container, + n_nodes, + calculate_classic_maxcut=True, + n_workers_feather=1, +): + + if type(graphs_container) == list: + graphs_edgelist_list_dict = { + f'graph_{i}': g for i, g in enumerate(graphs_container) + } + elif type(graphs_container) == dict: + graphs_edgelist_list_dict = graphs_container + else: + raise ValueError( + "Only list or dict containers are supported for input graphs!") + + graphs_nx_dict = defaultdict(dict) + + for name, nx_graph in tqdm(graphs_edgelist_list_dict.items(), + desc='Preparing graphs...'): + nx_elist_dict = nx_to_elist(nx_graph) + + graphs_nx_dict[name]['elist'] = nx_elist_dict['elist'] + graphs_nx_dict[name]['n_nodes'] = nx_elist_dict['n_nodes'] + if calculate_classic_maxcut: + graphs_nx_dict[name]['energy_gurobi'] = gurobi_max_cut_val_from_nx( + nx_graph) + + graphs_nx_df = pd.DataFrame(graphs_nx_dict).T.reset_index(names='graph_id') + graphs_nx_df['token_seq_round_d2'] = graphs_nx_df['elist'].apply( + seq_tokenize_graph) + graphs_nx_df['edgelist_list_len'] = graphs_nx_df['elist'].apply(len) + graphs_nx_df['approx_ratio'] = None + graphs_nx_df['label'] = 'test_interactive' + graphs_nx_df['edgelist_json'] = graphs_nx_df['elist'].apply( + lambda x: json.dumps(x)) + + print("Performing FEATHER embedding") + feather_par_emb, emb_graph_idx_to_id_dict = get_feather_emb( + graphs_nx_df, + n_workers=n_workers_feather, + n_nodes=n_nodes, + ) + + emb_graph_id_to_idx_dict = { + v: k for k, v in emb_graph_idx_to_id_dict.items() + } + + graphs_nx_df['has_emb'] = graphs_nx_df['graph_id'].apply( + lambda x: True if x in emb_graph_id_to_idx_dict else False) + + graphs_nx_df = graphs_nx_df[graphs_nx_df['has_emb']] + + graphs_nx_df['graph_id'].apply(lambda x: x[:2]).value_counts() + + return graphs_nx_df, feather_par_emb, emb_graph_id_to_idx_dict diff --git a/docs/sphinx/using/applications.rst b/docs/sphinx/using/applications.rst index 2fcb260b060..8af982d1a74 100644 --- a/docs/sphinx/using/applications.rst +++ b/docs/sphinx/using/applications.rst @@ -311,6 +311,15 @@ CUDA-Q Applications QM/MM partitioning in the PE model. +
+ QAOA-GPT: GPT model for generating efficient QAOA quantum circuit. +
+ Learn how to implement QAOA-GPT algorithm using CUDA-Q. +
+ QAOA-GPt for generating QAOa circuit. +
+ +