| --- |
| license: cc0-1.0 |
| language: [en] |
| tags: [astrology, sidereal, jyotisha, relationships, tabular, phase-model, genderless, artaquest] |
| datasets: [artaquest-foundation/artamatch-genderless, artaquest-foundation/artamatch-sidereal] |
| metrics: [roc_auc] |
| library_name: numpy |
| --- |
| |
|
|
| # The deployed STACK (2026-08-19): geography + ArtaModel IV — public leaderboard 0.64303 |
|
|
| The model that leads the public board of `artamatch-genderless` is not ArtaModel alone but a **stack of three |
| members** with non-negative weights on their ranks, fitted per availability group on forward-chained train OOF: |
|
|
| | member | what it reads | held out alone | |
| |---|---|---| |
| | **GEO** — plain + geography | older/younger age at the start, their gap, the start year, **the two birthplaces**, their great-circle distance, order-free lat/lon extremes, same-place flag; three small LightGBMs averaged (`geo_lgbm_{0,1,2}.txt`, JSON dumps beside them) | 0.6406 | |
| | **AM-G** — ArtaModel IV greedy | the phase model above, boosted over split single-phasor fields | 0.6071 | |
| | **AM-F** — ArtaModel IV fixed cycle | the same phasors in a fixed cycle (the stable twin) | 0.6184 | |
|
|
| | group | GEO | AM-G | AM-F | bias | |
| |---|---|---|---|---| |
| | 0 — wedding-sky clocks exist (t1/t2 Uranus) | 2.055 | 0.355 | 0.335 | +0.066 | |
| | 1 — synastry only (a Uranus) | 1.467 | 0.390 | 0.000 | -0.529 | |
| | 2 — wedding sky only | 1.750 | 0.000 | 0.335 | +0.260 | |
|
|
| `logit = Σ wᵢ·(rankᵢ − 0.5) + b` per group (a member that cannot speak for a pair casts no vote); ranks are read |
| off the quantile grids stored in `stack_iv_deployed.json`; both orders of the pair are scored and averaged. Members |
| are refitted on train + test for deployment; the weights are the fitted ones. **Held out 0.6448 (7,631 pairs) · |
| public board 0.64303 · the plain ages alone 0.6155.** The gain is geography — where the two were born — not the sky; |
| every other tradition tried (21 tropical traditions incl. numerology, 14 Jyotiṣa/Zǐ Wēi families) added nothing. |
|
|
| ```python |
| import sweshim, stack_iv_predictor as P # pure Python + numpy; the same files run in the browser |
| P.init(open("ephem4.bin","rb").read(), open("tables.json").read(), open("stack_iv_deployed.json").read(), |
| [open(f"geo_lgbm_{k}.json").read() for k in range(3)], sweshim_module=sweshim) |
| r = P.predict("1936-08-04", 37.943, 23.647, "1924-05-14", 37.727, 26.909, "1968-06-15") |
| r["probability"], r["breakdown"] # 0.333 here; identical with the partners swapped |
| ``` |
|
|
|
|
| **The match finder.** `best_matches(dob, lat, lon, start=None, top=20, min_age=18, max_age=100)` scores the stack over every |
| birthday of people alive at the start × the capital of every country (`capitals.json`, 197 — Wikidata P36 + P625): ~5.9 M |
| candidates in about a second, by the model's structure (geography by year × capital, vectorised; ArtaModel by day from |
| weekly-interpolated outer planets), one best day per (year, capital). Live on the prod page. |
|
|
| ```python |
| P._CAPS = json.load(open("capitals.json")); r = P.best_matches("1994-02-15", 35.6892, 51.389) # Tehran, start = today |
| r["matches"][:3] # [{dob, capital, country, lat, lon, probability, geo_probability, am_greedy_logit, am_fixed_logit}, ...] |
| ``` |
|
|
|
|
| **The best start day.** `best_start_days(dob_1, lat_1, lon_1, dob_2, lat_2, lon_2, from_date=None, years=5, top=20)` scores every |
| day of the horizon for a pair (ages, weekday/month, the day's outer planets against each chart; 1 January skipped as the |
| dataset's year-only placeholder) and returns the top days plus the best day of each month. Live on the prod page. |
|
|
| It is served live on the prod page: https://artaquest.github.io/artamatch/artamodel.html (Pyodide, nothing leaves the page). |
| `artamodel_iv_stack_deploy.py` builds it; `artamodel_iv_ensemble.py` is the study it came out of. |
|
|
| --- |
| # ArtaModel IV — genderless |
|
|
| **A genderless sidereal phase model of a long-term relationship**, the fourth edition of ArtaModel (Arash |
| Ashrafnejad, ArtaQuest Foundation, 2026-08-19: "I want a genderless model from now on"). Two births and birthplaces |
| — **in no order** — and the date the relationship began, one probability out: did it last thirty years? |
|
|
| ``` |
| y = | b + Σᵢ aᵢ ·e^{i|θ1ᵢ − θ2ᵢ|} the absolute synastry angle (even under the swap) |
| + t1ᵢ·e^{i|θtᵢ − θ1ᵢ|} the wedding sky to partner 1's chart |
| + t2ᵢ·e^{i|θtᵢ − θ2ᵢ|} the wedding sky to partner 2's chart |
| + n1ᵢ·e^{i θ1ᵢ} partner 1's own natal longitude |
| + n2ᵢ·e^{i θ2ᵢ} partner 2's own natal longitude |
| + tnᵢ·e^{i θtᵢ} |² the wedding sky itself |
| ``` |
|
|
| for each of fourteen bodies *i* (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Rāhu, |
| Ketu, Chiron, Lilith). θ are **sidereal longitudes (Lahiri)** from Kerykeion (Swiss Ephemeris): births cast at |
| **09:00 local time at the birthplace** (nobody's birth time is recorded — the dataset's convention), the start at |
| 12:00 UT. **Genderless, three ways:** no sex is read; every phase *difference* enters as its wrapped absolute value |
| |Δθ| ∈ [0°, 180°], so each term is an even function of the swap; the training data carries every pair in both |
| orders; and the scorer averages the two orders, so the answer is identical whichever way the partners are given. |
| **Every long-term relationship** in Wikidata is in the data — marriages of every kind (same-sex included), |
| unmarried partnerships, business and sporting partnerships, "significant person" pairs with family excluded. |
| **A term exists only when both of its phases exist**: an unknown start day drops the wedding-sky terms, an unknown |
| birth drops that partner's terms; a missing phase contributes exactly zero. |
|
|
| ## The deployed model, term by term |
|
|
| Gradient boosting over **split single-sum fields**: each stage is one field `|bₖ + wₖ·e^{iφ}|²` on **one** phasor, |
| chosen greedily as the phasor that best explains the current residual (all 84 phasors of all six terms compete at |
| every stage), added to the logit as `stepₖ·(αₖ·u + cₖ)`. Fitted on **all the data — train and test rows with both |
| natal charts, 35,894 rows (every pair in both orders)** — for 8 stages (the number the |
| train-only fit chose on its inner temporal split). Of the 84 phasors offered it chose **3**: |
|
|
| | phasor | body | term | stages | contribution to the logit swing | phase at which the field peaks | |
| |---|---|---|---|---|---| |
| | `a_uranus` | Uranus | a | 4 | 0.151 | 176° | |
| | `t2_neptune` | Neptune | t2 | 2 | 0.099 | 195° | |
| | `t1_neptune` | Neptune | t1 | 2 | 0.081 | 331° | |
|
|
| Never chosen, at any stage: |
| - **a** (a·e^{i|θ1−θ2|}): 13 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **t1** (t1·e^{i|θt−θ1|}): 13 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **t2** (t2·e^{i|θt−θ2|}): 13 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **n1** (n1·e^{iθ1}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **n2** (n2·e^{iθ2}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **tn** (tn·e^{iθt}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
|
|
| **Read plainly:** `a_uranus` is the absolute gap between the two births measured by Uranus (4.3°/yr); |
| `t1_neptune` and `t2_neptune` are each partner's age at the start measured by Neptune (2.2°/yr) — chosen as a pair, |
| as a genderless model should. No natal phase, no wedding-sky phase, no fast body. |
|
|
| ## What it scores, honestly |
|
|
| | on pairs born after 1900 (temporal hold-out; 7,631 pairs, both orders, symmetrised) | AUC | |
| |---|---| |
| | ArtaModel IV, fitted on train alone (inner split 0.6326, 8 stages) | **0.6252** (public board 0.6101) | |
| | the plain columns — the two ages at the start, the absolute gap, the start year (LightGBM) | 0.6114 (public board 0.6000) | |
| | equal-weight rank average of the two | 0.6289 (public board 0.6144) | |
| | ArtaModel IV with the two ages held flat (AUC within 3-year age cells) | 0.5659 | |
|
|
| What the model reads is the two partners' ages at the start and the absolute gap between their births, through the |
| outer planets as clocks — the same finding as every edition before it, now without a sex anywhere in the model. It |
| is exactly invariant to the ayanāṁśa, the birth hour and the birthplace; the age-cell-matched row is what is left |
| once the ages are held flat. See `ARTAMODEL.md` for the study (editions III and IV). |
|
|
| ## Use |
|
|
| ```python |
| from artamodel_score_iv import predict # needs: numpy, kerykeion, timezonefinder |
| r = predict("1936-08-04", 37.943, 23.647, # partner 1: dob, lat, lon |
| "1924-05-14", 37.727, 26.909, # partner 2 — the order does not matter |
| "1968-06-15") # start date (YYYY-01-01 = year only -> wedding-sky terms dropped) |
| r["probability"], r["terms"], r["terms_swapped"] |
| ``` |
|
|
| `artamodel_iv_deployed.json` holds every stage's weights; `artamodel_iv.py` is the fit (with `artamodel.py`, |
| `artamodel_deploy.py`, `kerykeion_phases.py`); `artamodel_iv.json` the leaderboard numbers. Data: |
| [artaquest-foundation/artamatch-genderless](https://www.kaggle.com/datasets/artaquest-foundation/artamatch-genderless); |
| competition: [artamatch-genderless](https://www.kaggle.com/competitions/artamatch-genderless). CC0. |
|
|
| --- |
|
|
| ## Edition III (superseded 2026-08-19): the gendered model, kept for the record |
|
|
| ### ArtaModel (third edition) |
|
|
| **A sidereal phase model of a marriage**, named by Arash Ashrafnejad (ArtaQuest Foundation, 2026-08-18). Three dates |
| and two places in — his birth and birthplace, hers, and the wedding date — one probability out: did the marriage |
| last thirty years? |
|
|
| ``` |
| y = | b + Σᵢ aᵢ ·e^{i(θmᵢ − θdᵢ)} mom's longitude minus dad's (synastry) |
| + mᵢ ·e^{i(θtᵢ − θmᵢ)} the wedding sky minus mom's chart (transit to mom) |
| + dᵢ ·e^{i(θtᵢ − θdᵢ)} the wedding sky minus dad's chart (transit to dad) |
| + mnᵢ·e^{i θmᵢ} mom's own natal longitude |
| + dnᵢ·e^{i θdᵢ} dad's own natal longitude |
| + tnᵢ·e^{i θtᵢ} |² the wedding sky itself |
| ``` |
|
|
| for each of fourteen bodies *i* (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Rāhu, |
| Ketu, Chiron, Lilith). θ are **sidereal longitudes (Lahiri)** from Kerykeion (Swiss Ephemeris): the births cast at |
| **09:00 local time at the birthplace** (nobody's birth time is recorded — this is the dataset's convention), the |
| wedding at 12:00 UT. **A term exists only when both of its phases exist**: an unknown wedding day drops the wedding |
| terms, an unknown birth drops that partner's terms; a missing phase contributes exactly zero. |
|
|
| ## The deployed model, term by term |
|
|
| The deployed model is **gradient boosting over split single-sum fields**: each stage is one field |
| `|bₖ + wₖ·e^{iφ}|²` on **one** phasor φ, chosen greedily at that stage as the phasor that best explains the |
| current residual (so all 84 phasors of all six terms compete at every stage), and added to the logit as |
| `stepₖ·(αₖ·u + cₖ)`. Fitted on **all the data — train and test rows with both natal charts, 16,802 |
| couples** — for 31 stages (the number the train-only fit chose on its inner temporal split). |
|
|
| Of the 84 phasors offered, the boosting chose **6**. Every one of them is an outer-planet clock: |
|
|
| | phasor | body | term | stages | contribution to the logit swing | phase at which the field peaks | |
| |---|---|---|---|---|---| |
| | `a_uranus` | Uranus | a | 12 | 0.221 | 193° | |
| | `d_pluto` | Pluto | d | 5 | 0.170 | 295° | |
| | `d_neptune` | Neptune | d | 3 | 0.096 | 200° | |
| | `d_uranus` | Uranus | d | 4 | 0.056 | 88° | |
| | `m_pluto` | Pluto | m | 3 | 0.019 | 134° | |
| | `m_saturn` | Saturn | m | 4 | 0.001 | 103° | |
|
|
| Never chosen, at any stage: |
| - **a** (a·e^{i(θm−θd)}): 13 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **m** (m·e^{i(θt−θm)}): 12 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, uranus, neptune, true_node, true_south_node, chiron, mean_lilith |
| - **d** (d·e^{i(θt−θd)}): 11 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, true_node, true_south_node, chiron, mean_lilith |
| - **mn** (mn·e^{iθm}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **dn** (dn·e^{iθd}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
| - **tn** (tn·e^{iθt}): 14 bodies never chosen — sun, moon, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, true_node, true_south_node, chiron, mean_lilith |
|
|
| **Read plainly:** `a_uranus` is the age gap between the two births measured by Uranus (4.3°/yr); `d_pluto`, |
| `d_neptune`, `d_uranus` are the groom's age at the wedding measured by Pluto, Neptune and Uranus; `m_pluto`, |
| `m_saturn` are the bride's. The model uses no natal phase, no wedding-sky phase, and no fast body — the study |
| (`ARTAMODEL.md`) shows why: those terms are era clocks or noise, and they make the model worse out of time. |
|
|
| ## What it scores, honestly |
|
|
| | on couples born after 1900 (temporal hold-out) | AUC | |
| |---|---| |
| | this construction, fitted on train alone (its inner temporal split chose 31 stages) | inner 0.6320 · **held-out ≈ 0.62–0.64** | |
| | the plain columns — two ages at the wedding, the gap, the start year (LightGBM) | 0.6189–0.6371 depending on the row population | |
| | the same model with the two ages held flat (AUC within 3-year age cells) | **≈ 0.50** | |
|
|
| The held-out AUC of ArtaModel is real, and every point of it is the two partners' ages at the wedding and the gap |
| between their births, read through the outer planets as clocks. It is exactly invariant to the ayanāṁśa (a |
| constant offset cancels in a phase difference), to the birth hour and to the birthplace; Uranus alone equals the |
| whole model; the Sun alone scores 0.47; and it adds nothing to a plain model of the ages. This card says so because |
| the study measured it, from every angle, on fixed populations — see `ARTAMODEL.md` and `artamodel_study.json`. |
|
|
| ## Use |
|
|
| ```python |
| from artamodel_score import predict # needs: numpy, kerykeion, timezonefinder |
| r = predict("1936-08-04", 37.943, 23.647, # dad: dob, lat, lon |
| "1924-05-14", 37.727, 26.909, # mom |
| "1968-06-15") # wedding date (YYYY-01-01 = year only -> wedding terms dropped) |
| r["probability"], r["terms"] # the probability, and the stage-by-stage account |
| ``` |
|
|
| `artamodel_deployed.json` holds every stage's weights; `artamodel.py` / `artamodel_ensemble.py` / |
| `artamodel_deploy.py` are the fit; `kerykeion_phases.py` the phase extraction. Data: |
| [artaquest-foundation/artamatch-sidereal](https://www.kaggle.com/datasets/artaquest-foundation/artamatch-sidereal); |
| competition: [artamatch-sidereal](https://www.kaggle.com/competitions/artamatch-sidereal). CC0. |
|
|