← Back to index

Changelog

Author: Hakim Ghelab, VegaLaboratories LTD

Real, chronological log of every change made to this codebase. This is the short what; PORT_LEDGER.md has the detailed why, the real evidence behind each fix, and the full engineering narrative.

2026-09-19 — Research section on layered floors and the two signals (Hessian vs KL), plus a floor-figure fix

Added

Found (at 431 of 497 scored, proxies only, not quality)

Review

Fixed

2026-09-19 — One integrated tool for the Hessian explainer page: the whole page is now rebuilt from live data

Changed

Found

2026-09-19 — Live-status updater for the Hessian explainer page, and the exact full command up front

Added

Found

2026-09-19 — --hessian-unscored-fallback kl: tensors with no Hessian score no longer read as “safest” under --hessian-primary

Added

Found

2026-09-19 — Site-wide design sweep: every page now has back-to-index, both rails, Cmd+K and index entries, proven in a real browser

Added

Fixed

2026-09-19 — New mechanism reference: how a Hessian score becomes a bit-width (--hessian-primary versus the floor)

Added

Found (real, verified on the plan-time snapshot of 407 scored tensors)

2026-09-18 — Real MILP infeasibility root-caused: --pareto meaningful (the default) silently blocks Hessian floor requirements; soft weight confirmed to do nothing

Real, direct debugging of a genuine HiGHS Status 8: Infeasible failure on 02_optimize_plan_HYBRID_OPTIQ_PARETO_v3_3.py with --hessian-primary + --hessian-floor-tiers active. Root cause found by testing, not guessed: --pareto meaningful (the real default) filters candidate bit-widths using each tensor’s isolated-KL dominance – a filter designed for a world where KL was the only signal, with no awareness that a Hessian floor is about to demand a specific bit-width for a completely different reason. Real, checked at 405/497 coverage: 55 of 497 tensors had their Hessian-floor-required bit filtered out, several forced to jump to 16-bit instead of their real tier, and the resulting collision with --hessian-primary’s own tight re-pinning produced genuine infeasibility – worse as real coverage grows, not better.

Fixed

Added

2026-09-16 — Hessian danger score investigated as a free KL substitute; hybrid-checkpoint design; toolkit consolidation; site sweep

Real Metal [metal::malloc] Resource limit (499000) exceeded crash root-caused and fixed in yaqa_core.py’s ldlq2hess_quantize() – wq_full/scales_full/ biases_full were never flushed by mx.eval(), only hatWr was, letting a Metal command buffer’s resource count balloon past the hard limit on large tensors.

Fixed two real multi-job bugs in monitor.sh/generate_dashboard.py: a fixed dashboard port shared across every simultaneously-watched job caused real job-flipping (fixed via a per-job hashed port), and find_pid() grabbed the first system-wide process match instead of filtering on the real --resume-dir path (fixed). Dashboards now show which job they belong to.

Added

Investigated (real, verified numbers)

Site

Five new pages built and integrated into the Cmd+K manifest and exports/index.html sidebar/cards: HESSIAN_MASTER_REFERENCE.html (start here), HESSIAN_SCORE_ORIGIN.html, HESSIAN_VS_KL_TENSOR_EXPLORER.html, YAQA_PAPER_VS_CODE.html, HESSIAN_HYBRID_CHECKPOINT_DESIGN.html.

Not yet done: the Hessian Hybrid checkpoint has never been built into a real model or benchmarked end to end – whether Hessian-driven allocation is actually competitive with cascaded-KL-driven allocation remains untested.

Full real narrative, start here: research_hadamard_blowup/exports/HESSIAN_MASTER_REFERENCE.html.

2026-09-14/15 — MILP-aware Hessian: closing the coverage gap, making the solver listen to curvature

Real godmode multi-bit sensitivity sweep added (--godmode-multi-bit-checkpoint --godmode-candidate-bits 3,4,5,6,8), testing every candidate bit-width per tensor and writing godmode_sensitivity_checkpoint.jsonl – closes the real coverage gap left by tensors this project’s own protection rules kept at full precision (and so never got a Hessian score from the normal correction pass).

Added

Full real narrative: research_hadamard_blowup/HESSIAN_STRENGTH_SWEEP_2026-09-14/HESSIAN_PROBE_COMPLETE_GUIDE.html and …/MILP_AWARE_HESSIAN_GUIDE.html. Raw sweep data + logs: SWEEP_SUMMARY.html.

2026-09-08 → 2026-09-15 — cascaded sensitivity: the free signal hiding in the Hessians; first real benchmark

Correction (2026-09-16, twice): this entry was first dated “2026-09-09/10” in this changelog with no verification, which was wrong. A second pass re-dated it from internal content clues (fix comments, “REAL BUG FOUND” markers) – closer, but still wrong for several pages, because a date mentioned in a page’s own body text is sometimes about the data being analyzed, not the page’s own authorship. Final, real dates below come from the actual Write tool-call timestamps in this project’s own Claude Code session transcript – the ground truth, not inferred from either source.

Real, live free-signal analysis of the Hessian’s own structure as a candidate proxy for expensive cascaded-KL sensitivity measurement – first pass at what would become the Sep 14-16 MILP-aware Hessian investigation above.

Added

IMPROVEMENT_LEDGER/00-09, each its own real page. Every date below is the page’s first-written date (its first real Write call in this project’s own session history), not a “last touched” date – a minor edit does not reset it, only a real, substantial revision earns a separate “updated” note: - 00 · Absolute Zero Start Here – 2026-09-10. - 01 · Zero to Expert, Redone – 2026-09-10. - 02 · MTP-YAQA Log Walkthrough – 2026-09-10. - 03 · The Brainstorm – 2026-09-11. - 04 · The lm_head Q4 Incident – written 2026-09-12, updated 2026-09-13. - 05 · lm_head Fix, Next Steps – written 2026-09-12, updated 2026-09-13. - 06 · Trunk, Hidden State, Sidecar – 2026-09-13. - 07 · The Hessian Signal – written 2026-09-13, updated 2026-09-15. - 08 · Full Solver Debrief – written 2026-09-14, updated 2026-09-15. - 09 · First Real Benchmark Results – 2026-09-15.

The lm_head Q4 protection incident and its fix, the trunk/hidden-state/sidecar story, the Hessian signal discovery, the full hybrid-solver debrief, and the project’s first real, complete MMLU/GSM8K/IFEval/BFCL/HumanEval benchmark results – highest 5-test mean of the group, never worst on any single metric.

2026-09-08 — MTP sidecar YAQA correction: implemented and validated

Real implementation of the plan below: --correct-mtp/--mtp-bits/--mtp-group-size flags added to 05_full_model_quantize.py, reusing the exact same Hessian-collection mechanism (YaqaCatcher/mx.custom_function vjp) already proven on the trunk, applied to the MTP module’s one real mlx_lm.models.qwen3_5.DecoderLayer instance – confirmed via inject_mtp_support() that this is the same class the trunk’s own layers are built from, so no new correction math was needed. New forward-pass recipe verified directly against mtp_patch.py’s own real _mtp_core(): trunk hidden state + real next-token embedding -> MTP layer -> real next-NEXT-token cross-entropy (MTP predicts one token further out than the trunk).

Fixed

Real result

All 7 MTP tensors corrected, every one passed the safety gate on the first attempt (no damping search needed – easier to correct than some trunk tensors were). Real Hessian-weighted error vs. naive: q_proj ~850x better, k_proj ~185x, v_proj ~226x, o_proj ~9,675x, gate_proj ~3,075x, up_proj ~5,404x, down_proj ~3,327x. Corrected sidecar has the identical 29-tensor structure as the naive one – only the 7 quantized tensors’ values changed. Full regression suite still green.

Not yet built into a full official output model or benchmarked end to end – the smoke test validated the mechanism against a symlinked-trunk test directory, not a real official build. Full plan and real evidence: research_hadamard_blowup/exports/MTP_YAQA_CORRECTION_PLAN.html.

Launched (2026-09-08, later) — real full build with MTP correction applied

Real production launch, reusing the existing fully-completed trunk resume cache (363/363 tensors + lm_head already corrected in the prior -v2-fp32 build) rather than recomputing it, so this run only re-assembles the model and additionally runs the new --correct-mtp step. The existing -v2-fp32 model is left completely untouched – the resume cache was copied under a new name first, specifically so this first real run of the new code path can’t corrupt the only known-good copy if something goes wrong mid-write.

Exact commands:

OLD="/Users/hghelab/.mtplx/models/Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32"
NEW="/Users/hghelab/.mtplx/models/Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32-mtpcorrected"
cp -a "${OLD}.yaqa_resume" "${NEW}.yaqa_resume"

cd /Users/hghelab/ai-employee-build/projects/MLX_OptiQ/qwen38-27b-heretic-ara/scripts/yaqa_port
nohup ./run_full_yaqa.sh "$NEW" --n-calibration 4 --incoherence none --correct-mtp \
  > "${NEW}.yaqa_resume/orchestrator_top.log" 2>&1 &

(--mtp-bits/--mtp-group-size deliberately omitted – defaults to the plan’s own native MTP bit-width, so this run is bit-for-bit naive-vs-corrected, not confounded by also changing the target bit-width.)

Real gaps found and fixed while launching this, both real bugs not previously exercised because no prior run had a live process outside 05_full_model_quantize.py itself at monitoring time: - monitor.sh’s find_pid() only pattern-matched 05_full_model_quantize.py, so it reported “NOT RUNNING (finished or crashed)” for the entire real duration of the 06_lm_head_gptq.py step even though that process was genuinely alive (confirmed directly via ps) – fixed to match both script names. - generate_dashboard.py’s real_assembly_progress() had no milestone at all for the new MTP-correction step – neither dashboard could show it happening. Added a mtp_yaqa step keyed on correct_mtp_sidecar()’s own real log markers ([MTP-YAQA] Wrote real YAQA-corrected sidecar to... / [MTP-YAQA] Source declares no MTP tensors...), shown as skipped (not a false failure) on any run that didn’t pass --correct-mtp at all. Verified by actually running the generator against the live resume dir and grepping the real output HTML for the new label – present in both dashboard.html and brand/live-status.html (they share the same render function).

Added (2026-09-08, later still) — --reuse-cached-fallback skip option for lm_head

Real, direct user request during the launch above: lm_head’s one-sided GPTQ damping search is deterministic against a fixed real Hessian, so a prior run that already exhausted every damping ratio and fell back to naive (method: "naive_fallback" in the manifest) will fail identically on retry – wasting ~15-20 real minutes to reach the same known result. Added --reuse-cached-fallback to 06_lm_head_gptq.py’s existing resume-skip check (previously only accepted a prior "gptq" success) so it also accepts a matching cached naive_fallback, still checked against the current plan’s real bits/group_size. Off by default.

Real bug found and fixed immediately after adding it: run_full_yaqa.sh forwards its full EXTRA_ARGS to every invocation, including 05_full_model_quantize.py (trunk batches, --print-num-batches, --assemble-from-resume) – which does not define this flag, so passing it at all crashed every one of those calls with “unrecognized arguments” before 06_lm_head_gptq.py ever got to use it. Fixed by adding the same flag to 05_full_model_quantize.py’s argparse as a real, documented no-op there.

Real pitfall found (2026-09-08, later still) — cp -a into an already-existing dir

Re-running Step 1 of the launch procedure (cp -a "${OLD}.yaqa_resume" "${NEW}.yaqa_resume") a second time, after ${NEW}.yaqa_resume already existed from the first run, silently nested the entire source directory one level inside the destination instead of refreshing it – cp -a src dst copies src’s contents into a new dst, but copies src itself into dst as a subdirectory when dst already exists. Real, measured effect: resume-cache size doubled from 16GB to 32GB with no error or warning. Did not affect the running build (it reads the correct top-level files regardless), but is real wasted disk space. Documented in YAQA_COMMAND_REFERENCE.md’s Step 1 as a check-before-you-copy warning.

2026-09-07 (yet even later) — the MTP sidecar was never YAQA-corrected; plan to fix it

Real gap, found while reviewing today’s full benchmark result: the YAQA build’s MTP speculative-decode draft head is quantized with plain mx.quantize (optiq.runtime. mtp_convert.preserve_mtp() -> _quantize_mtp()), zero correction – confirmed 0/363 MTP-related entries in the real resume manifest. This is the same code path the reference baseline uses too, so it looked at first like a shared, non-differentiating limitation.

Real correction to that read: checked the reference model’s own real build manifest (V5_BUILD_MANIFEST.json) directly – "elapsed_build_s": 35.77 for the whole trunk, "language_trunk_source": "original_source + exact plan predicate", and zero Hessian/GPTQ code anywhere in 05_build_final_model_V5.py, the script that actually built it. The reference trunk is naive too. So the reference is naive-trunk + naive-sidecar (internally consistent); YAQA is corrected-trunk + naive-sidecar (internally inconsistent). Today’s entire YAQA-vs-reference benchmark was never a clean apples-to-apples comparison – a real, plausible contributor to the D1/D3 speculative-decode degradation found earlier, since deeper depths depend most on trunk/draft agreement and only one side of that pair moved.

Fix, not a workaround: extend real YAQA correction to the MTP sidecar’s 7 real quantizable tensors, at the same bit width the naive sidecar already uses (Q4, confirmed from the real build manifest), so the comparison becomes naive-Q4-sidecar vs. YAQA-corrected-Q4-sidecar – same tensors, same bits, only the rounding method differs. Real feasibility check: optiq.runtime.mtp.mtp_patch.inject_mtp_support() builds the MTP module from mlx_lm.models.qwen3_5.DecoderLayer – the exact same class the trunk’s own decoder layers use – so YAQA’s existing Hessian-collection and correction machinery, already proven on 362 real trunk DecoderLayer instances, should attach to this one directly rather than needing new correction math.

Full plan, evidence, and design requirements: research_hadamard_blowup/exports/MTP_YAQA_CORRECTION_PLAN.html, with the real trunk-vs-MTP-sidecar tensor-name cross-reference in MTP_TENSOR_MATCH_TABLE.html. Implementation as a new flag on 05_full_model_quantize.py (not a new standalone script) in progress.

2026-09-07 (even later) — every doc built by build_html_docs.sh was showing its own title twice

Real, visible bug, found by directly clicking through every link in exports/index.html: every page built through this project’s own build_html_docs.sh rendered its title twice – once in pandoc’s styled title-block header, once again immediately underneath as a plain, unstyled heading, same text both times.

Real root cause: the script derives each doc’s title with TITLE="$(grep -m1 '^# ' "$INPUT_MD" | ...)" – the first # Heading line found anywhere in the file, which is also that same doc’s real first body heading. Passing that to pandoc as --metadata title="$TITLE" makes pandoc render it once in the generated title-block header and pandoc separately renders the body’s own identical heading as a normal <h1> right after it – same text, twice, stacked on the page. Confirmed present on 12 of the 13 real docs reachable from exports/index.html (README, RUNNING_GUIDE, YAQA_COMMAND_REFERENCE, PORT_LEDGER, CHANGELOG, RESEARCH_Hadamard_Blowup, Adaptive_Damping_Power, BF16_Curvature_Bug_Plain_English, YAQA_UMA_Hessian_Precision_Root_Cause, GPTQ_LM_Head_Safety_Gate_Plan, AFFINE_MODE_CODE_REVIEW, GPTQ_MLX_Integration) – every hand-authored HTML doc (no title-block-header, e.g. this project’s dashboards and the RCA report above) was unaffected, confirmed by direct check, not assumed.

Fixed

2026-09-07 (later still) — real, measured slowdown root-caused (again): F32 scales/biases + embed_tokens mismatch; rebuilt and re-verified

Real, measured gap, after the mode: affine fix above was already live: mtplx tune still showed the YAQA build 21-55% slower than the reference across AR/D1/D2/D3, and peak memory +17.2% (27.85GB vs 23.77GB). The mode fix did not touch this – it’s a separate bug.

Real root cause #1, confirmed by reading every tensor’s real safetensors header on disk (not estimated): every one of 727 .scales/.biases tensors in the YAQA build was stored as float32 instead of bfloat16 – double the per-group metadata bytes MLX’s quantized-matmul kernel reads on every forward pass. bits/group_size/mode matched the reference on all 1912 tensors (0 mismatches) – never a bit-width bug. Traced to 5 save points across 05_full_model_quantize.py and 06_lm_head_gptq.py: the correction math deliberately runs in float32 for numerical precision (correct), but nothing ever downcast the resulting scales/biases back to bf16 before saving, unlike the reference pipeline which runs bf16 throughout.

Real root cause #2 (found while checking for like-for-like mtplx tune parity): embed_tokens isn’t in either model’s real plan file (it’s a lookup table, not a YAQA/GPTQ correction target), but this script’s generic “outside the plan” fallback branch was quantizing it anyway (4-bit, U32, 636MB) while the reference leaves it uncompressed (BF16, 2.54GB) – a real confound for any like-for-like comparison, not itself the speed cause.

Fixed

Real rebuild and independent verification (not just code review)

Rebuilt via --assemble-from-resume (26h correction not re-run; already-corrected values just re-saved at the right width) with --fallback-bits 4 added to match the reference’s fallback convention:

/Users/hghelab/.local/share/uv/tools/mlx-optiq/bin/python3 -u 05_full_model_quantize.py \
  --assemble-from-resume \
  --resume-dir "/Users/hghelab/.mtplx/models/Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32.yaqa_resume" \
  --output "/Users/hghelab/.mtplx/models/Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32" \
  --n-calibration 4 --incoherence none --fallback-bits 4

Prior broken build moved aside first (not deleted) to Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32.OLD-before-fallback4-fix.

Verified against the real rebuilt model on disk, independent of the manifest’s own self-reported numbers: - Tensor forensics re-run: 0/1910 tensors differ from the reference in dtype, bits, group_size, mode, or byte size – BF16 and U32 totals match the reference to the byte. - Weight-provenance check: dequantized the same real tensor 4 ways (original source, naive quantization, resume-cache correction output, live shipped model). Live matched the resume cache to bf16-rounding precision (~0.1-0.6%) and differed substantially from naive (1.9-4.5% absolute) – the real correction ran and its exact output shipped, not naive quantization standing in for it. - Resume manifest analysis: 362/363 tensors used real YAQA correction (lm_head is the one known naive_fallback); real Hessian-weighted error reduction vs. naive ranged 97.49-100%, median 99.67%, zero tensors worse than naive. - Re-benchmarked (mtplx tune): AR 17.889 vs reference 17.914 (-0.14%, parity), D2 47.422 vs 47.931 (-1.06%, parity), peak memory 23.765GB == reference exactly. D1 (-20.19%) and D3 (-19.93%) still lag, with real acceptance-rate degradation at deeper speculative depths (D3 depth-3: 94.74% -> 80.60%). No localized failure found in the manifest to explain this – working hypothesis is that YAQA’s correction and the reference’s own correction algorithm land on different, individually-small residual errors that compound differently through depth; not yet confirmed by an independent output-quality benchmark.

Full detail, real evidence, and the full comprehensive tensor table: research_hadamard_blowup/exports/RCA_scales_f32_bug.html.

2026-09-07 (later) — real, measured inference slowdown root-caused: missing quantization “mode” declaration

Real, measured gap (direct user finding via mtplx tune on both models): the real production YAQA build ran substantially slower than the reference HybridPareto5bpw-V3.3_Stratified model at every real benchmarked depth – AR 14.06 vs 17.91 tok/s (~27% slower), best-depth 28.83 vs 54.08 tok/s (~1.87x slower). Confirmed NOT caused by a different plan: real per-tensor bit assignments matched almost exactly (178/130/45/11 vs 178/130/44/11 tensors at 4/5/6/8-bit – off by one tensor) and real total file size was nearly identical (20.22GB vs 20.19GB).

Real root cause, confirmed by direct comparison of both models’ real config.json: every per-tensor entry this script ever wrote to plan_config[name] only ever carried {"bits", "group_size"} – the real "mode" key (MLX’s own quantization-mode field) was never included anywhere in 05_full_model_quantize.py, at either the per-tensor or top-level quantization dict. The reference model’s config declares mode: "affine" on all 363 real entries plus the top level; the YAQA build’s config had it on 0 of 364. Confirmed the underlying packed tensor bytes were never wrong – every real mx.quantize()/to_quantized() call in this codebase always used MLX’s own real default (mode="affine", confirmed directly from mlx.core.quantize’s real signature) – this was purely a missing config declaration, not corrupted data.

Fixed

2026-09-07 — real production build completed; found and fixed a real missing-sidecar-files gap

The real full run finished successfully: Part 5c PASS, real output model saved to Qwen3.8-27B-heretic-ara-YAQA-5bpw-v2-fp32 — 363/363 tensors real-corrected (362 YAQA + 1 GPTQ), 134 kept at full precision by the real plan, 6.015 real bits/weight, real vision (921MB) and MTP (299.7MB) sidecars reattached. Confirmed the source BF16 model’s memory was genuinely released (no process running, ~34.6GB real free RAM afterward).

Fixed

2026-09-06 — the BF16 curvature bug: Hessian construction was running in 16-bit, not 32

Real, measured gap missing from this changelog until 2026-09-07 — this fix has full documentation (5 real docs: root-cause research, plain-English writeup, the math/technical companion, the lm_head GPTQ safety-gate companion fix, and the adaptive-damping writeup, all in research_hadamard_blowup/exports/) but never got a changelog entry when it actually happened. Recorded here now, in its real chronological place, not folded into a later entry.

Real root cause: production was constructing YAQA’s Hessian curvature (the matrix the two-sided correction is built on) in bfloat16 instead of float32. A Hessian needs enough numerical precision to stay genuinely positive-semidefinite — bf16’s ~3 decimal digits of precision was not enough, and this broke that guarantee for at least one real tensor (layers.14.linear_attn.in_proj_qkv), which the correction made measurably worse than naive quantization instead of better. Found via a full Define/Hypothesize/Research/Test/ Measure/Validate investigation that first ruled out Hadamard rotation and calibration size as causes before finding the real one.

Fixed

Zero-assumed-knowledge companion explainer for this whole investigation: HESSIAN_ZERO_TO_EXPERT.html.

Not to be confused with the 2026-09-07 F32-scales/biases bug below — that one is about the output metadata being saved too wide (a speed bug); this one is about the input curvature computation being too narrow (a correction-quality bug). Different stage, different symptom, different fix. Cross-referenced directly in both docs.

2026-09-06 (later) — tensor health check, monitor.sh stale-process bug, brand redesign, outreach kit

Added

Fixed

Clarified (real understanding, not a new finding)

2026-09-04 — Part 5c: real output model, memory/Metal fixes, batching

Fixed

Added

Verified end to end (real data, not synthetic)

Added (real-time, prompted directly by the user)

2026-09-05 — Real orchestrator crash fix, universality, naming

Fixed

Added

Disclosed, not done

2026-09-05 (later) — lm_head auto-detection hardened, doc/diagram pipeline fixed

Fixed

Added

Real file status as of this entry (so this doesn’t have to be re-derived later)

File Status
scripts/yaqa_port/PORT_LEDGER.md Fixed (diagram source uses real newlines, not <br/>)
scripts/yaqa_port/exports/PORT_LEDGER.html + .pdf Fixed and visually verified (both HTML and printed PDF page)
00_DOCS/HTML/yaqa_batching_architecture.html Fixed in place (real diagram baked in, .eyebrow/.callout .tag fonts raised from 11.5px/10.5px to 13px, below this project’s stated 12-13px floor)
HybridOptiQ_FINAL_BENCHMARK/.../YAQA-to-MLX Port Ledger_dark.html Still broken, not yet touched – a stray browser-saved copy, not a canonical source; should be regenerated from the fixed exports/PORT_LEDGER.html rather than hand-patched separately
README.md, RUNNING_GUIDE.md, YAQA_COMMAND_REFERENCE.md Never affected – confirmed zero Mermaid blocks in any of them

Earlier (Steps 1-4, and Step 5 Parts 5a/5b) – see PORT_LEDGER.md

Full detail in PORT_LEDGER.md’s Step 1-4 and early Step 5 sections. Summary: the gradient-capture mechanism, Sketch B’s real Hessian formula, the LDLQ_2hess block-sequential rounding algorithm, and the complete pipeline on one real layer were each independently built and verified (Steps 1-4). Step 5 generalized this to many real tensors at once, found and fixed a Hessian batch-accumulation formula bug, a missing Hadamard rotation stage, a diagonal-zeroing bug, and an RNG-reuse bug across two rounds of independent code review, before this session’s Part 5c work above.


© 2026 Hakim Ghelab, VegaLaboratories LTD. All rights reserved.