教程:实现代数结构¶
本教程将介绍四个概念:
赋予自由模和向量空间以附加代数结构
定义态射
定义强制和转换
用几种实现实现代数结构
在本教程的最后,读者应该能够通过几个实现重新实现代数的例子:
sage: Sets().WithRealizations().example()
The subset algebra of {1, 2, 3} over Rational Field
也就是说,我们考虑一个代数 A(S) 其基础由子集索引 s 对给定集合的 S . A(S) 有三个自然基础: F
, In
, Out
在第一个基础上,乘积是由索引集的并集给出的。这个 In
基础和 Out
依据分别定义为:
\[In_s=sum{tsubset s}F_tqquad F_s=sum{tsupset s}输出\]
每一个这样的基础都给出了 A ,其中元素在此基础上通过展开来表示。在运行练习中,我们将逐步实现这个代数及其三个实现,其中包括强制和混合算术。
本教程主要依赖于 教程:使用自由模和向量空间 . 你可能还想阅读不太专业的专题教程 How to implement new algebraic structures .
子类化自由模块并包含类别信息¶
作为热身,我们实现了可加群的群代数 ZZ/5ZZ . 当然,这仅仅是为了教学目的;群代数已经实现了(参见 ZMod(5).algebra(ZZ)
). 回想一下 ZZ -可以使用以下命令创建此组上的模块:
sage: A = CombinatorialFreeModule(ZZ, Zmod(5), prefix='a')
我们复制了相同的,但是通过派生 CombinatorialFreeModule
::
sage: class MyCyclicGroupModule(CombinatorialFreeModule):
....: """An absolutely minimal implementation of a module whose basis is a cyclic group"""
....: def __init__(self, R, n, *args, **kwargs):
....: CombinatorialFreeModule.__init__(self, R, Zmod(n), *args, **kwargs)
sage: A = MyCyclicGroupModule(QQ, 6, prefix='a') # or 4 or 5 or 11 ...
sage: a = A.basis()
sage: A.an_element()
2*a[0] + 2*a[1] + 3*a[2]
我们现在想捐赠 A 利用其自然积结构,得到所需的群代数。要定义乘法,我们应该属于乘法有意义的范畴,但现在还不是这样:
sage: A.category()
Category of finite dimensional vector spaces with basis over Rational Field
我们可以看看 Categories 从参考手册中的文档中,或者我们可以使用自省来查看类别列表以选择我们想要的:
sage: sage.categories.<tab> # not tested
一旦我们选择了一个合适的类别(这里 AlgebrasWithBasis
),我们可以看一个例子:
sage: E = AlgebrasWithBasis(QQ).example(); E
An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field
sage: e = E.an_element(); e
B[word: ] + 2*B[word: a] + 3*B[word: b] + B[word: bab]
浏览它的代码:
sage: E?? # not tested
这段代码是用来实现一个新代数的模板。特别是,这个模板表明我们需要实现这些方法 product_on_basis
, one_basis
, _repr_
和 algebra_generators
. 获取此方法列表的另一种方法是询问类别(TODO:为此找到一个更巧妙的习惯用法)::
sage: from sage.misc.abstract_method import abstract_methods_of_class
sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).element_class) # py2
{'optional': ['_add_', '_mul_'],
'required': ['__nonzero__', 'monomial_coefficients']}
sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).element_class) # py3
{'optional': ['_add_', '_mul_'],
'required': ['__bool__', 'monomial_coefficients']}
sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).parent_class)
{'optional': ['one_basis', 'product_on_basis'], 'required': ['__contains__']}
警告
上面的结果还不一定完整;类别中许多必需的方法还没有标记为 abstract_methods()
. 我们还建议浏览此类文档: AlgebrasWithBasis
.
加上这些方法,下面是群代数的最小实现:
sage: class MyCyclicGroupAlgebra(CombinatorialFreeModule):
....:
....: def __init__(self, R, n, **keywords):
....: self._group = Zmod(n)
....: CombinatorialFreeModule.__init__(self, R, self._group,
....: category=AlgebrasWithBasis(R), **keywords)
....:
....: def product_on_basis(self, left, right):
....: return self.monomial( left + right )
....:
....: def one_basis(self):
....: return self._group.zero()
....:
....: def algebra_generators(self):
....: return Family( [self.monomial( self._group(1) ) ] )
....:
....: def _repr_(self):
....: return "Jason's group algebra of %s over %s"%(self._group, self.base_ring())
关于此实现的一些注意事项:
或者,我们可以定义
product
而不是product_on_basis
::....: # def product(self, left, right): ....: # return ## something ##
为了本教程的可读性,我们去掉了所有文档字符串。当然,所有这些都应该出现在
E
.目的
**keywords
是把选择权传下去prefix
到CombinatorialFreeModules
.
让我们做些计算:
sage: A = MyCyclicGroupAlgebra(QQ, 2, prefix='a') # or 4 or 5 or 11 ...
sage: a = A.basis();
sage: f = A.an_element();
sage: A, f
(Jason's group algebra of Ring of integers modulo 2 over Rational Field, 2*a[0] + 2*a[1])
sage: f * f
8*a[0] + 8*a[1]
sage: f.<tab> # not tested
sage: f.is_idempotent()
False
sage: A.one()
a[0]
sage: x = A.algebra_generators().first() # Typically x,y, ... = A.algebra_generators()
sage: [x^i for i in range(4)]
[a[0], a[1], a[0], a[1]]
sage: g = 2*a[1]; (f + g)*f == f*f + g*f
True
这看起来工作得很好,但我们希望在实现上施加更多的压力,以消除潜在的bug。为此,我们将使用 TestSuite
,这是一个为我们的代数执行许多常规测试的工具。
因为我们是以交互方式定义类的,而不是在Python模块中定义的,所以这些测试将抱怨“pickling”。我们可以通过让sage认为类是在模块中定义的来消除这个错误。我们也可以暂时忽略那些失败的测试或者打电话 TestSuite
有了争论 skip='_test_pickling') ::
sage: import __main__
sage: __main__.MyCyclicGroupAlgebra = MyCyclicGroupAlgebra
好吧,让我们来测试一下:
sage: TestSuite(A).run(verbose=True)
running ._test_additive_associativity() . . . pass
running ._test_an_element() . . . pass
running ._test_associativity() . . . pass
running ._test_cardinality() . . . pass
running ._test_category() . . . pass
running ._test_characteristic() . . . pass
running ._test_distributivity() . . . pass
running ._test_elements() . . .
Running the test suite of self.an_element()
running ._test_category() . . . pass
running ._test_eq() . . . pass
running ._test_new() . . . pass
running ._test_nonzero_equal() . . . pass
running ._test_not_implemented_methods() . . . pass
running ._test_pickling() . . . pass
pass
running ._test_elements_eq_reflexive() . . . pass
running ._test_elements_eq_symmetric() . . . pass
running ._test_elements_eq_transitive() . . . pass
running ._test_elements_neq() . . . pass
running ._test_eq() . . . pass
running ._test_new() . . . pass
running ._test_not_implemented_methods() . . . pass
running ._test_one() . . . pass
running ._test_pickling() . . . pass
running ._test_prod() . . . pass
running ._test_some_elements() . . . pass
running ._test_zero() . . . pass
有关类别的详细信息,请参见 Sage中的元素、父类和类别:初级读本(草稿) ::
sage: sage.categories.primer? # not tested
回顾¶
我们想实现一个代数,所以我们:
使用创建基础向量空间
CombinatorialFreeModule
看着
sage.categories.<tab>
找到合适的类别加载了该类别的示例,并使用
sage.misc.abstract_method.abstract_methods_of_class()
,看看我们需要写什么方法在我们的类中添加了类别信息和其他必要的方法
跑
TestSuite
捕捉潜在的差异
练习¶
对
product_on_basis
在“mycyclicgroup代数”中实现 dual 循环群的群代数而不是它的群代数(因此乘积现在由 b_fb_g=delta_{{f,g}}bf )运行
TestSuite
测试(您可以忽略“酸洗”错误)。你注意到了什么?修复
one
检查一下TestSuite
现在测试通过了。加上Hopf代数结构。提示:看看这个例子:
sage: C = HopfAlgebrasWithBasis(QQ).example()
给一套 S ,说:
sage: S = Set([1,2,3,4,5])
还有一个基环,比如说:
sage: R = QQ
实施 R -代数:
sage: F = SubsetAlgebraOnFundamentalBasis(S, R) # todo: not implemented
有基础的
(b_s)_{{s\subset S}}
按子集索引S
::sage: Subsets(S) Subsets of {1, 2, 3, 4, 5}
以及产品的定义 b_s b_t = b_{{scup t}} .
态射¶
为了更好地理解代数空间之间的关系,我们需要考虑它们之间的态射:
sage: A.module_morphism? # not tested
sage: A = MyCyclicGroupAlgebra(QQ, 2, prefix='a')
sage: B = MyCyclicGroupAlgebra(QQ, 6, prefix='b')
sage: A, B
(Jason's group algebra of Ring of integers modulo 2 over Rational Field, Jason's group algebra of Ring of integers modulo 6 over Rational Field)
sage: def func_on_basis(g):
....: r"""
....: This function is the 'brain' of a (linear) morphism
....: from A --> B.
....: The input is the index of basis element of the domain (A).
....: The output is an element of the codomain (B).
....: """
....: if g==1: return B.monomial(Zmod(6)(3))# g==1 in the range A
....: else: return B.one()
我们现在可以定义一个态射,将这个函数扩展到 A 线性度:
sage: phi = A.module_morphism(func_on_basis, codomain=B)
sage: f = A.an_element()
sage: f
2*a[0] + 2*a[1]
sage: phi(f)
2*b[0] + 2*b[3]
对角态射和三角态射¶
我们现在说明如何在基上指定给定态射相对于某个阶是对角的或三角形的,这意味着态射是可逆的 Sage 能够自动计算逆态射。当前此功能要求域和codomain具有相同的索引集(正在进行…)。
sage: X = CombinatorialFreeModule(QQ, Partitions(), prefix='x'); x = X.basis();
sage: Y = CombinatorialFreeModule(QQ, Partitions(), prefix='y'); y = Y.basis();
对角模态射以一个函数为参数,该函数的输入是域的一个基元的索引,其输出是该域的相应基元的系数:
sage: def diag_func(p):
....: if len(p)==0: return 1
....: else: return p[0]
....:
....:
sage: diag_func(Partition([3,2,1]))
3
sage: X_to_Y = X.module_morphism(diagonal=diag_func, codomain=Y)
sage: f = X.an_element();
sage: f
2*x[[]] + 2*x[[1]] + 3*x[[2]]
sage: X_to_Y(f)
2*y[[]] + 2*y[[1]] + 6*y[[2]]
Python有趣的事实: ~
是反转运算符(但是要小心int:
sage: ~2
1/2
sage: ~(int(2)) # in python this is the bitwise complement: ~x = -x-1
-3
对角模态射是可逆的:
sage: Y_to_X = ~X_to_Y
sage: f = y[Partition([3])] - 2*y[Partition([2,1])]
sage: f
-2*y[[2, 1]] + y[[3]]
sage: Y_to_X(f)
-x[[2, 1]] + 1/3*x[[3]]
sage: X_to_Y(Y_to_X(f))
-2*y[[2, 1]] + y[[3]]
对于三角态射,就像普通态射一样,我们需要一个函数,它接受域的一个基元素的索引作为输入,并返回一个codomain元素。我们认为这个函数表示线性变换矩阵的列:
sage: def triang_on_basis(p):
....: return Y.sum_of_monomials(mu for mu in Partitions(sum(p)) if mu >= p)
....:
sage: triang_on_basis([3,2])
y[[3, 2]] + y[[4, 1]] + y[[5]]
sage: X_to_Y = X.module_morphism(triang_on_basis, triangular='lower', unitriangular=True, codomain=Y)
sage: f = x[Partition([1,1,1])] + 2*x[Partition([3,2])];
sage: f
x[[1, 1, 1]] + 2*x[[3, 2]]
sage: X_to_Y(f)
y[[1, 1, 1]] + y[[2, 1]] + y[[3]] + 2*y[[3, 2]] + 2*y[[4, 1]] + 2*y[[5]]
三角模态射也是可逆的,即使 X
和 Y
都是无限维的:
sage: Y_to_X = ~X_to_Y
sage: f
x[[1, 1, 1]] + 2*x[[3, 2]]
sage: Y_to_X(X_to_Y(f))
x[[1, 1, 1]] + 2*x[[3, 2]]
有关详细信息,请参阅 ModulesWithBasis.ParentMethods.module_morphism()
(以及 sage.categories.modules_with_basis.TriangularModuleMorphism
):
sage: A.module_morphism? # not tested
锻炼¶
重新定义态射 phi
在前面的练习中,作为一个态射,它是关于包含子集的三角形,并定义了逆态射。您可能希望使用以下比较键作为 key
参数 modules_morphism
::
sage: def subset_key(s):
....: """
....: A comparison key on sets that gives a linear extension
....: of the inclusion order.
....:
....: INPUT:
....:
....: - ``s`` -- set
....:
....: EXAMPLES::
....:
....: sage: sorted(Subsets([1,2,3]), key=subset_key)
....: [{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}]
....: """
....: return (len(s), list(s))
胁迫¶
一旦我们从 X to Y ,我们可以登记为胁迫。这将允许Sage在我们组合 X 和 Y 一起。看到了吗http://sagemath.com/doc/reference/forception.html了解更多信息。作为一个训练步骤,让我们首先定义一个态射 X 到 Y ,并注册为强制:
sage: def triang_on_basis(p):
....: return Y.sum_of_monomials(mu for mu in Partitions(sum(p)) if mu >= p)
sage: triang_on_basis([3,2])
y[[3, 2]] + y[[4, 1]] + y[[5]]
sage: X_to_Y = X.module_morphism(triang_on_basis, triangular='lower', unitriangular=True, codomain=Y)
sage: X_to_Y.<tab> # not tested
sage: X_to_Y.register_as_coercion()
现在我们不仅可以从 X 到 Y ,但我们也可以对这些元素进行混合运算:
sage: Y(x[Partition([3,2])])
y[[3, 2]] + y[[4, 1]] + y[[5]]
sage: Y([2,2,1]) + x[Partition([2,2,1])]
2*y[[2, 2, 1]] + y[[3, 1, 1]] + y[[3, 2]] + y[[4, 1]] + y[[5]]
锻炼¶
使用 phi
从 F
到 In
. 重新实施 In
作为一个代数,用乘积法 phi
以及它的反面。
离题:对称函数的新基和新商¶
作为一个应用程序,我们将展示如何结合我们所学来实现对称函数代数的新基和商:
sage: SF = SymmetricFunctions(QQ); # A graded Hopf algebra
sage: h = SF.homogeneous() # A particular basis, indexed by partitions (with some additional magic)
所以, h 是一个分次代数,其基被分区索引。更详细地说, h([i])
是所有度的单项式之和 i ::
sage: h([2]).expand(4)
x0^2 + x0*x1 + x1^2 + x0*x2 + x1*x2 + x2^2 + x0*x3 + x1*x3 + x2*x3 + x3^2
和 h(mu) = prod( h(p) for p in mu )
::
sage: h([3,2,2,1]) == h([3]) * h([2]) * h([2]) * h([1])
True
在这里我们定义了一个新的基础 (X_lambda)_lambda 关于 h 也就是说,我们 X_lambda = sum_{{mugeq lambda, |mu|=|nu|}} h_mu ::
sage: class MySFBasis(CombinatorialFreeModule):
....: r"""
....: Note: We would typically use SymmetricFunctionAlgebra_generic
....: for this. This is as an example only.
....: """
....:
....: def __init__(self, R, *args, **kwargs):
....: """ TODO: Informative doc-string and examples """
....: CombinatorialFreeModule.__init__(self, R, Partitions(), category=AlgebrasWithBasis(R), *args, **kwargs)
....: self._h = SymmetricFunctions(R).homogeneous()
....: self._to_h = self.module_morphism( self._to_h_on_basis, triangular='lower', unitriangular=True, codomain=self._h)
....: self._from_h = ~(self._to_h)
....: self._to_h.register_as_coercion()
....: self._from_h.register_as_coercion()
....:
....: def _to_h_on_basis(self, la):
....: return self._h.sum_of_monomials(mu for mu in Partitions(sum(la)) if mu >= la)
....:
....: def product(self, left, right):
....: return self( self._h(left) * self._h(right) )
....:
....: def _repr_(self):
....: return "Jason's basis for symmetric functions over %s"%self.base_ring()
....:
....: @cached_method
....: def one_basis(self):
....: r""" Returns the index of the basis element that is equal to '1'."""
....: return Partition([])
sage: X = MySFBasis(QQ, prefix='x'); x = X.basis(); h = SymmetricFunctions(QQ).homogeneous()
sage: f = X(h([2,1,1]) - 2*h([2,2])) # Note the capital X
sage: f
x[[2, 1, 1]] - 3*x[[2, 2]] + 2*x[[3, 1]]
sage: h(f)
h[2, 1, 1] - 2*h[2, 2]
sage: f*f*f
x[[2, 2, 2, 1, 1, 1, 1, 1, 1]] - 7*x[[2, 2, 2, 2, 1, 1, 1, 1]] + 18*x[[2, 2, 2, 2, 2, 1, 1]]
- 20*x[[2, 2, 2, 2, 2, 2]] + 8*x[[3, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
sage: h(f*f)
h[2, 2, 1, 1, 1, 1] - 4*h[2, 2, 2, 1, 1] + 4*h[2, 2, 2, 2]
现在我们实现了通过消除任何单项式对称函数得到的对称函数代数的商 m_lambda 使第一部分 lambda 大于 k . 见 Sets.SubcategoryMethods.Subquotients()
有关实现商的更多详细信息:
sage: class MySFQuotient(CombinatorialFreeModule):
....: r"""
....: The quotient of the ring of symmetric functions by the ideal generated
....: by those monomial symmetric functions whose part is larger than some fixed
....: number ``k``.
....: """
....: def __init__(self, R, k, prefix=None, *args, **kwargs):
....: CombinatorialFreeModule.__init__(self, R,
....: Partitions(NonNegativeIntegers(), max_part=k),
....: prefix = 'mm',
....: category = Algebras(R).Graded().WithBasis().Quotients(), *args, **kwargs)
....:
....: self._k = k
....: self._m = SymmetricFunctions(R).monomial()
....:
....: self.lift = self.module_morphism(self._m.monomial)
....: self.retract = self._m.module_morphism(self._retract_on_basis, codomain=self)
....:
....: self.lift.register_as_coercion()
....: self.retract.register_as_coercion()
....:
....: def ambient(self):
....: return self._m
....:
....: def _retract_on_basis(self, mu):
....: r"""
....: Takes the index of a basis element of a monomial
....: symmetric function, and returns the projection of that
....: element to the quotient.
....: """
....: if len(mu) > 0 and mu[0] > self._k:
....: return self.zero()
....: return self.monomial(mu)
....:
sage: MM = MySFQuotient(QQ, 3)
sage: mm = MM.basis()
sage: m = SymmetricFunctions(QQ).monomial()
sage: P = Partition
sage: g = m[P([3,2,1])] + 2*m[P([3,3])] + m[P([4,2])]; g
m[3, 2, 1] + 2*m[3, 3] + m[4, 2]
sage: f = MM(g); f
mm[[3, 2, 1]] + 2*mm[[3, 3]]
sage: m(f)
m[3, 2, 1] + 2*m[3, 3]
sage: (m(f))^2
8*m[3, 3, 2, 2, 1, 1] + 12*m[3, 3, 2, 2, 2] + 24*m[3, 3, 3, 2, 1] + 48*m[3, 3, 3, 3]
+ 4*m[4, 3, 2, 2, 1] + 4*m[4, 3, 3, 1, 1] + 14*m[4, 3, 3, 2] + 4*m[4, 4, 2, 2]
+ 4*m[4, 4, 3, 1] + 6*m[4, 4, 4] + 4*m[5, 3, 2, 1, 1] + 4*m[5, 3, 2, 2]
+ 12*m[5, 3, 3, 1] + 2*m[5, 4, 2, 1] + 6*m[5, 4, 3] + 4*m[5, 5, 1, 1] + 2*m[5, 5, 2]
+ 4*m[6, 2, 2, 1, 1] + 6*m[6, 2, 2, 2] + 6*m[6, 3, 2, 1] + 10*m[6, 3, 3] + 2*m[6, 4, 1, 1] + 5*m[6, 4, 2] + 4*m[6, 5, 1] + 4*m[6, 6]
sage: f^2
8*mm[[3, 3, 2, 2, 1, 1]] + 12*mm[[3, 3, 2, 2, 2]] + 24*mm[[3, 3, 3, 2, 1]] + 48*mm[[3, 3, 3, 3]]
sage: (m(f))^2 - m(f^2)
4*m[4, 3, 2, 2, 1] + 4*m[4, 3, 3, 1, 1] + 14*m[4, 3, 3, 2] + 4*m[4, 4, 2, 2] + 4*m[4, 4, 3, 1] + 6*m[4, 4, 4] + 4*m[5, 3, 2, 1, 1] + 4*m[5, 3, 2, 2] + 12*m[5, 3, 3, 1] + 2*m[5, 4, 2, 1] + 6*m[5, 4, 3] + 4*m[5, 5, 1, 1] + 2*m[5, 5, 2] + 4*m[6, 2, 2, 1, 1] + 6*m[6, 2, 2, 2] + 6*m[6, 3, 2, 1] + 10*m[6, 3, 3] + 2*m[6, 4, 1, 1] + 5*m[6, 4, 2] + 4*m[6, 5, 1] + 4*m[6, 6]
sage: MM( (m(f))^2 - m(f^2) )
0
用几种实现实现代数结构¶
现在我们回到子集代数,并用它作为一个例子来说明如何在不同的基之间自动强制实现一个代数的几个不同的基。我们已经为这个代数实现了三个基: F
, In
和 Out
基地,以及他们之间的胁迫。在实际计算中,通过实现一个对象,可以方便地将这些父对象连接在一起 A
抽象代数本身的模型。然后,父母 F
, In
和 Out
将 实现 属于 A
,同时 A
将是 有变现的母公司 . 见 Sets().WithRealizations
有关预期用户界面和基本原理的更多信息。
下面是一个简短的模板,突出显示了整体结构:
class MyAlgebra(Parent, UniqueRepresentation):
def __init__(self, R, ...):
category = Algebras(R).Commutative()
Parent.__init__(self, category=category.WithRealizations())
# attribute initialization, construction of the morphisms
# between the bases, ...
class Bases(Category_realization_of_parent):
def super_categories(self):
A = self.base()
category = Algebras(A.base_ring()).Commutative()
return [A.Realizations(), category.Realizations().WithBasis()]
class ParentMethods:
r"""Code that is common to all bases of the algebra"""
class ElementMethods:
r"""Code that is common to elements of all bases of the algebra"""
class FirstBasis(CombinatorialFreeModule, BindableClass):
def __init__(self, A):
CombinatorialFreeModule.__init__(self, ..., category=A.Bases())
# implementation of the multiplication, the unit, ...
class SecondBasis(CombinatorialFreeModule, BindableClass):
def __init__(self, A):
CombinatorialFreeModule.__init__(self, ..., category=A.Bases())
# implementation of the multiplication, the unit, ...
班级 MyAlgebra
实现交换代数 A
我们在 MyAlgebra
. 两个基类 MyAlgebra.FirstBasis
和 MyAlgebra.SecondBasis
实现 A
对应于元素展开的不同基础。它们在类别中初始化 MyAlgebra.Bases
所有基础的 A
,其作用是找出它们的共同特征。特别是,这种结构表示它们是:
实现
A
一个交换代数的实现,有一个可区别的基
注解
这里有一点冗余:鉴于此 A
知道它是一个具有实现的交换代数基础设施可以,原则上,确定它的实现是交换代数。如果这样做了,那么就有可能实现 Bases.super_categories 通过返回:
[A.Realizations().WithBasis()]
然而,这一点尚未实施。
注解
继承自 BindableCass
只是提供了语法上的糖:它使 MyAlgebras().FirstBasis()
简写 MyAlgebras.FirstBasis(MyAlgebras().FirstBasis())
(绑定行为)。班级 Bases
从继承此绑定行为 Category_realization_of_parent
,这就是为什么我们可以写作 MyAlgebras().Bases
而不是 MyAlgebras.Bases(MyAlgebras())
注解
大多数情况下,所有基类的构造函数即使不完全相同,也会非常相似;因此我们希望将其分解出来。令人恼火的是,将构造函数放入 Bases.ParentMethods
不起作用,因为这是一个抽象类,而构造函数处理数据结构的具体实现。类似地,如果只需要指定一次基继承的类就更好了,但是代码不能进入 Bases
出于同样的原因。
目前推荐的解决方案是增加一个类 Basis
这就排除了不同基地的共同具体特征:
...
class Basis(CombinatorialFreeModule, BindableClass):
def __init__(self, A):
CombinatorialFreeModule.__init__(self, ..., category=A.Bases())
class FirstBasis(Basis):
...
class SecondBasis(Basis):
...
这个解决方案可行,但不是最佳方案,因为要在两个基础代码之间共享特性,需要进入两个位置, Basis
和 Bases
,取决于它们是具体的还是抽象的。
现在,我们敦促读者浏览以下示例的完整代码,这是一个完整的模板,用于构造具有实现的新父级:
sage: A = Sets().WithRealizations().example(); A
The subset algebra of {1, 2, 3} over Rational Field
sage: A?? # not implemented