DGCV 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of DGCV might be problematic. Click here for more details.

DGCV/CRGeometry.py ADDED
@@ -0,0 +1,175 @@
1
+ ############## dependencies
2
+ from sympy import eye, expand, Matrix, numer, poly_from_expr, solve, Transpose, Symbol
3
+ from functools import reduce
4
+ from .config import _cached_caller_globals
5
+ from .combinatorics import *
6
+ from .classesAndVariables import *
7
+ from .vectorFieldsAndDifferentialForms import *
8
+ from .polynomials import *
9
+ from ._safeguards import retrieve_passkey
10
+
11
+
12
+ ############## CR geometry
13
+
14
+ def tangencyObstruction(arg1,arg2,arg3,*args):
15
+ """
16
+ Computes the Lie derivative of a holomorphic vector field's real part applied to a graph defining a CR hypersurface, then substitutes the defining equation into the result.
17
+
18
+ Returns 0 (up to simplification) if and only if the vector field is a symmetry of the hypersurface.
19
+
20
+ Arguments must be expressions w.r.t. a complex coordinate system initialized by *complexVarProc*
21
+
22
+ Args:
23
+ arg1: VFClass class instance in the form of a holomorphic vector field (for example, a VF initialized by *assembleFromHolVFC*)
24
+ arg2: Defining function of CR hypersurface in holomorphic or real variables format (i.e., a real-valued function of the complex parameter space depending on some but not all of the real and imaginaray parts of the complex variables)
25
+ arg3: The real variable that when set equal to the defining function defines the CR hypersurface.
26
+
27
+ Returns:
28
+ A sympy expression.
29
+
30
+ Raises:
31
+ NA
32
+ """
33
+ if isinstance(arg1,VFClass):
34
+ if arg1.DGCVType == 'standard':
35
+ raise TypeError('`tangencyObstruction` requires its first argument `vf` to be VFClass with vf.DGCVType=\'complex\'')
36
+ else:
37
+ raise TypeError('`tangencyObstruction` requires its first argument `vf` to be VFClass with vf.DGCVType=\'complex\'')
38
+ arg1=allToReal(arg1)
39
+ evaluationLoc=allToReal(realPartOfVF(arg1)(holToReal(arg3-arg2))).subs(holToReal(symToReal(arg3)),symToReal(arg2))
40
+ return simplify(symToReal(evaluationLoc))
41
+
42
+ def weightedHomogeneousVF(arg1,arg2,arg3,arg4,degreeCap=0,_tempVar=None,assumeReal=None):
43
+ """
44
+ Creates a general weighted homogeneous vector field (i.e., VFClass instance) in the coordinate space of variables in *arg1* of weight *arg2* w.r.t. weights in *arg3*. Variables for the coefficients in the vector field are created with the label *arg4*.
45
+
46
+ If variable weights assigned are nonzero then the returned vector field is truly general. Otherwise the returned vector field is the general one with polynomial degree in the zero-weight variables bounded by *degreeCap* which can be set to any integer. If *degreeCap* is not specified then it defaults to zero.
47
+
48
+ Args:
49
+ arg1: a tuple/list of variables initialized by either *varWithVF* or *complexVarProc*
50
+ arg2: int
51
+ arg3: list of non-negative integer weights corresponding to the variables in *arg1* (must have the same length as *arg1*). If 0 is among the weights, then then proceedure will only test polynomial vector fields with polynomial degree in the weight zero variables up to the weight specified in *degreeCap*. By default, degreeCap=0, and can be set to any positive integer.
52
+ arg4: str
53
+ _tempVar: (optional keyword) internal key
54
+ assumeReal: (optional keyword) True or False
55
+ degreeCap: (optional keyword) set this keyword argument equal to any positive integer. If not specified, it defaults to zero.
56
+
57
+
58
+ Returns:
59
+ A vector field (i.e., VFClass instance)
60
+
61
+ Raises:
62
+ NA
63
+ """
64
+ pListLoc=[]
65
+ for j in range(len(arg3)):
66
+ pListLoc.append(createPolynomial(arg4+'_'+str(j)+'_',arg2+arg3[j],arg1,degreeCap=degreeCap,weightedHomogeneity=arg3,_tempVar=_tempVar,assumeReal=assumeReal))
67
+ return reduce(addVF,[scaleVF(pListLoc[j],eval('D_'+str(arg1[j]),_cached_caller_globals)) for j in range(len(arg1))])
68
+ def findWeightedCRSymmetries(arg1,arg2,arg3,arg4,arg5,arg6,degreeCap=0,returnVectorFieldBasis=False,applyNumer=False,simplifyingFactor=None):
69
+ """
70
+ ***This function's algorithm will be revised in future updates***
71
+
72
+ Attempts to find all infinitesimal symmetries of a rigid CR hypersurface given by setting one variable *arg5* equal to a defining function *arg1* in the variable space *arg2* with weighted homogeneity *arg3* w.r.t. to non-negative integer weights in *arg4*. Variables in the returned vector field's coefficients are labeled by *arg6*.
73
+
74
+ Only polynomial vector fields are searched for, so if a variable is assigned weight zero, the function cannot search across general symmetries. In such cases, it rather searches all possible symmetries with polynomial degree in the zero-weighted variables up to the specified bound *degreeCap*. If *degreeCap* is not specified, then it defaults to zero.
75
+
76
+ The algorithm is most succesful when the function *arg1* must be a polynomial.
77
+
78
+ The function *arg1* should not depend on the variable *arg5* (i.e., the algorithm is not intended for implicit defining equations). If there is such dependence *findWeightedCRSymmetries* may still find some but not all symmetries.
79
+
80
+
81
+ Args:
82
+ arg1: Defining function of a rigid CR hypersurface.
83
+ arg2: a tuple or list of complex variables parameterizing the space that the above CR hypersurface is defined in (not including the transverse direction symmetry).
84
+ arg3: list of non-negative integer weights corresponding to the variables in *arg1* (must have the same length as *arg1*). If 0 is among the weights, then then proceedure will only test polynomial vector fields with polynomial degree in the weight zero variables up to the weight specified in *degreeCap*. By default, degreeCap=0, and can be set to any positive integer.
85
+ arg4: int
86
+ arg5: The real variable that when set equal to the defining function defines the CR hypersurface.
87
+ arg6: str
88
+ degreeCap: (optional keyword) set this keyword argument equal to any positive integer. If not specified, it defaults to zero.
89
+ applyNumer: (optional keyword) True or False. Set equal to true if defining equation is rational but not polynomial. It can help the internal solvers.
90
+
91
+ Returns:
92
+ coefficient list for a holomorphic vector field containing variables, and any set real value for these variables defines an infinitesimal symmetry. **Note, indeed only real values for the variables define actual symmetries**
93
+
94
+ Raises:
95
+ NA
96
+ """
97
+ def extractRIVar(arg1):
98
+ return sum([list(holToReal(j).atoms(Symbol)) for j in arg1],[])
99
+ VFLoc=addVF(weightedHomogeneousVF(arg2,arg4,arg3,'ALoc',_tempVar=retrieve_passkey(),degreeCap=degreeCap,assumeReal=True),scaleVF(I,weightedHomogeneousVF(arg2,arg4,arg3,'BLoc',_tempVar=retrieve_passkey(),degreeCap=degreeCap,assumeReal=True)))
100
+ tOLoc=tangencyObstruction(VFLoc,arg1,arg5,arg2)
101
+ varLoc=tOLoc.atoms(Symbol)
102
+ varLoc1={j for j in varLoc}
103
+ varComp=set(extractRIVar(arg2))
104
+ varLoc.difference_update(varComp)
105
+ varLoc1.difference_update(varLoc)
106
+ varComp.difference_update(varLoc1)
107
+ variableProcedure(arg6,len(varLoc),assumeReal=True)
108
+ if applyNumer==True:
109
+ if varLoc1==set():
110
+ varLoc1=set(arg2)
111
+ coefListLoc=poly_from_expr(expand(numer(tOLoc)),*varLoc1)[0].coeffs()
112
+ solLoc=solve(coefListLoc,varLoc)
113
+ elif simplifyingFactor==None:
114
+ if varLoc1==set():
115
+ varLoc1=set(arg2)
116
+ coefListLoc=poly_from_expr(expand(tOLoc),*varLoc1)[0].coeffs()
117
+ solLoc=solve(coefListLoc,varLoc)
118
+ else:
119
+ if varLoc1==set():
120
+ varLoc1=set(arg2)
121
+ coefListLoc=poly_from_expr(expand(simplify(symToReal(simplifyingFactor)*tOLoc)),*varLoc1)[0].coeffs()
122
+ solLoc=solve(coefListLoc,varLoc)
123
+ if solLoc==[]:
124
+ clearVar(*listVar(temporary_only=True),report=False)
125
+ return 'no solution'
126
+ elif type(solLoc)==dict:
127
+ VFCLoc=[j.subs(solLoc) for j in holVF_coeffs(VFLoc,arg2)]
128
+ subVar=sum(VFCLoc).atoms(Symbol)
129
+ subVar.difference_update(set(arg2))
130
+ variableProcedure(arg6,len(subVar),assumeReal=True)
131
+ VFCLoc=[j.subs(dict(zip(subVar,eval(arg6,_cached_caller_globals)))) for j in VFCLoc]
132
+ clearVar(*listVar(temporary_only=True),report=False)
133
+ if returnVectorFieldBasis==True:
134
+ VFListLoc=[]
135
+ for j in eval(arg6,_cached_caller_globals):
136
+ VFCLocTemp=[k.subs(j,1).subs([(l,0) for l in eval(arg6,_cached_caller_globals)]) for k in VFCLoc]
137
+ VFListLoc.append(assembleFromHolVFC(VFCLocTemp,arg2))
138
+ clearVar(arg6,report=False)
139
+ return VFListLoc, VFCLoc
140
+ else:
141
+ return VFCLoc
142
+ else:
143
+ VFCLoc=holVF_coeffs(VFLoc,arg2)
144
+ subVar=sum(VFCLoc).atoms(Symbol)
145
+ subVar.difference_update(set(arg2))
146
+ variableProcedure(arg6,len(subVar),assumeReal=True)
147
+ VFCLoc=[j.subs(dict(zip(subVar,eval(arg6,_cached_caller_globals)))) for j in VFCLoc]
148
+ clearVar(*listVar(temporary_only=True),report=False)
149
+ return VFCLoc,solLoc
150
+
151
+ def model2Nondegenerate(arg1,arg2,arg3,arg4):
152
+ """
153
+ Builds the defining equation for a 2-nondegnerate model hypersurface using the general formula from the arXiv preprint arXiv:2404.06525.
154
+
155
+ Args:
156
+ arg1: nondegenerate s-by-s hermitian matrix
157
+ arg2: s-by-s symmetric matrix valued function of some complex variables whose differential at zero is injective, and whose value at zero is zero.
158
+ arg3: a length s tuple of complex variables, different from those appearing in *arg2*
159
+ arg4: a single complex variable, different from those appearing in *arg2* and *arg3*
160
+
161
+ Returns:
162
+ A sympy expression. Setting this expression equal to zero defines the 2-nondegenerate model.
163
+
164
+ Raises:
165
+ NA
166
+ """
167
+ BARSLoc=conjugate(arg2)
168
+ zVecLoc=Matrix(arg3)
169
+ bzVecLoc=Matrix([conjugate(j) for j in arg3])
170
+ sizeLoc=arg1.shape[0]
171
+ hFun=(Rational(1,2))*((arg1*(eye(sizeLoc)-(BARSLoc*Transpose(arg1)*arg2*arg1))**(-1))+((eye(sizeLoc)-(arg1*BARSLoc*Transpose(arg1)*arg2))**(-1)*arg1))
172
+ sFun=arg1*((eye(sizeLoc)-(BARSLoc*Transpose(arg1)*arg2*arg1))**(-1))*BARSLoc*Transpose(arg1)
173
+ bsFun=Transpose(arg1)*((eye(sizeLoc)-(arg2*arg1*BARSLoc*Transpose(arg1)))**(-1))*arg2*arg1
174
+ return simplify((Transpose(zVecLoc)*hFun*bzVecLoc+(Rational(1,2))*(Transpose(zVecLoc)*sFun*zVecLoc+Transpose(bzVecLoc)*bsFun*bzVecLoc))[0])-im(arg4)
175
+
DGCV/DGCVFormatter.py ADDED
@@ -0,0 +1,215 @@
1
+ import re
2
+ from pandas import DataFrame, MultiIndex
3
+ from .config import get_variable_registry, greek_letters
4
+ from .styles import get_style
5
+
6
+
7
+ # Modular function to collect data for complex, standard, and algebra variables
8
+ def collect_variable_data(variable_registry, use_latex):
9
+ data = [['Number of Var.', 'Real Part', 'Imaginary Part', 'Vector Fields', 'Differential Forms']]
10
+ index = []
11
+
12
+ complex_vars = sorted(variable_registry.get('complex_variable_systems', {}).keys())
13
+ standard_vars = sorted(variable_registry.get('standard_variable_systems', {}).keys())
14
+ finite_algebra_vars = sorted(variable_registry.get('finite_algebra_systems', {}).keys())
15
+
16
+ # Process complex variables
17
+ for var_name in complex_vars:
18
+ process_variable(var_name, 'complex', variable_registry, data, index, use_latex)
19
+
20
+ # Process standard variables
21
+ for var_name in standard_vars:
22
+ process_variable(var_name, 'standard', variable_registry, data, index, use_latex)
23
+
24
+ # Process finite algebra systems
25
+ for var_name in finite_algebra_vars:
26
+ process_algebra(var_name, variable_registry, data, index, use_latex)
27
+
28
+ return data, index
29
+
30
+
31
+ # Function to process complex and standard variables
32
+ def process_variable(var_name, system_type, variable_registry, data, index, use_latex):
33
+ # Common logic for both standard and complex variable systems
34
+ family_type = variable_registry[f'{system_type}_variable_systems'][var_name].get('family_type', 'single')
35
+ family_names = variable_registry[f'{system_type}_variable_systems'][var_name]['family_names']
36
+ initial_index = variable_registry[f'{system_type}_variable_systems'][var_name].get('initial_index', 1)
37
+
38
+ # Logic to process tuple or single variables and append to data
39
+ tuple_len = len(family_names)
40
+ real_part, imaginary_part, vf_str, df_str = format_variable_details(var_name, family_names, initial_index, tuple_len, system_type, use_latex)
41
+
42
+ # Append to data
43
+ data.append([
44
+ tuple_len,
45
+ real_part,
46
+ imaginary_part,
47
+ vf_str,
48
+ df_str
49
+ ])
50
+
51
+ # Update the index
52
+ formatted_name = format_variable_name(var_name, system_type, use_latex)
53
+ index.append((formatted_name, ''))
54
+
55
+
56
+ # Function to process algebra systems
57
+ def process_algebra(var_name, variable_registry, data, index, use_latex):
58
+ system_data = variable_registry['finite_algebra_systems'][var_name]
59
+ algebra_family_names = system_data.get('family_names', [])
60
+
61
+ # Format algebra label and basis
62
+ formatted_str = format_algebra_name(var_name, algebra_family_names, use_latex)
63
+
64
+ # Append to data
65
+ data.append([len(algebra_family_names), '----', '----', '----', '----'])
66
+
67
+ # Update the index
68
+ index.append((formatted_str, ''))
69
+
70
+
71
+ # Function to build the table
72
+ def build_table(data, index, style):
73
+ # Build and return the final styled table
74
+ columns = MultiIndex.from_product([['Initialized Coordinate Systems and Algebras'], ['', '', '', '', '']])
75
+ table = DataFrame(data=data, index=index, columns=columns)
76
+
77
+ # Apply styles and return the table
78
+ table_styles = get_style(style)
79
+ return table.style.set_table_styles(table_styles)
80
+
81
+ # Helper function to format variable details (real part, imaginary part, vector fields, differential forms)
82
+ def format_variable_details(var_name, family_names, initial_index, tuple_len, system_type, use_latex):
83
+ if system_type == 'complex':
84
+ variable_registry=get_variable_registry()
85
+ # Access the real and imaginary parts (assuming they are the 3rd and 4th elements in family_names)
86
+ family_houses = variable_registry['complex_variable_systems'][var_name]['family_houses']
87
+ family_names = variable_registry['complex_variable_systems'][var_name]['family_names']
88
+
89
+ if len(family_names[2]) > 1:
90
+ real_part = f"{family_houses[2]} = ({family_names[2][0]}, ..., {family_names[2][-1]})"
91
+ else:
92
+ real_part = f"{family_houses[2]} = ({family_names[2][0]})"
93
+
94
+ if len(family_names[3]) > 1:
95
+ imaginary_part = f"{family_houses[3]} = ({family_names[3][0]}, ..., {family_names[3][-1]})"
96
+ else:
97
+ imaginary_part = f"{family_houses[3]} = ({family_names[3][0]})"
98
+
99
+ vf_str = build_object_string_for_complex('D', family_houses, family_names, initial_index, use_latex)
100
+ df_str = build_object_string_for_complex('d', family_houses, family_names, initial_index, use_latex)
101
+
102
+ else: # Standard variables
103
+ real_part = '----' # Real part not applicable for standard variables
104
+ imaginary_part = '----' # Imaginary part not applicable for standard variables
105
+ vf_str = build_object_string('D', var_name, initial_index, tuple_len, system_type, use_latex)
106
+ df_str = build_object_string('d', var_name, initial_index, tuple_len, system_type, use_latex)
107
+
108
+ return real_part, imaginary_part, vf_str, df_str
109
+
110
+ def format_variable_name(var_name, system_type, use_latex=False):
111
+ variable_registry=get_variable_registry()
112
+ if system_type == 'standard':
113
+ family_type = variable_registry['standard_variable_systems'][var_name].get('family_type', 'single')
114
+ family_names = variable_registry['standard_variable_systems'][var_name]['family_names']
115
+ initial_index = variable_registry['standard_variable_systems'][var_name].get('initial_index', 1)
116
+ else: # system_type == 'complex'
117
+ family_type = variable_registry['complex_variable_systems'][var_name].get('family_type', 'single')
118
+ family_names = variable_registry['complex_variable_systems'][var_name]['family_names'][0] # First tuple entry
119
+ initial_index = variable_registry['complex_variable_systems'][var_name].get('initial_index', 1)
120
+
121
+ # Handle tuple or single variable cases
122
+ if family_type == 'tuple':
123
+ first_index = initial_index
124
+ last_index = initial_index + len(family_names) - 1
125
+ if use_latex:
126
+ content = f"{convert_to_greek(var_name)} = \\left( {convert_to_greek(var_name)}_{{{first_index}}}, \\ldots, {convert_to_greek(var_name)}_{{{last_index}}} \\right)"
127
+ else:
128
+ content = f"{var_name} = ({family_names[0]}, ..., {family_names[-1]})"
129
+ else:
130
+ content = convert_to_greek(var_name) if use_latex else var_name
131
+
132
+ return wrap_in_dollars(content) if use_latex else content
133
+
134
+ def format_algebra_name(var_name, algebra_family_names, use_latex=False):
135
+ if use_latex:
136
+ match = re.match(r'(.*?)(\d+)?$', var_name)
137
+ algebra_name = match.group(1) if match else var_name
138
+ algebra_number = match.group(2) if match else None
139
+
140
+ # Format the algebra name in LaTeX
141
+ algebra_label = f"\\mathfrak{{{convert_to_greek(algebra_name)}}}" if re.fullmatch(r'[a-z]+', algebra_name) else algebra_name
142
+ if algebra_number:
143
+ algebra_label += f"_{{{algebra_number}}}"
144
+
145
+ # Format the basis string
146
+ if len(algebra_family_names) > 1:
147
+ first_basis_label = process_basis_label(algebra_family_names[0])
148
+ last_basis_label = process_basis_label(algebra_family_names[-1])
149
+ basis_str = f"{first_basis_label}, \\ldots, {last_basis_label}"
150
+ else:
151
+ basis_str = process_basis_label(algebra_family_names[0]) if algebra_family_names else ""
152
+ return f"Algebra: {wrap_in_dollars(algebra_label)} \\\\ Basis: {wrap_in_dollars(basis_str)}"
153
+ else:
154
+ algebra_label = var_name
155
+ if len(algebra_family_names) > 1:
156
+ basis_str = f"{algebra_family_names[0]}, ..., {algebra_family_names[-1]}"
157
+ else:
158
+ basis_str = algebra_family_names[0] if algebra_family_names else ""
159
+ return f"Algebra: {algebra_label}<br>Basis: {basis_str}"
160
+
161
+ def build_object_string_for_complex(obj_type, part_names, family_names, start_index, use_latex=False):
162
+ parts = []
163
+ for part_name, part in zip(part_names, family_names):
164
+ if use_latex:
165
+ base_var = convert_to_greek(part_name.replace('BAR', '', 1)) if part_name.startswith('BAR') else convert_to_greek(part_name)
166
+ if len(part) > 1:
167
+ part_str = f"\\overline{{{base_var}_{{{start_index}}}}}, \\ldots, \\overline{{{base_var}_{{{start_index + len(part) - 1}}}}}"
168
+ else:
169
+ part_str = f"\\overline{{{base_var}}}" if part_name.startswith('BAR') else base_var
170
+ parts.append(f"d {part_str}" if obj_type == 'd' else f"\\frac{{\\partial}}{{\\partial {part_str}}}")
171
+ else:
172
+ part_str = f"{obj_type}_{convert_to_greek(part_name)}{start_index},...,{convert_to_greek(part_name)}{start_index + len(part) - 1}" if len(part) > 1 else f"{obj_type}_{convert_to_greek(part_name)}"
173
+ parts.append(part_str)
174
+ return ', '.join(parts)
175
+
176
+ def build_object_string(obj_type, var_name, start_index, tuple_len, system_type, use_latex=False):
177
+ if tuple_len == 1:
178
+ if use_latex:
179
+ content = f"d {convert_to_greek(var_name)}" if obj_type == 'd' else f"\\frac{{\\partial}}{{\\partial {convert_to_greek(var_name)}}}"
180
+ else:
181
+ content = f"{obj_type}_{var_name}"
182
+ else:
183
+ if use_latex:
184
+ content = f"d {convert_to_greek(var_name)}_{{{start_index}}}, \\ldots, d {convert_to_greek(var_name)}_{{{start_index + tuple_len - 1}}}" if obj_type == 'd' else f"\\frac{{\\partial}}{{\\partial {convert_to_greek(var_name)}_{{{start_index}}}}}, \\ldots, \\frac{{\\partial}}{{\\partial {convert_to_greek(var_name)}_{{{start_index + tuple_len - 1}}}}}"
185
+ else:
186
+ content = f"{obj_type}_{var_name}{start_index},...,{obj_type}_{var_name}{start_index + tuple_len - 1}"
187
+ return wrap_in_dollars(content) if use_latex else content
188
+
189
+ # Helper function to convert variable names to Greek letters (if applicable)
190
+ def convert_to_greek(var_name):
191
+ # Replace variable names with their corresponding Greek letters
192
+ for name, greek in greek_letters.items():
193
+ if var_name == name:
194
+ return greek
195
+ return var_name
196
+
197
+ # Helper function to wrap content in LaTeX dollar signs for rendering
198
+ def wrap_in_dollars(content):
199
+ return f"${content}$"
200
+
201
+ # Helper function to process basis labels (e.g., e1 -> e_{1})
202
+ def process_basis_label(label):
203
+ # Use regex to split the name and numeric suffix
204
+ match = re.match(r'(.*?)(\d+)?$', label)
205
+ basis_elem_name = match.group(1) # Alphabetic part
206
+ basis_elem_number = match.group(2) # Numeric part, if any
207
+
208
+ # Remove trailing underscores from the name
209
+ basis_elem_name = basis_elem_name.rstrip('_')
210
+
211
+ # Build the LaTeX string
212
+ if basis_elem_number:
213
+ return f"{convert_to_greek(basis_elem_name)}_{{{basis_elem_number}}}"
214
+ else:
215
+ return f"{convert_to_greek(basis_elem_name)}"
DGCV/DGCV_display.py ADDED
@@ -0,0 +1,198 @@
1
+
2
+ """
3
+ display.py
4
+
5
+ This module provides functions for customizing font display in Jupyter notebooks.
6
+ The `load_fonts()` function loads web fonts and applies them to the notebook
7
+ environment using HTML and CSS.
8
+
9
+ Functions
10
+ ---------
11
+ - load_fonts: Injects HTML and CSS to load Google Fonts and apply custom fonts to the notebook.
12
+ """
13
+ import sympy
14
+ from .classesAndVariables import DFClass,VFClass,STFClass,FAClass,DGCVPolyClass,symToHol,symToReal,TFClass,AlgebraElement
15
+ from .RiemannianGeometry import metricClass
16
+
17
+ from IPython.display import display, HTML, Latex
18
+
19
+
20
+
21
+ from sympy import latex, Basic
22
+
23
+ def LaTeX(obj,removeBARs=False):
24
+ """
25
+ Custom LaTeX function for DGCV.
26
+ Applies sympy.latex() to the input object, with special handling for lists and tuples.
27
+
28
+ For lists, the elements are LaTeX-processed, stripped of surrounding $ or $$ symbols if present,
29
+ and wrapped in \left[ ... \right].
30
+
31
+ For tuples, the elements are LaTeX-processed similarly but wrapped in \left( ... \right).
32
+
33
+ Parameters
34
+ ----------
35
+ obj : any
36
+ The object to convert to LaTeX. Can be a SymPy expression, DGCV object, or a list/tuple of such objects.
37
+
38
+ Returns
39
+ -------
40
+ str
41
+ The LaTeX-formatted string.
42
+ """
43
+ def filter(term):
44
+ if removeBARs==True:
45
+ return latex(term)
46
+ if isinstance(term,(DFClass,VFClass,STFClass,DGCVPolyClass,TFClass)):
47
+ if term._varSpace_type == 'real':
48
+ return latex(symToReal(term))
49
+ elif term._varSpace_type == 'complex':
50
+ return latex(symToHol(term))
51
+ else:
52
+ return latex(term)
53
+ elif isinstance(term, FAClass):
54
+ return _alglabeldisplayclass(term.label)._repr_latex_()
55
+ elif isinstance(term, AlgebraElement):
56
+ return _alglabeldisplayclass(term.algebra.label,term)._repr_latex_()
57
+ else:
58
+ return latex(symToHol(term))
59
+
60
+ def strip_dollar_signs(latex_str):
61
+ """Strip leading and trailing $ or $$ signs from a LaTeX string."""
62
+ # if latex_str == None:
63
+ # return ''
64
+ if latex_str.startswith("$$") and latex_str.endswith("$$"):
65
+ return latex_str[2:-2]
66
+ elif latex_str.startswith("$") and latex_str.endswith("$"):
67
+ return latex_str[1:-1]
68
+ return latex_str
69
+
70
+ if isinstance(obj, list):
71
+ # Handle lists by applying LaTeX and wrapping with \left[ ... \right]
72
+ latex_elements = [strip_dollar_signs(filter(elem)) for elem in obj]
73
+ return r"\left[ " + ", ".join(latex_elements) + r" \right]"
74
+
75
+ elif isinstance(obj, tuple):
76
+ # Handle tuples by applying LaTeX and wrapping with \left( ... \right)
77
+ latex_elements = [strip_dollar_signs(filter(elem)) for elem in obj]
78
+ return r"\left( " + ", ".join(latex_elements) + r" \right)"
79
+
80
+ else:
81
+ # Apply sympy.latex() for non-list/tuple objects
82
+ return strip_dollar_signs(filter(obj))
83
+
84
+
85
+ def display_DGCV(*args):
86
+ for j in args:
87
+ _display_DGCV_single(j)
88
+
89
+ def _display_DGCV_single(arg):
90
+ if isinstance(arg,str):
91
+ display(Latex(arg))
92
+ elif isinstance(arg,(sympy.Expr,metricClass,DFClass,VFClass,TFClass,STFClass,DGCVPolyClass)):
93
+ _complexDisplay(arg)
94
+ elif isinstance(arg,FAClass):
95
+ _complexDisplay(_alglabeldisplayclass(arg.label))
96
+ elif isinstance(arg,AlgebraElement):
97
+ _complexDisplay(_alglabeldisplayclass(arg.algebra.label,ae=arg))
98
+ else:
99
+ display(arg)
100
+
101
+ def _complexDisplay(*args):
102
+ """
103
+ Taking DGCV expressions in *args* written in terms of symbolic conjugate variables, displays them with actual complex conjugates
104
+ """
105
+ display(*[symToHol(j,simplify_everything=False) for j in args])
106
+
107
+
108
+ class _alglabeldisplayclass(Basic):
109
+
110
+ def __new__(cls, label, ae = None):
111
+ obj = Basic.__new__(cls, label)
112
+ return obj
113
+
114
+ def __init__(self, label, ae = None):
115
+ self.label = str(label)
116
+ self.ae = ae
117
+
118
+ @staticmethod
119
+ def format_algebra_label(label):
120
+ """Wrap the algebra label in \mathfrak{} if all characters are lowercase, and subscript any numeric suffix."""
121
+ if label[-1].isdigit():
122
+ # Split into text and number parts for subscript formatting
123
+ label_text = ''.join(filter(str.isalpha, label))
124
+ label_number = ''.join(filter(str.isdigit, label))
125
+ if label_text.islower():
126
+ return rf"\mathfrak{{{label_text}}}_{{{label_number}}}"
127
+ return rf"{label_text}_{{{label_number}}}"
128
+ elif label.islower():
129
+ return rf"\mathfrak{{{label}}}"
130
+ return label
131
+
132
+ @staticmethod
133
+ def format_ae(ae):
134
+ if ae != None:
135
+ terms = []
136
+ for coeff, basis_label in zip(ae.coeffs, ae.algebra.basis_labels):
137
+ if coeff == 0:
138
+ continue # Skip zero terms
139
+ elif coeff == 1:
140
+ terms.append(rf"{basis_label}") # Suppress 1 as coefficient
141
+ elif coeff == -1:
142
+ terms.append(rf"-{basis_label}") # Suppress 1 but keep the negative sign
143
+ else:
144
+ # Check if the coefficient has more than one term (e.g., 1 + I)
145
+ if isinstance(coeff, sympy.Expr) and len(coeff.args) > 1:
146
+ terms.append(rf"({sympy.latex(coeff)}) \cdot {basis_label}") # Wrap multi-term coefficient in parentheses
147
+ else:
148
+ terms.append(rf"{sympy.latex(coeff)} \cdot {basis_label}") # Single-term coefficient
149
+
150
+ # Handle special case: all zero coefficients
151
+ if not terms:
152
+ return rf"$0 \cdot {ae.algebra.basis_labels[0]}$"
153
+
154
+ # Join terms with proper LaTeX sign handling
155
+ result = " + ".join(terms).replace("+ -", "- ")
156
+ return rf"{result}"
157
+
158
+ def _repr_latex_(self):
159
+ if self.ae == None:
160
+ return _alglabeldisplayclass.format_algebra_label(self.label)
161
+ else:
162
+ return _alglabeldisplayclass.format_ae(self.ae)
163
+
164
+ def __str__(self):
165
+ return self.label
166
+
167
+
168
+ def load_fonts():
169
+ """
170
+ Load custom web fonts into the Jupyter notebook.
171
+
172
+ This function injects HTML and CSS to load fonts from Google Fonts and sets
173
+ the body font to 'Roboto'. It is intended for use in Jupyter notebooks to
174
+ customize the appearance of text.
175
+
176
+ Fonts Loaded
177
+ ------------
178
+ - Orbitron
179
+ - Press Start 2P
180
+ - Roboto
181
+
182
+ Examples
183
+ --------
184
+ >>> load_fonts()
185
+
186
+ Notes
187
+ -----
188
+ The fonts are applied globally to the notebook interface via CSS.
189
+ """
190
+ font_links = """
191
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Press+Start+2P&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
192
+ <style>
193
+ body {
194
+ font-family: 'Roboto', sans-serif;
195
+ }
196
+ </style>
197
+ """
198
+ display(HTML(font_links))