Одним вечером я решил проверить: что если взять решётку из 10 000 узлов, задать одно правило передачи энергии между соседями — и просто запустить? Никакой теоретической физики из учебника, никакой подготовки. Посмотреть что вырастет само.

Спойлер: за один день появились волновое уравнение, интерференция, фазовый переход, рождение и аннигиляция частиц, и стрела времени. Всё из одного уравнения.

<cut />

Старт: убираем случайность

У меня уже был проект — симуляция квантового поля на тетраэдрах, где узлы случайно осциллировали (Math.random()). Красиво, но мертво. Шум без смысла, без структуры.

Я сделал шаг: заменил случайность на детерминированный закон. Каждый тик поле в каждом узле обновляется по правилу:

// GridField.java
private void update() {
    double[][] newPhi = new double[HEIGHT][WIDTH];

    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            // Дискретный Лапласиан (4 соседа, торическая топология)
            int left  = (x - 1 + WIDTH)  % WIDTH;
            int right = (x + 1) % WIDTH;
            int up    = (y - 1 + HEIGHT) % HEIGHT;
            int down  = (y + 1) % HEIGHT;

            double laplacian = phi[y][left] + phi[y][right]
                             + phi[up][x]   + phi[down][x]
                             - 4.0 * phi[y][x];

            // φ⁴ потенциал: тянет поле к вакуумам ±V
            double force = -LAMBDA * (phi[y][x] * phi[y][x] - V * V) * phi[y][x];

            velocity[y][x] = velocity[y][x] * DAMPING
                           + FLOW_RATE * laplacian
                           + force;

            newPhi[y][x] = phi[y][x] + velocity[y][x];
        }
    }
    phi = newPhi;
    tick++;
}

Константы которые управляют всем поведением:

static final int    WIDTH     = 100;
static final int    HEIGHT    = 100;
static final double FLOW_RATE = 0.20;   // скорость волны, ОБЯЗАТЕЛЬНО < 0.25
static final double DAMPING   = 0.998;  // затухание (1.0 = без потерь)
static final double V         = 300.0;  // положение вакуумов ±V
static final double LAMBDA    = 0.000002; // сила нелинейности φ⁴

Важно: FLOW_RATE < 0.25 — это условие устойчивости Куранта для дискретного волнового уравнения. Выше — поле взрывается в NaN за несколько тиков.

Фаза 1: одно правило → волна и интерференция

Запустил с LAMBDA = 0 (чистое волновое уравнение). Одна точка возбуждения в центре:

// GridMain.java
GridField field = new GridField();
field.excite(50, 50, 600.0);
field.start(50); // 50ms на тик = ~20 тиков/сек

По экрану пошла идеальная круговая волна. Дошла до края — появилась с другой стороны, потому что решётка замкнута как тор (периодические граничные условия).

Волна вернулась и встретила себя. На экране появились концентрические интерференционные кольца — точно как в двухщелевом опыте Юнга. Только я этого не программировал. Это вышло из Лапласиана.

Два источника:

field.excite(35, 50, 600.0);
field.excite(65, 50, 600.0);

Два кольца пересеклись — паттерн интерференции с узлами и пучностями. Принцип Гюйгенса — каждый узел сам становится источником — реализовался автоматически.


Фаза 2: нелинейность → частицы

Чистое волновое уравнение умирает: через тысячу тиков поле превращается в равномерный туман. Тепловая смерть. Второе начало термодинамики не было заложено — оно просто случилось.

Включаю LAMBDA = 0.000002.

Потенциал φ⁴ — это V(arphi) = \lambda(arphi^2 - V^2)^2 — двойная яма. Поле теперь хочет быть либо в +V, либо в −V. Как магнит который намагничен в одну из двух сторон.

После одного клика мышью:

  1. Расходится волна — как раньше

  2. Поле начинает выбирать: здесь домен +V, там домен −V

  3. На границах доменов возникает устойчивая структура — не размывается

  4. Эти границы движутся, сталкиваются, аннигилируют

Детектор "частиц":

// GridField.java
public List<int[]> detectParticles() {
    List<int[]> boundaries = new ArrayList<>();
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            int right = (x + 1) % WIDTH;
            int down  = (y + 1) % HEIGHT;
            // Граница домена = соседи имеют разные знаки
            if (phi[y][x] * phi[y][right] < 0 ||
                phi[y][x] * phi[down][x]  < 0) {
                boundaries.add(new int[]{x, y});
            }
        }
    }
    return boundaries;
}

Счётчик показал: 7 → 222 → 1397 → 9821 частиц. Всегда чётное число — они рождаются парами (топологический заряд сохраняется). Я это не закладывал.


Что я увидел на экране

Tick 58 — одна точка возбуждения, φ⁴ включён. На синем фоне (вакуум −V) появился идеальный золотой круг (домен +V) с чёткой чёрной границей. Как мыльный пузырь. Как другое пространство внутри.

В космологии это называется фазовым переходом при инфляции — пузырь новой фазы вакуума внутри старого. Это есть в учебниках по физике раннего Вселенной. Я не читал про это когда писал код. Оно просто получилось.

Tick 821 — 5297 частиц. Крестообразная структура из доменов — результат возвратных волн с тора, интерферирующих с доменными стенками.

Tick 18 245 — 9821 частица. Максимально хаотичный вакуум. Это квантовый вакуум — не пустота, а кипящее море виртуальных пар которые рождаются и тут же аннигилируют.

Физика которая вышла сама — без намерения

Явление

Где видно

Было запрограммировано?

Волновое уравнение

Концентрические кольца

Нет

Принцип Гюйгенса

Каждый узел = источник

Нет

Интерференция

Полосы при двух источниках

Нет

Торическая топология

Волна возвращается

Да (граничные условия)

Фазовый переход

Домены ±V

Нет

Рождение пар

Счётчик чётный

Нет

Аннигиляция

Счётчик падает

Нет

Стрела времени

Порядок → хаос

Нет

Тепловая смерть

Туман через 1000 тиков

Нет

9 из 10 явлений не программировались. Они вышли из одного уравнения в 15 строк.

Управление

// GridVisualization.java
// ЛКМ / drag → +2000 к φ в узле
// ПКМ / drag → −1000 к φ в узле

@Override
public void mousePressed(MouseEvent e) {
    int x = e.getX() / CELL_SIZE;
    int y = e.getY() / CELL_SIZE;
    double amount = SwingUtilities.isRightMouseButton(e) ? -1000.0 : 2000.0;
    field.excite(x, y, amount);
}

Кликаешь левой — создаёшь возбуждение. Правой — убираешь. Можно "потолкать" домен и посмотреть как он движется.

Что дальше

Следующий шаг — атом. Атом это стоячая волна которая замкнулась сама на себе. Для этого нужно второе поле χ, связанное с основным φ через бипотенциал:

V(arphi, \chi) = \lambda(arphi^2 - V^2)^2 - g \cdot arphi^2 \cdot \chi^2

Лёгкое поле χ будет резонировать вокруг тяжёлого φ-сгустка. Выживут только орбиты где умещается целое число длин волн χ. Квантование энергий выйдет само — из геометрии, не из аксиом.

Код

Java 17+, никаких зависимостей кроме стандартного Swing. Gradle сборка. Репозиторий: github.com/malexple/quant

git clone https://github.com/malexple/quant
./gradlew run

Запускаешь. Кликаешь в центр. Смотришь как рождаются и умирают частицы. Занимает минуту.

После этой минуты квантовое поле перестаёт быть абстракцией из учебника.


Написано за один день итеративного эксперимента. Следующая статья — два связанных поля и попытка получить атом.