TEORİDEN UYGULAMAYA · PYTHON EŞLİKÇİSİ

Sayısal Kararlılık ve Geometrik Model Uydurma

Prof. Dr. Zühal Küçükarslan Yüzbaşı · Prof. Dr. Bahadır Yüzbaşı

Bölüm 23 · v1.0 · Türkçe

Bu bölümün laboratuvarı, kitaptaki örnekleri yeniden üretmek ve sayısal sonuçları incelemek için hazırlanmıştır. Matematiksel tanımlar, ispatlar ve sorular kitapta yer alır.

Laboratuvarı indir (.py) Tam Python paketi (.zip)

Laboratuvar kodu

"""Bölüm 23: sayısal kararlılık ve geometrik model uydurma laboratuvarı.

Sabit tohumlu sentetik bir ölçüm plakası üzerinde doğru, çember, elips ve
elipsoid uydurulur. Veriler gerçek ölçüm değildir; bütün uzunluklar mm'dir.
Tek kirletilmiş çember noktası açıkça korunur ve sağlam kayıp yalnız belirtilen
ölçekle etkinleştirilir. Hiçbir gözlem silinmez veya yeniden kodlanmaz.
"""

from __future__ import annotations

import math

import numpy as np

from agbook import (
    canonical_array_sha256,
    fit_circle_geometric,
    fit_ellipse_direct,
    fit_line_tls,
    fit_line_vertical_ols,
    fit_quadric_implicit,
)


def rotation_2d(angle: float) -> np.ndarray:
    cosine, sine = math.cos(angle), math.sin(angle)
    return np.array([[cosine, -sine], [sine, cosine]])


def rotation_xyz(ax: float, ay: float, az: float) -> np.ndarray:
    cx, sx = math.cos(ax), math.sin(ax)
    cy, sy = math.cos(ay), math.sin(ay)
    cz, sz = math.cos(az), math.sin(az)
    rx = np.array([[1, 0, 0], [0, cx, -sx], [0, sx, cx]], dtype=float)
    ry = np.array([[cy, 0, sy], [0, 1, 0], [-sy, 0, cy]], dtype=float)
    rz = np.array([[cz, -sz, 0], [sz, cz, 0], [0, 0, 1]], dtype=float)
    return rz @ ry @ rx


def main() -> None:
    seed = 2301
    rng = np.random.default_rng(seed)
    length_unit = "mm"

    line_direction = np.array([0.82, 0.5723635208501674])
    line_normal = np.array([-line_direction[1], line_direction[0]])
    line_parameter = np.linspace(-55.0, 55.0, 70)
    line_points = np.array([-25.0, 18.0]) + line_parameter[:, None] * line_direction
    line_points += rng.normal(scale=0.35, size=(line_parameter.size, 1)) * line_normal
    line_tls = fit_line_tls(line_points)
    line_ols = fit_line_vertical_ols(line_points)

    circle_center = np.array([42.0, -28.0])
    circle_radius = 24.0
    circle_theta = np.linspace(0.0, 2.0 * np.pi, 72, endpoint=False)
    circle_radial = circle_radius + rng.normal(scale=0.28, size=circle_theta.size)
    circle_points = circle_center + circle_radial[:, None] * np.column_stack(
        (np.cos(circle_theta), np.sin(circle_theta))
    )
    contaminated_index = 9
    circle_points[contaminated_index] += np.array([11.0, -7.0])
    circle_least_squares = fit_circle_geometric(
        circle_points,
        measurement_sigma=0.28,
    )
    circle_robust = fit_circle_geometric(
        circle_points,
        loss="soft_l1",
        loss_scale=0.6,
    )

    ellipse_center = np.array([-36.0, 31.0])
    ellipse_axes = np.array([30.0, 13.0])
    ellipse_rotation = rotation_2d(math.radians(27.0))
    ellipse_theta = np.linspace(0.0, 2.0 * np.pi, 100, endpoint=False)
    ellipse_local = np.column_stack(
        (ellipse_axes[0] * np.cos(ellipse_theta), ellipse_axes[1] * np.sin(ellipse_theta))
    )
    ellipse_points = ellipse_local @ ellipse_rotation.T + ellipse_center
    ellipse_points += rng.normal(scale=0.18, size=ellipse_points.shape)
    ellipse_fit = fit_ellipse_direct(ellipse_points)

    ellipsoid_center = np.array([18.0, -12.0, 9.0])
    ellipsoid_axes = np.array([21.0, 14.0, 8.0])
    ellipsoid_rotation = rotation_xyz(0.22, -0.31, 0.47)
    azimuth = np.linspace(0.0, 2.0 * np.pi, 18, endpoint=False)
    polar = np.linspace(0.22, np.pi - 0.22, 9)
    aa, pp = np.meshgrid(azimuth, polar)
    ellipsoid_local = np.column_stack(
        (
            ellipsoid_axes[0] * np.sin(pp.ravel()) * np.cos(aa.ravel()),
            ellipsoid_axes[1] * np.sin(pp.ravel()) * np.sin(aa.ravel()),
            ellipsoid_axes[2] * np.cos(pp.ravel()),
        )
    )
    ellipsoid_points = ellipsoid_local @ ellipsoid_rotation.T + ellipsoid_center
    ellipsoid_points += rng.normal(scale=0.12, size=ellipsoid_points.shape)
    quadric_fit = fit_quadric_implicit(ellipsoid_points)

    assert abs(line_tls.direction @ line_direction) > 0.99999
    assert line_tls.rms_orthogonal < 0.45
    assert circle_least_squares.final_sum_squares <= circle_least_squares.initial_sum_squares
    assert np.linalg.norm(circle_robust.center - circle_center) < np.linalg.norm(
        circle_least_squares.center - circle_center
    )
    assert abs(circle_robust.radius - circle_radius) < abs(
        circle_least_squares.radius - circle_radius
    )
    assert ellipse_fit.conic_diagnostics.locus_type == "ellipse"
    assert quadric_fit.quadric_diagnostics.locus_type == "ellipsoid"
    np.testing.assert_allclose(
        ellipse_fit.conic_diagnostics.center,
        ellipse_center,
        atol=0.15,
    )
    np.testing.assert_allclose(
        np.sort(ellipse_fit.conic_diagnostics.semi_axes),
        np.sort(ellipse_axes),
        atol=0.2,
    )
    np.testing.assert_allclose(
        quadric_fit.quadric_diagnostics.center,
        ellipsoid_center,
        atol=0.12,
    )
    np.testing.assert_allclose(
        np.sort(quadric_fit.quadric_diagnostics.semi_axes),
        np.sort(ellipsoid_axes),
        atol=0.15,
    )

    signature_values = np.concatenate(
        (
            line_points.ravel(),
            line_tls.coefficients,
            [line_tls.rms_orthogonal, line_ols.rms_vertical],
            circle_points.ravel(),
            circle_least_squares.center,
            [circle_least_squares.radius, circle_least_squares.rms_geometric],
            circle_robust.center,
            [circle_robust.radius, circle_robust.rms_geometric],
            ellipse_points.ravel(),
            ellipse_fit.coefficients,
            ellipse_fit.conic_diagnostics.center,
            ellipse_fit.conic_diagnostics.semi_axes,
            ellipsoid_points.ravel(),
            quadric_fit.coefficients,
            quadric_fit.quadric_diagnostics.center,
            quadric_fit.quadric_diagnostics.semi_axes,
        )
    )

    print("tohum:", seed)
    print("uzunluk_birimi:", length_unit)
    print("veri: sentetik_olcum_plakasi")
    print("olculmus_veri_mi: False")
    print("eksik_deger_islemi: yok")
    print("gozlem_silindi_mi: False")
    print("dogru_tls_katsayilari:", line_tls.coefficients)
    print("dogru_tls_rms_mm:", f"{line_tls.rms_orthogonal:.9f}")
    print("dogru_ols_dikey_rms_mm:", f"{line_ols.rms_vertical:.9f}")
    print("cember_kirletilmis_indis:", contaminated_index)
    print("cember_ls_merkez_mm:", circle_least_squares.center)
    print("cember_ls_yaricap_mm:", f"{circle_least_squares.radius:.9f}")
    print("cember_ls_standart_hatalar_mm:", circle_least_squares.standard_errors)
    print("cember_robust_merkez_mm:", circle_robust.center)
    print("cember_robust_yaricap_mm:", f"{circle_robust.radius:.9f}")
    print("cember_robust_kayip:", circle_robust.loss)
    print("cember_robust_kayip_olcegi_mm:", circle_robust.loss_scale)
    print("elips_sinifi:", ellipse_fit.conic_diagnostics.locus_type)
    print("elips_merkez_mm:", ellipse_fit.conic_diagnostics.center)
    print("elips_yari_eksenler_mm:", ellipse_fit.conic_diagnostics.semi_axes)
    print("elips_tasarim_ranki:", ellipse_fit.diagnostics.effective_rank)
    print("elips_nullspace_araligi:", ellipse_fit.diagnostics.nullspace_gap)
    print("kuadrik_sinifi:", quadric_fit.quadric_diagnostics.locus_type)
    print("kuadrik_merkez_mm:", quadric_fit.quadric_diagnostics.center)
    print("kuadrik_yari_eksenler_mm:", quadric_fit.quadric_diagnostics.semi_axes)
    print("kuadrik_tasarim_ranki:", quadric_fit.diagnostics.effective_rank)
    print("kuadrik_nullspace_araligi:", quadric_fit.diagnostics.nullspace_gap)
    print("bilimsel_imza:", canonical_array_sha256(signature_values))


if __name__ == "__main__":
    main()
Doğrulama çalıştırmasının çıktısı
tohum: 2301
uzunluk_birimi: mm
veri: sentetik_olcum_plakasi
olculmus_veri_mi: False
eksik_deger_islemi: yok
gozlem_silindi_mi: False
dogru_tls_katsayilari: [ -0.57358722   0.81914449 -29.07421489]
dogru_tls_rms_mm: 0.348263749
dogru_ols_dikey_rms_mm: 0.425143271
cember_kirletilmis_indis: 9
cember_ls_merkez_mm: [ 42.1592895  -27.90317228]
cember_ls_yaricap_mm: 24.060698726
cember_ls_standart_hatalar_mm: [0.04641723 0.04692385 0.03300004]
cember_robust_merkez_mm: [ 42.02769068 -27.95747329]
cember_robust_yaricap_mm: 23.999879324
cember_robust_kayip: soft_l1
cember_robust_kayip_olcegi_mm: 0.6
elips_sinifi: ellipse
elips_merkez_mm: [-35.98431752  30.98696831]
elips_yari_eksenler_mm: [29.96742543 12.9971687 ]
elips_tasarim_ranki: 6
elips_nullspace_araligi: 47.21158044468563
kuadrik_sinifi: ellipsoid
kuadrik_merkez_mm: [ 17.9954167  -12.02247577   8.98762276]
kuadrik_yari_eksenler_mm: [21.01573467 14.03668937  8.00341117]
kuadrik_tasarim_ranki: 10
kuadrik_nullspace_araligi: 31.386652518643867
bilimsel_imza: 1cf21d5b37678090855dbbb478b2755e957478709c26a6fa05a57f59afedf595
Metindeki Python kodları

Kod dosyasını indir

# Kitaptaki kod blokları; bu dosya içinde sırayla çalıştırılır.
# --- 1. Doğru, çember, elips ve elipsoid uydurma ---
import runpy

runpy.run_path(
    "labs/bolum_23_sayisal_kararlilik_model_uydurma.py",
    run_name="__main__",
)

Kaydedilen çıktı

1. Doğru, çember, elips ve elipsoid uydurma
tohum: 2301
uzunluk_birimi: mm
veri: sentetik_olcum_plakasi
olculmus_veri_mi: False
eksik_deger_islemi: yok
gozlem_silindi_mi: False
dogru_tls_katsayilari: [ -0.57358722   0.81914449 -29.07421489]
dogru_tls_rms_mm: 0.348263749
dogru_ols_dikey_rms_mm: 0.425143271
cember_kirletilmis_indis: 9
cember_ls_merkez_mm: [ 42.1592895  -27.90317228]
cember_ls_yaricap_mm: 24.060698726
cember_ls_standart_hatalar_mm: [0.04641723 0.04692385 0.03300004]
cember_robust_merkez_mm: [ 42.02769068 -27.95747329]
cember_robust_yaricap_mm: 23.999879324
cember_robust_kayip: soft_l1
cember_robust_kayip_olcegi_mm: 0.6
elips_sinifi: ellipse
elips_merkez_mm: [-35.98431752  30.98696831]
elips_yari_eksenler_mm: [29.96742543 12.9971687 ]
elips_tasarim_ranki: 6
elips_nullspace_araligi: 47.21158044468563
kuadrik_sinifi: ellipsoid
kuadrik_merkez_mm: [ 17.9954167  -12.02247577   8.98762276]
kuadrik_yari_eksenler_mm: [21.01573467 14.03668937  8.00341117]
kuadrik_tasarim_ranki: 10
kuadrik_nullspace_araligi: 31.386652518643867
bilimsel_imza: 1cf21d5b37678090855dbbb478b2755e957478709c26a6fa05a57f59afedf595
Çözümlerdeki Python kodları

Kod dosyasını indir

# Kitaptaki kod blokları; bu dosya içinde sırayla çalıştırılır.
# --- 1. TLS için dönme ve ölçek metamorfik testi ---
import numpy as np
from agbook import fit_line_tls

rng = np.random.default_rng(2311)
t = np.linspace(-4, 4, 50)
v = np.array([0.8, 0.6])
n = np.array([-0.6, 0.8])
points = t[:, None]*v + rng.normal(0, 0.04, (50, 1))*n
base = fit_line_tls(points)

for angle in np.linspace(0, 2*np.pi, 20, endpoint=False):
    c, s = np.cos(angle), np.sin(angle)
    R = np.array([[c, -s], [s, c]])
    moved = 7*(points @ R.T) + np.array([12.0, -5.0])
    got = fit_line_tls(moved)
    assert abs(got.direction @ (R @ base.direction)) > 1-1e-12
    np.testing.assert_allclose(
        got.rms_orthogonal, 7*base.rms_orthogonal, rtol=2e-12
    )

# --- 2. Çember geometrik amacının başlangıcı iyileştirmesi ---
import numpy as np
from agbook import fit_circle_geometric

rng = np.random.default_rng(2312)
theta = np.linspace(0, 2*np.pi, 80, endpoint=False)
radial = 5 + rng.normal(0, 0.12, theta.size)
points = np.array([3.0, -2.0]) + radial[:, None]*np.c_[
    np.cos(theta), np.sin(theta)
]
fit = fit_circle_geometric(points)
assert fit.final_sum_squares <= fit.initial_sum_squares + 1e-12
print(fit.initial_sum_squares, fit.final_sum_squares)

# --- 3. Elips yaylarında nullspace ayrışması ---
import numpy as np
from agbook import fit_conic_implicit

for degrees in (360, 90, 30, 10):
    width = np.deg2rad(degrees)
    theta = np.linspace(-width/2, width/2, 120)
    points = np.c_[4*np.cos(theta), 1.5*np.sin(theta)]
    fit = fit_conic_implicit(points)
    print(
        degrees,
        fit.diagnostics.singular_values,
        fit.diagnostics.nullspace_gap,
        fit.conic_diagnostics.locus_type,
    )

# --- 4. Elipsoid uydurmasının benzerlik testi ---
import numpy as np
from agbook import fit_quadric_implicit

azimuth, polar = np.meshgrid(
    np.linspace(0, 2*np.pi, 24, endpoint=False),
    np.linspace(0.15, np.pi-0.15, 12),
)
points = np.c_[
    5*np.sin(polar.ravel())*np.cos(azimuth.ravel()),
    3*np.sin(polar.ravel())*np.sin(azimuth.ravel()),
    2*np.cos(polar.ravel()),
] + np.array([1., -2., 3.])
base = fit_quadric_implicit(points)
Q, _ = np.linalg.qr(np.random.default_rng(2314).normal(size=(3, 3)))
if np.linalg.det(Q) < 0:
    Q[:, -1] *= -1
t = np.array([7.0, -4.0, 2.0])
moved_points = 1000*(points @ Q.T + t)
moved = fit_quadric_implicit(moved_points)

np.testing.assert_allclose(
    moved.quadric_diagnostics.center,
    1000*(Q @ base.quadric_diagnostics.center + t),
    rtol=2e-10,
)
np.testing.assert_allclose(
    np.sort(moved.quadric_diagnostics.semi_axes),
    1000*np.sort(base.quadric_diagnostics.semi_axes),
    rtol=2e-10,
)
assert moved.quadric_diagnostics.locus_type == \
       base.quadric_diagnostics.locus_type

# --- 5. Tam elipsoid ile dar kutup yamasını karşılaştırma ---
import numpy as np
from agbook import fit_quadric_implicit

rng = np.random.default_rng(2344)
azimuth = np.linspace(0, 2*np.pi, 24, endpoint=False)
noise = rng.normal(0, 0.002, (288, 3))

def sampled_ellipsoid(polar):
    aa, pp = np.meshgrid(azimuth, polar)
    return np.c_[
        5*np.sin(pp.ravel())*np.cos(aa.ravel()),
        3*np.sin(pp.ravel())*np.sin(aa.ravel()),
        2*np.cos(pp.ravel()),
    ] + np.array([1., -2., 3.]) + noise

full_points = sampled_ellipsoid(np.linspace(0.15, np.pi-0.15, 12))
polar_patch_points = sampled_ellipsoid(np.linspace(0.15, 0.35, 12))
full = fit_quadric_implicit(full_points)
patch = fit_quadric_implicit(polar_patch_points)

for name, fit in (("tam", full), ("yama", patch)):
    print(name)
    print("tekil_degerler", fit.diagnostics.singular_values)
    print("rank", fit.diagnostics.effective_rank)
    print("nullspace_boslugu", fit.diagnostics.nullspace_gap)
    print("sinif", fit.quadric_diagnostics.locus_type)
    print("merkez", fit.quadric_diagnostics.center)
    print("yari_eksenler", fit.quadric_diagnostics.semi_axes)

Kaydedilen çıktı

1. TLS için dönme ve ölçek metamorfik testi

2. Çember geometrik amacının başlangıcı iyileştirmesi
1.3217054302761628 1.32138108617027

3. Elips yaylarında nullspace ayrışması
360 [2.53298316e+01 1.45197717e+01 6.57910352e+00 5.40528459e+00
 5.03625969e+00 1.25437653e-15] 4014950499731706.5 ellipse
90 [2.47535967e+01 1.62992267e+01 9.02669056e+00 6.75496709e+00
 4.37600164e+00 2.78606180e-15] 1570676443350304.2 ellipse
30 [2.96641145e+01 1.56416891e+01 7.31430141e+00 3.30259680e+00
 6.03693113e-01 8.69720869e-15] 69412283154415.77 ellipse
10 [3.04667377e+01 1.55089458e+01 7.05477833e+00 1.15798514e+00
 6.86729316e-02 2.60677137e-14] 2634405628283.9546 ellipse

4. Elipsoid uydurmasının benzerlik testi

5. Tam elipsoid ile dar kutup yamasını karşılaştırma
tam
tekil_degerler [4.75154453e+01 2.27347343e+01 1.85702071e+01 1.57154789e+01
 1.36423146e+01 1.26893321e+01 1.21219589e+01 1.21049451e+01
 7.26631900e+00 1.87092589e-02]
rank 10
nullspace_boslugu 388.38090910571316
sinif ellipsoid
merkez [ 0.99966798 -2.00004857  3.00031982]
yari_eksenler [4.99869401 3.0002509  2.00012267]
yama
tekil_degerler [5.26285905e+01 2.51974713e+01 1.90768622e+01 1.75086802e+01
 1.51268888e+01 6.24873857e+00 1.24289140e+00 7.46972412e-01
 5.11303654e-02 3.97049382e-02]
rank 10
nullspace_boslugu 1.287758342445121
sinif ellipsoid
merkez [ 0.99864635 -2.00102972  4.90166561]
yari_eksenler [1.57396546 0.94792143 0.07642433]

Çalıştırma rehberi

Önce tam Python paketini indirin ve arşivi açın. Aşağıdaki komutları arşivin üst klasöründen başlatın. İlk kurulum internet bağlantısı ve Python 3.12 veya 3.13 gerektirir. Bağımlılıklar sabittir; sistem Python'unu değiştirmemek için ayrı sanal ortam kullanılır.

macOS / Linux
cd analitik-geometri-python-v1.0
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
python labs/bolum_23_sayisal_kararlilik_model_uydurma.py
python -m pytest
Windows PowerShell
cd analitik-geometri-python-v1.0
py -3.13 -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
.venv\Scripts\python.exe labs/bolum_23_sayisal_kararlilik_model_uydurma.py
.venv\Scripts\python.exe -m pytest

Laboratuvar dosyaları agbook yardımcı paketini kullanır; tek bir dosyayı indirmek paketi kurmanın yerini tutmaz. Metin/çözüm kodları kendi dosyaları içinde sırayla çalıştırılır; gizli bir notebook oturumu gerekmez. Son ondalık basamaklar platforma göre değişebilir.