#!/usr/bin/env python3 # Konvexitaet_Demo.py """ Kapitel Fundament: Konvexität praktisch erfahrbar machen. Teil 1: Die Sehnen-Bedingung numerisch nachprüfen. Teil 2: Zeigen, warum lokale Suche bei nicht-konvexen Funktionen scheitert. """ import numpy as np from scipy.optimize import minimize # --- Teil 1: Sehnen-Test --------------------------------------------------- def ist_konvex_numerisch(f, unten, oben, proben=2000, seed=0): """ Prüft die Konvexitätsdefinition an zufälligen Punktepaaren: f(theta*x + (1-theta)*y) <= theta*f(x) + (1-theta)*f(y) ? Findet Gegenbeispiele - beweist aber KEINE Konvexität. """ rng = np.random.default_rng(seed) schlimmste_verletzung = 0.0 for _ in range(proben): x, y = rng.uniform(unten, oben, size=2) theta = rng.uniform(0.0, 1.0) links = f(theta * x + (1 - theta) * y) rechts = theta * f(x) + (1 - theta) * f(y) schlimmste_verletzung = max(schlimmste_verletzung, links - rechts) return schlimmste_verletzung funktionen = { "f(x) = x^2 (konvex)": lambda x: x ** 2, "f(x) = |x| (konvex)": lambda x: abs(x), "f(x) = e^x (konvex)": lambda x: np.exp(x), "f(x) = x^3 (NICHT konvex)": lambda x: x ** 3, "f(x) = x^2+3sin(3x) (NICHT konvex)": lambda x: x ** 2 + 3 * np.sin(3 * x), } print("=" * 68) print(" TEIL 1: SEHNEN-TEST (positive Zahl = Konvexität verletzt)") print("=" * 68) for name, f in funktionen.items(): verletzung = ist_konvex_numerisch(f, -3.0, 3.0) urteil = "konvex (keine Verletzung gefunden)" if verletzung < 1e-9 \ else f"NICHT konvex (Verletzung bis {verletzung:.3f})" print(f"{name:<34} -> {urteil}") # --- Teil 2: Lokale Suche von verschiedenen Startpunkten ------------------- def wellige_funktion(x): """Nicht-konvex: eine Parabel mit aufmodulierter Welle -> viele lokale Minima.""" return x[0] ** 2 + 3.0 * np.sin(3.0 * x[0]) print("\n" + "=" * 68) print(" TEIL 2: LOKALE SUCHE BEI NICHT-KONVEXER FUNKTION") print("=" * 68) print(f"{'Startpunkt':>12} | {'gefundenes Minimum':>20} | {'Funktionswert':>14}") print("-" * 68) ergebnisse = [] for start in [-3.0, -1.5, 0.0, 1.5, 3.0]: res = minimize(wellige_funktion, x0=[start], method="BFGS") ergebnisse.append((start, res.x[0], res.fun)) print(f"{start:>12.1f} | {res.x[0]:>20.4f} | {res.fun:>14.4f}") bester = min(ergebnisse, key=lambda t: t[2]) print("-" * 68) print(f"Je nach Startpunkt landet derselbe Algorithmus in " f"{len({round(e[1], 3) for e in ergebnisse})} verschiedenen Minima.") print(f"Das beste gefundene: x = {bester[1]:.4f} mit f = {bester[2]:.4f} " f"(Start bei {bester[0]:.1f})") print("Bei einer KONVEXEN Funktion waeren alle Zeilen identisch --") print("der Startpunkt waere voellig gleichgueltig.") print("=" * 68)