Family(indices,
function=['4ti2-20061025', 'R-2.6.0', 'atlas-3.7.37', 'atlas-3.8.1', 'a...,
name=['4ti2-20061025', 'R-2.6.0', 'atlas-3.7.37', 'atlas-3.8.1', 'a...,
hidden_keys=[],
hidden_function=['4ti2-20061025', 'R-2.6.0', 'atlas-3.7.37', 'atlas-3.8.1', 'a...)
| source code
|
A Family is an associative container which models a family
(f_i)_{i in I}. Then, f[i] returns the element of the family
indexed by i. Whenever available, set and combinatorial class
operations (counting, iteration, listing) on the family are
induced from those of the index set.
There are several available implementations (classes) for
different usages; Family serves as a factory, and will create
instances of the appropriate classes depending on its arguments.
EXAMPLES:
In its simplest form, a list l by itself is considered as the
family $(l[i]_{i in I})$ where $I$ is the range $0\dots,len(l)$.
So Family(l) just returns it.
sage: f = Family([1,2,3])
sage: f
[1, 2, 3]
A family can also be constructed from a dictionary t. The
resulting family is very close to t, except that the elements
of the family are the values of t. Here, we define the family
(f_i)_{i in \{3,4,7\}} with f_3='a', f_4='b', and f_7='d':
sage: f = Family({3: 'a', 4: 'b', 7: 'd'})
sage: f
Finite family {3: 'a', 4: 'b', 7: 'd'}
sage: f[7]
'd'
sage: len(f)
3
sage: list(f)
['a', 'b', 'd']
sage: [ x for x in f ]
['a', 'b', 'd']
sage: f.keys()
[3, 4, 7]
sage: 'b' in f
True
sage: 'e' in f
False
A familly can also be constructed by its index set $I$ and a
function $f$, as in $(f(i))_{i in I}$:
sage: f = Family([3,4,7], lambda i: 2*i)
sage: f
Finite family {3: 6, 4: 8, 7: 14}
sage: f.keys()
[3, 4, 7]
sage: f[7]
14
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
By default, if the index set is a list, all images are
computed right away, and stored in an internal
dictionary. Note that this requires all the elements of the
list to be hashable. One can ask instead for the images $f(i)$
to be computed lazily, when needed:
sage: f = LazyFamily([3,4,7], lambda i: 2r*i)
sage: f
Lazy family (f(i))_{i in [3, 4, 7]}
sage: f[7]
14
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
This allows in particular for modeling infinite families:
sage: f = Family(ZZ, lambda i: 2r*i)
sage: f
Lazy family (f(i))_{i in Integer Ring}
sage: f.keys()
Integer Ring
sage: f[1]
2
sage: f[-5]
-10
sage: i = f.__iter__()
sage: i.next(), i.next(), i.next(), i.next(), i.next()
(0, 2, -2, 4, -4)
Caveat: Only certain families with lazy behavior can be pickled. In
particular, only functions that work with Sage's pickle_function
and unpickle_function (in sage.misc.fpickle) will correctly
unpickle.
Finally, it can occasionally be useful to add some hidden
elements in a family, which are accessible as f[i], but
do not appear in the keys or the container operations.
sage: f = Family([3,4,7], lambda i: 2*i, hidden_keys=[2])
sage: f
Finite family {3: 6, 4: 8, 7: 14}
sage: f.keys()
[3, 4, 7]
sage: f.hidden_keys()
[2]
sage: f[7]
14
sage: f[2]
4
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
The following example illustrates when the function is actually called:
sage: def compute_value(i):
... print('computing 2*'+str(i))
... return 2*i
sage: f = Family([3,4,7], compute_value, hidden_keys=[2])
computing 2*3
computing 2*4
computing 2*7
sage: f
Finite family {3: 6, 4: 8, 7: 14}
sage: f.keys()
[3, 4, 7]
sage: f.hidden_keys()
[2]
sage: f[7]
14
sage: f[2]
computing 2*2
4
sage: f[2]
4
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
Here is a close variant where the function for the hidden keys
is different from that for the other keys:
sage: f = Family([3,4,7], lambda i: 2*i, hidden_keys=[2], hidden_function = lambda i: 3*i)
sage: f
Finite family {3: 6, 4: 8, 7: 14}
sage: f.keys()
[3, 4, 7]
sage: f.hidden_keys()
[2]
sage: f[7]
14
sage: f[2]
6
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
Family behaves the same way with FiniteCombinatorialClass
instances and lists. This feature will eventually disapear
when FiniteCombinatorialClass won't be needed anymore.
sage: f = Family(FiniteCombinatorialClass([1,2,3]))
sage: f
Combinatorial class with elements in [1, 2, 3]
sage: f = Family(FiniteCombinatorialClass([3,4,7]), lambda i: 2*i)
sage: f
Finite family {3: 6, 4: 8, 7: 14}
sage: f.keys()
[3, 4, 7]
sage: f[7]
14
sage: list(f)
[6, 8, 14]
sage: [ x for x in f]
[6, 8, 14]
sage: len(f)
3
TESTS:
sage: f = Family({1:'a', 2:'b', 3:'c'})
sage: f
Finite family {1: 'a', 2: 'b', 3: 'c'}
sage: f[2]
'b'
sage: loads(dumps(f)) == f
True
sage: f = Family(range(1,27), lambda i: chr(i+96))
sage: f
Finite family {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z'}
sage: f[2]
'b'
|