Adding operator Ansätze
This commit is contained in:
@@ -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
|
||||||
|
"""
|
||||||
@@ -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))
|
||||||
|
|
||||||
Reference in New Issue
Block a user