Adding operator Ansätze

This commit is contained in:
Julian Piribauer
2026-08-15 20:58:11 +02:00
parent 0de52cf0ee
commit 76cd797243
2 changed files with 75 additions and 21 deletions
+58 -20
View File
@@ -1,6 +1,8 @@
import logging import logging
from sage.all import sage_eval, PolynomialRing, QQ, SR, log, var from sage.all import sage_eval, PolynomialRing, QQ, SR, log, var
load("sage/util.py")
# Logger # Logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
@@ -10,25 +12,66 @@ class PFOperator:
A class representing a Picard-Fuchs operator in a single variable z. A class representing a Picard-Fuchs operator in a single variable z.
Independent of the given order, the variables are assumed to be left of Independent of the given order, the variables are assumed to be left of
the derivatives. the derivatives.
The variables extra_locals are needed for symbolic coefficients used for
operator Ansätze.
""" """
def _operator_from_string(self, operator_string: str) -> "sage.rings.polynomial.polynomial_ring.Polynomial": def _operator_from_string(self, operator_string: str, extra_locals: dict = None):
z_names = ['z%d' % i for i in range(self.no_variables)] z_names = ['z%d' % i for i in range(self.no_variables)]
theta_names = ['theta%d' % i for i in range(self.no_variables)] theta_names = ['theta%d' % i for i in range(self.no_variables)]
self.ring = PolynomialRing(QQ, z_names + theta_names) base_ring = SR if extra_locals else QQ
self.ring = PolynomialRing(base_ring, z_names + theta_names)
self.z_gens = self.ring.gens()[:self.no_variables] self.z_gens = self.ring.gens()[:self.no_variables]
self.theta_gens = self.ring.gens()[self.no_variables:] self.theta_gens = self.ring.gens()[self.no_variables:]
parse_locals = dict(self.ring.gens_dict())
if extra_locals:
parse_locals.update(extra_locals)
try: try:
operator = sage_eval(operator_string, locals=self.ring.gens_dict()) operator = sage_eval(operator_string, locals=parse_locals)
return operator
except Exception as e: except Exception as e:
raise ValueError("Invalid operator string: %s" % e) raise ValueError("Invalid operator string: %s" % e)
return self.ring(operator)
def __init__(self, operator_string: str, no_variables: int = 1): def __init__(self, operator_string: str, no_variables: int = 1, extra_locals: dict = None):
self.no_variables = no_variables self.no_variables = no_variables
self.operator = self._operator_from_string(operator_string) self.operator_string = operator_string
self.operator = self._operator_from_string(operator_string, extra_locals=extra_locals)
logger.info("Initialised PFOperator: %s", self.operator) logger.info("Initialised PFOperator: %s", self.operator)
class PFOperatorAnsatz(PFOperator):
"""
A class for Picard-Fuchs operator Ansätze, characterised by number of variables and their z- and theta-multi-degrees.
"""
def __init__(self, no_variables: int, theta_degree: int, z_degree: int):
self.no_variables = no_variables
self.theta_degree = theta_degree
self.z_degree = z_degree
z_indices = _multi_indices(self.no_variables, z_degree)
theta_indices = _multi_indices(self.no_variables, theta_degree)
# Every (z_index, theta_index) pair allowed by the degree bounds gets its own
# fresh unknown, to be solved for once the Ansatz is applied to a period.
operator_terms = [
(var("b_" + "_".join(str(x) for x in z_index + theta_index)), (z_index, theta_index))
for z_index in z_indices
for theta_index in theta_indices
]
def _monomial_factors(index, name):
return [f"{name}{i}^{exp}" for i, exp in enumerate(index) if exp > 0]
operator_string = " + ".join(
"*".join([str(coeff), *_monomial_factors(z_index, "z"), *_monomial_factors(theta_index, "theta")])
for coeff, (z_index, theta_index) in operator_terms
)
extra_locals = {str(coeff): coeff for coeff, _ in operator_terms}
super().__init__(operator_string=operator_string, no_variables=no_variables, extra_locals=extra_locals)
class Period: class Period:
""" """
A class representing a period as a formal power series in z-variables and their logs. A class representing a period as a formal power series in z-variables and their logs.
@@ -211,25 +254,13 @@ class PeriodAnsatz(Period):
A class for period Ansätze, characterised by number of variables and their z- and log-multi-degrees. A class for period Ansätze, characterised by number of variables and their z- and log-multi-degrees.
""" """
def _multi_indices(self, total_degree: int) -> list:
# All no_variables-tuples of non-negative integers whose entries sum to at most total_degree.
def helper(remaining_vars, remaining_degree):
if remaining_vars == 0:
yield ()
return
for first in range(remaining_degree + 1):
for rest in helper(remaining_vars - 1, remaining_degree - first):
yield (first,) + rest
return list(helper(self.no_variables, total_degree))
def __init__(self, no_variables: int, z_degree: int, log_degree: int): def __init__(self, no_variables: int, z_degree: int, log_degree: int):
self.no_variables = no_variables self.no_variables = no_variables
self.z_degree = z_degree self.z_degree = z_degree
self.log_degree = log_degree self.log_degree = log_degree
log_indices = self._multi_indices(log_degree) log_indices = _multi_indices(self.no_variables, log_degree)
z_indices = self._multi_indices(z_degree) z_indices = _multi_indices(self.no_variables, z_degree)
# Every (log_index, z_index) pair allowed by the degree bounds gets its own # Every (log_index, z_index) pair allowed by the degree bounds gets its own
# fresh unknown, to be solved for once a PFOperator is applied to the Ansatz. # fresh unknown, to be solved for once a PFOperator is applied to the Ansatz.
@@ -247,3 +278,10 @@ class PeriodAnsatz(Period):
"Initialised PeriodAnsatz with %d unknown coefficient(s).", "Initialised PeriodAnsatz with %d unknown coefficient(s).",
sum(len(z_dict) for z_dict in self.expansion_coefficients.values()), sum(len(z_dict) for z_dict in self.expansion_coefficients.values()),
) )
"""
To do:
- add method to Period that finds operators that annihilate it (using operator ansatz class)
- add class PFideal containing a list of PFOperators
"""
+16
View File
@@ -0,0 +1,16 @@
"""
Utility functions for the Calabi-Yau period geometry project.
"""
def _multi_indices(no_variables, total_degree: int) -> list:
# All no_variables-tuples of non-negative integers whose entries sum to at most total_degree.
def helper(remaining_vars, remaining_degree):
if remaining_vars == 0:
yield ()
return
for first in range(remaining_degree + 1):
for rest in helper(remaining_vars - 1, remaining_degree - first):
yield (first,) + rest
return list(helper(no_variables, total_degree))