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
+59 -21
View File
@@ -1,6 +1,8 @@
import logging
from sage.all import sage_eval, PolynomialRing, QQ, SR, log, var
load("sage/util.py")
# Logger
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
@@ -10,25 +12,66 @@ class PFOperator:
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
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)]
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.theta_gens = self.ring.gens()[self.no_variables:]
parse_locals = dict(self.ring.gens_dict())
if extra_locals:
parse_locals.update(extra_locals)
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:
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.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)
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:
"""
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.
"""
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):
self.no_variables = no_variables
self.z_degree = z_degree
self.log_degree = log_degree
log_indices = self._multi_indices(log_degree)
z_indices = self._multi_indices(z_degree)
log_indices = _multi_indices(self.no_variables, log_degree)
z_indices = _multi_indices(self.no_variables, z_degree)
# 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.
@@ -246,4 +277,11 @@ class PeriodAnsatz(Period):
logger.info(
"Initialised PeriodAnsatz with %d unknown coefficient(s).",
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))