σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 10 · Ingénierie Python haute performance›Sujet 10.7·~1 hAvancé

Cython

Position dans le parcoursModule 10 sur 11 · Sujet 7 sur 13
10.7 Cython
—Score du lab— / 100XP total—Série—

Du Python avec des types C, compilé

Cython est un compilateur pour un langage qui est du Python plus des déclarations de types C optionnelles. Vous écrivez un fichier .pyx, Cython le traduit en C, un compilateur C en fait un module d'extension, et vous l'importez comme n'importe quel module. Du code Python simple compilé ainsi va déjà un peu plus vite, mais le vrai gain vient quand on déclare des types : cdef double total dit à Cython que total est un nombre machine, pas un objet Python, si bien que la boucle travaille sur des nombres bruts sans surcoût d'interpréteur. Les boucles numériques lourdes peuvent devenir cinquante à cent fois plus rapides, comme avec Numba, mais on obtient un module compilé ordinaire que l'on peut distribuer.

# What a Cython file looks like: Python with cdef type declarations (shown here as text)
pyx = """\
cpdef double dot(double[:] a, double[:] b):
    cdef double total = 0.0
    cdef long i
    for i in range(a.shape[0]):
        total += a[i] * b[i]
    return total
"""
print(pyx.splitlines()[0])                           # cpdef double dot(double[:] a, double[:] b):
print(sum("cdef" in line for line in pyx.splitlines()))     # 2   two typed declarations inside the function

Déclarer des types : cdef, cpdef et vues mémoire

cdef déclare une variable C (cdef double total, cdef long i) ou une fonction que seul du code C peut appeler. cpdef déclare une fonction appelable depuis Python et depuis C, ce qui convient aux points d'entrée d'un module. def reste une fonction Python ordinaire. Les tableaux se passent en vues mémoire typées, comme double[:] pour un tableau unidimensionnel de doubles : elles acceptent les tableaux NumPy sans copie et s'indexent à la vitesse du C. Une signature comme cpdef double mean(double[:] x, long n) remplace donc les types dynamiques de Python par des types exacts, et un type omis, ou déclaré object, reste un objet Python lent.

# Building the declarations as text: one table from Python type names to C types
C_TYPES = {"int": "long", "float": "double", "bool": "bint", "array[float]": "double[:]"}

def c_type(py_type):
    return C_TYPES.get(py_type, "object")

def cdef_signature(name, params, returns):
    result = "void" if returns is None else c_type(returns)
    arguments = ", ".join(f"{c_type(t)} {p}" for p, t in params)
    return f"cpdef {result} {name}({arguments})"

print(cdef_signature("mean", [("x", "array[float]"), ("n", "int")], "float"))    # cpdef double mean(double[:] x, long n)
print(cdef_signature("log_event", [("message", "dict")], None))                  # cpdef void log_event(object message)

Construire le module et trouver quoi typer

Un setup.py avec cythonize(["pricing.pyx"]) demande à setuptools de traduire et compiler le fichier ; python setup.py build_ext --inplace produit une bibliothèque partagée à côté de votre code, et import pricing la charge. Pour des essais rapides, pyximport compile à l'import. Pour voir ce qui reste lent, lancez cython -a pricing.pyx : il écrit une page HTML où chaque ligne est teintée de jaune selon la quantité de machinerie Python qu'elle utilise, et les lignes blanches sont du C pur. Typez les variables des lignes jaunes dans vos boucles critiques, puis désactivez les contrôles dont vous avez prouvé l'inutilité avec @cython.boundscheck(False) et @cython.wraparound(False) pour que l'indexation coûte aussi peu qu'en C.

# The build script is a few lines of text; this generates it for a list of modules
def build_setup(modules):
    files = ", ".join(f'"{module}.pyx"' for module in modules)
    return "from setuptools import setup\nfrom Cython.Build import cythonize\n\n" + f"setup(ext_modules=cythonize([{files}]))\n"

print(build_setup(["pricing", "risk"]), end="")
# from setuptools import setup
# from Cython.Build import cythonize
#
# setup(ext_modules=cythonize(["pricing.pyx", "risk.pyx"]))
# The loop a .pyx file would hold, as plain Python: same result, compiled once types are added
def dot(a, b):
    total = 0.0
    for i in range(len(a)):
        total += a[i] * b[i]
    return total

prices = [184.25, 410.5, 99.0]
quantities = [100.0, 50.0, 200.0]
print(dot(prices, quantities))                       # 58750.0  the compiled version returns exactly this number
print(dot(prices, quantities) == sum(p * q for p, q in zip(prices, quantities)))     # True
Cython, Numba ou aucun des deux ?Numba est le choix léger : un décorateur, pas d'étape de compilation, idéal pour les fonctions numériques. Cython coûte une étape de build et un second type de fichier mais donne un module normal que l'on distribue sans démarrage JIT, et permet d'appeler des bibliothèques C et de mêler librement code C et Python. Essayez d'abord la vectorisation NumPy, puis Numba, puis Cython ; et dans tous les cas comparez la sortie à la version Python simple avant de lui faire confiance.
  • Cython compile un fichier .pyx (Python plus des types C optionnels) en C puis en module d'extension que l'on import normalement.
  • cdef déclare des variables C et des fonctions réservées à C, cpdef une fonction appelable depuis Python et C ; les tableaux sont des vues mémoire typées comme double[:].
  • Construisez avec cythonize dans setup.py, utilisez cython -a pour trouver les lignes jaunes (lentes) des boucles critiques, et vérifiez avec le résultat Python simple.

À suivre : appeler du C++ avec pybind11, exposer du code C++ existant à Python.

Lab : ce qui est noté

  • Associer types Python et types C avec c_type25 pts
  • Écrire l'en-tête d'une fonction avec cdef_signature25 pts
  • Typer les variables locales avec declare_locals25 pts
  • Écrire le script de compilation avec build_setup25 pts
Explorateur · lab-123-1
▾ lab-123-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-123-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-123-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub