Piecewise affine transformations (PATs) and induced transformations

Piecewise affine transformations and induced transformations

EXAMPLES:

Recall how to create affine maps:

sage: F = AffineGroup(3, QQ); F
Affine Group of degree 3 over Rational Field
sage: M = matrix(QQ,[[1,2,3],[4,5,6],[7,8,0]])
sage: v = vector(QQ,[10,11,12])
sage: F(M, v)
      [1 2 3]     [10]
x |-> [4 5 6] x + [11]
      [7 8 0]     [12]
sage: F.linear(M)
      [1 2 3]     [0]
x |-> [4 5 6] x + [0]
      [7 8 0]     [0]
sage: F.translation(v)
      [1 0 0]     [10]
x |-> [0 1 0] x + [11]
      [0 0 1]     [12]

A polyhedron partition:

sage: from slabbe import PolyhedronPartition
sage: h = 1/3
sage: p = Polyhedron([(0,h),(0,1),(h,1)])
sage: q = Polyhedron([(0,0), (0,h), (h,1), (h,0)])
sage: r = Polyhedron([(h,1), (1,1), (1,h), (h,0)])
sage: s = Polyhedron([(h,0), (1,0), (1,h)])
sage: P = PolyhedronPartition({0:p, 1:q, 2:r, 3:s})

Inducing a piecewise affine transformation on a sub-domain:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: ieq = [1/2, -1, 0]   # x0 <= 1/2
sage: T_induced,sub = T.induced_transformation(ieq)
sage: T_induced.pp()
Piecewise Affine Transformation given by a
Polyhedron partition of 6 atoms with 6 letters
defined by 6 affine maps:
Affine map 0:
      [0 1]     [  0]
x |-> [1 0] x + [2/3]
Affine map 1:
      [0 1]     [   0]
x |-> [1 0] x + [-1/3]
Affine map 2:
      [1 0]     [-1/3]
x |-> [0 1] x + [-1/3]
Affine map 3:
      [0 1]     [-1/3]
x |-> [1 0] x + [ 1/3]
Affine map 4:
      [1 0]     [ 1/3]
x |-> [0 1] x + [-2/3]
Affine map 5:
      [0 1]     [-2/3]
x |-> [1 0] x + [   0]
sage: sub
{0: [0], 1: [1], 2: [1, 1], 3: [0, 1, 1], 4: [0, 1, 1, 1], 5: [0, 1, 1, 1, 1]}

AUTHORS:

  • Sébastien Labbé, January 2019, added a class for polyhedron exchange transformations

  • Sébastien Labbé, September 15, 2023, translated PET into piecewise affine transformations

class slabbe.piecewise_affine_transformation.PiecewiseAffineTransformation(partition, affine_maps, affine_group=None)

Bases: object

Piecewise Affine Transformation (PAT).

INPUT:

  • partition – a polyhedron partition (with associated indices)

  • affine_maps – list or dict, associating each index with an affine map

  • affine_group – affine group (default:None), the affine group in which the affine maps live. If None, it takes the parent of the first affine map as default value.

EXAMPLES:

Create Polyhedron partition:

sage: from slabbe import PolyhedronPartition
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})

Create affine maps:

sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))

Create a piecewise affine transformation:

sage: from slabbe import PiecewiseAffineTransformation
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})

TESTS:

Works with general indices:

sage: P = PolyhedronPartition({'a':p, 'b':q})
sage: T = PiecewiseAffineTransformation(P, {'a':f0, 'b':f1})
affine_group()

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.affine_group()
Affine Group of degree 2 over Rational Field
affine_maps()

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: d = T.affine_maps()
sage: type(d)
<class 'dict'>
ambient_space()

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.ambient_space()
Vector space of dimension 2 over Rational Field

This code also handle PETs:

sage: from slabbe import PolyhedronExchangeTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(h,0),(h,1),(0,1)])
sage: q = Polyhedron([(1,0),(h,0),(h,1),(1,1)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: T = {0:(1-h,0), 1:(-h,0)}
sage: F = PolyhedronExchangeTransformation(P, T)
sage: F.ambient_space()
Vector space of dimension 2 over Rational Field
cylinder(word, partition=None)

Return the region associated to the coding word.

INPUT:

  • word – list

  • partition – polyhedron partition (default:None), if None, it uses the domain partition of the transformation

OUTPUT:

polyhedron partition

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: c = T.cylinder([0,1,1]); c
Polyhedron partition of 1 atoms with 1 letters
sage: c.volume()
2/9
sage: T.cylinder([0,1,1,1,1,0]).volume()
1/9
sage: T.cylinder([0,1,1,1,1,1]).volume()
0

Cylinders of words of length 0:

sage: T.cylinder([], P).volume()
1

Cylinders of words of length 1:

sage: C1 = [T.cylinder([a], P).volume() for a in range(3)]
sage: C1
[1/3, 2/3, 0]
sage: sum(C1)
1

Cylinders of words of length 2:

sage: import itertools
sage: L2 = itertools.product(range(3),repeat=2)
sage: C2 = [T.cylinder([a,b], P).volume() for (a,b) in L2]
sage: C2
[1/9, 2/9, 0, 2/9, 4/9, 0, 0, 0, 0]
sage: sum(C2)
1

Cylinders of words of length 3:

sage: L3 = itertools.product(range(3),repeat=3)
sage: C3 = [T.cylinder([a,b,c], P).volume() for (a,b,c) in L3]
sage: sum(C3)
1

It also works for PETs:

sage: from slabbe import PolyhedronPartition
sage: h = 1/2
sage: p = Polyhedron([(0,h),(0,1),(h,1)])
sage: q = Polyhedron([(0,0), (0,h), (h,1), (1,1), (1,h), (h,0)])
sage: r = Polyhedron([(h,0), (1,0), (1,h)])
sage: P = PolyhedronPartition([p,q,r])
sage: from slabbe import PolyhedronExchangeTransformation as PET
sage: base = identity_matrix(2)
sage: translation = vector((1/3, 0))
sage: u = PET.toral_translation(base, translation)
sage: c = u.cylinder([2,2], P); c
Polyhedron partition of 1 atoms with 1 letters
sage: c.alphabet()
{0}
sage: u.cylinder([1,1], P)
Polyhedron partition of 2 atoms with 2 letters
sage: u.cylinder([1], P)
Polyhedron partition of 1 atoms with 1 letters
sage: u.cylinder([], P).volume()
1
sage: C1 = [u.cylinder([a], P).volume() for a in range(3)]
sage: C1
[1/8, 3/4, 1/8]
sage: sum(C1)
1
sage: import itertools
sage: L2 = itertools.product(range(3),repeat=2)
sage: C2 = [u.cylinder([a,b], P).volume() for (a,b) in L2]
sage: C2
[1/72, 1/9, 0, 1/9, 19/36, 1/9, 0, 1/9, 1/72]
sage: sum(C2)
1
sage: L3 = itertools.product(range(3),repeat=3)
sage: C3 = [u.cylinder([a,b,c], P).volume() for (a,b,c) in L3]
sage: sum(C3)
1

TESTS:

sage: u.cylinder([0,0,0], P)
Polyhedron partition of 0 atoms with 0 letters
sage: u.cylinder([2,3], P)
Polyhedron partition of 0 atoms with 0 letters
sage: u.cylinder([2,1], P)
Polyhedron partition of 1 atoms with 1 letters
sage: u.cylinder([], P)
Polyhedron partition of 3 atoms with 3 letters
cylinders(size, partition=None)

Return the cylinders of given size.

INPUT:

  • size – nonnegative integer

  • partition – polyhedron partition (default:None), if None, it uses the domain partition of the transformation

OUTPUT:

polyhedron partition

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: [T.cylinders(i) for i in range(5)]
[Polyhedron partition of 1 atoms with 1 letters,
 Polyhedron partition of 2 atoms with 2 letters,
 Polyhedron partition of 4 atoms with 4 letters,
 Polyhedron partition of 6 atoms with 6 letters,
 Polyhedron partition of 9 atoms with 9 letters]
sage: [T.cylinders(i).alphabet() for i in range(5)]
[{()}, {0, 1}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5, 6, 7, 8}]

The code works also for PETs:

sage: from slabbe import PolyhedronExchangeTransformation as PET
sage: base = identity_matrix(2)
sage: translation = vector((1/3, 0))
sage: u = PET.toral_translation(base, translation)
sage: [u.cylinders(i) for i in range(5)]
[Polyhedron partition of 1 atoms with 1 letters,
 Polyhedron partition of 2 atoms with 2 letters,
 Polyhedron partition of 3 atoms with 3 letters,
 Polyhedron partition of 3 atoms with 3 letters,
 Polyhedron partition of 3 atoms with 3 letters]
sage: [u.cylinders(i).alphabet() for i in range(5)]
[{()}, {0, 1}, {0, 1, 2}, {0, 1, 2}, {0, 1, 2}]
domain()

Return the domain of the transformation.

OUTPUT:

a polyhedron

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.domain()
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
sage: T.domain().vertices()
(A vertex at (0, 0),
 A vertex at (0, 1),
 A vertex at (1, 0),
 A vertex at (1, 1))

This code also handle PETs:

sage: from slabbe import PolyhedronExchangeTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(h,0),(h,1),(0,1)])
sage: q = Polyhedron([(1,0),(h,0),(h,1),(1,1)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: T = {0:(1-h,0), 1:(-h,0)}
sage: F = PolyhedronExchangeTransformation(P, T)
sage: F.domain()
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
sage: F.domain().vertices()
(A vertex at (0, 0),
 A vertex at (0, 1),
 A vertex at (1, 0),
 A vertex at (1, 1))
image_partition()

Return the partition of the image.

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.image_partition()
Polyhedron partition of 2 atoms with 2 letters

It works also for PETs:

sage: from slabbe import PolyhedronPartition, PolyhedronExchangeTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(h,0),(h,1),(0,1)])
sage: q = Polyhedron([(1,0),(h,0),(h,1),(1,1)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: T = {0:(1-h,0), 1:(-h,0)}
sage: F = PolyhedronExchangeTransformation(P, T)
sage: F.image_partition()
Polyhedron partition of 2 atoms with 2 letters
sage: h = 1/3
sage: p = Polyhedron([(0,h),(0,1),(h,1)])
sage: q = Polyhedron([(0,0), (0,h), (h,1), (h,0)])
sage: r = Polyhedron([(h,1), (1,1), (1,h), (h,0)])
sage: s = Polyhedron([(h,0), (1,0), (1,h)])
sage: P = PolyhedronPartition([(0,p), (0,q), (1,r), (1,s)])
sage: T = {0:(1-h,0), 1:(-h,0)}
sage: F = PolyhedronExchangeTransformation(P, T)
sage: F.image_partition()
Polyhedron partition of 4 atoms with 2 letters
induced_partition(ieq, partition=None, substitution_type='dict', ignore_volume=0, verbose=False)

Returns the partition of the induced transformation on the domain.

INPUT:

  • ieq – list, an inequality. An entry equal to “[-1,7,3,4]” represents the inequality 7x_1+3x_2+4x_3>= 1.

  • partition – polyhedron partition (default:None), if None, it uses the domain partition of the transformation

  • substitution_type – string (default:'dict'), if 'column' or 'row', it returns a substitution2d, otherwise it returns a dict.

  • ignore_volume – real (optional:0), stop the while loop if the volume of what’s not yet returned is less than the given threshold

  • verbose – bool (optional:False), print verbose information

OUTPUT:

  • a polyhedron partition

  • a substitution2d or a dict

EXAMPLES:

sage: from slabbe import PolyhedronExchangeTransformation as PET
sage: base = identity_matrix(2)
sage: translation = vector((1/3, 0))
sage: u = PET.toral_translation(base, translation)

We compute the induced partition of a polyhedron exchange transformation on a subdomain given by an inequality:

sage: ieq = [1/3, -1, 0]   # x0 <= 1/3
sage: u.induced_partition(ieq)
(Polyhedron partition of 1 atoms with 1 letters,
 {0: [0, 0, 1]})
sage: ieq = [1/2, -1, 0]   # x0 <= 1/2
sage: u.induced_partition(ieq)
(Polyhedron partition of 3 atoms with 3 letters,
 {0: [0], 1: [0, 1], 2: [0, 0, 1]})

The second output can be turned into a column or a row Substitution2d if desired:

sage: u.induced_partition(ieq, substitution_type='row')
(Polyhedron partition of 3 atoms with 3 letters,
 Substitution 2d: {0: [[0]], 1: [[0], [1]], 2: [[0], [0], [1]]})

Now we construct a another coding partition:

sage: from slabbe import PolyhedronPartition
sage: h = 1/3
sage: p = Polyhedron([(0,h),(0,1),(h,1)])
sage: q = Polyhedron([(0,0), (0,h), (h,1), (h,0)])
sage: r = Polyhedron([(h,1), (1,1), (1,h), (h,0)])
sage: s = Polyhedron([(h,0), (1,0), (1,h)])
sage: P = PolyhedronPartition({0:p, 1:q, 2:r, 3:s})

We use this other partition to compute the induced partition:

sage: ieq = [h, -1, 0]   # x0 <= h
sage: Q,sub = u.induced_partition(ieq, P)
sage: Q
Polyhedron partition of 4 atoms with 4 letters
sage: sub
{0: [0, 2, 2], 1: [1, 2, 2], 2: [1, 2, 3], 3: [1, 3, 3]}
sage: P = PolyhedronPartition({0:p, 1:q, 2:r, 3:s})
sage: ieq2 = [1/2, -1, 0]   # x0 <= 1/2
sage: Q,sub = u.induced_partition(ieq2, P)
sage: Q
Polyhedron partition of 9 atoms with 9 letters
sage: sub
{0: [0],
 1: [1],
 2: [2, 2],
 3: [2, 3],
 4: [3, 3],
 5: [0, 2, 2],
 6: [1, 2, 2],
 7: [1, 2, 3],
 8: [1, 3, 3]}

Irrationnal rotations:

sage: z = polygen(QQ, 'z') #z = QQ['z'].0 # same as
sage: K = NumberField(z**2-z-1, 'phi', embedding=RR(1.6))
sage: phi = K.gen()
sage: h = 1/phi^2
sage: p = Polyhedron([(0,h),(0,1),(h,1)])
sage: q = Polyhedron([(0,0), (0,h), (h,1), (h,0)])
sage: r = Polyhedron([(h,1), (1,1), (1,h), (h,0)])
sage: s = Polyhedron([(h,0), (1,0), (1,h)])
sage: P = PolyhedronPartition({0:p, 1:q, 2:r, 3:s}, base_ring=K)
sage: base = identity_matrix(2)
sage: translation = vector((1/phi, 0))
sage: u = PET.toral_translation(base, translation)
sage: ieq = [h, -1, 0]   # x0 <= h
sage: P1,sub01 = u.induced_partition(ieq, P)
sage: P1
Polyhedron partition of 7 atoms with 7 letters
sage: sub01
{0: [0, 2],
 1: [1, 2],
 2: [1, 3],
 3: [0, 2, 2],
 4: [1, 2, 2],
 5: [1, 3, 2],
 6: [1, 3, 3]}

We do the induction on a smaller domain:

sage: ieq2 = [1/phi^3, -1, 0]   # x0 <= h
sage: P2,sub02 = u.induced_partition(ieq2, P)
sage: P2
Polyhedron partition of 10 atoms with 10 letters
sage: sub02
{0: [0, 2, 2],
 1: [1, 2, 2],
 2: [1, 3, 2],
 3: [1, 3, 3],
 4: [0, 2, 0, 2, 2],
 5: [0, 2, 1, 2, 2],
 6: [1, 2, 1, 2, 2],
 7: [1, 2, 1, 3, 2],
 8: [1, 3, 1, 3, 2],
 9: [1, 3, 1, 3, 3]}

We check that inductions commute:

sage: base = diagonal_matrix((phi^-2,1))
sage: translation = vector((phi^-3, 0))
sage: u1 = PET.toral_translation(base, translation)
sage: P2_alt,sub12 = u1.induced_partition(ieq2, P1)
sage: P2_alt
Polyhedron partition of 10 atoms with 10 letters
sage: P2_alt == P2
True

Up to a permutation of the alphabet, sub02 and sub01*sub12 are equal:

sage: s01 = WordMorphism(sub01)
sage: s12 = WordMorphism(sub12)
sage: s02 = WordMorphism(sub02)
sage: s02
WordMorphism: 0->022, 1->122, 2->132, 3->133, 4->02022, 5->02122, 6->12122, 7->12132, 8->13132, 9->13133
sage: s01*s12 == s02
True

By chance, the above is true, but in general, we have:

sage: perm = WordMorphism(P2.keys_permutation(P2_alt))
sage: perm
WordMorphism: 0->0, 1->1, 2->2, 3->3, 4->4, 5->5, 6->6, 7->7, 8->8, 9->9
sage: s01*s12*perm == s02
True
induced_transformation(ieq, ignore_volume=0, verbose=False)

Return the induced transformation on the domain.

INPUT:

  • ieq – list, an inequality. An entry equal to “[-1,7,3,4]” represents the inequality 7x_1+3x_2+4x_3>= 1.

  • ignore_volume – real (optional:0), stop the while loop if the volume of what’s not yet returned is less than the given threshold

  • verbose – bool (optional:False), print verbose information

OUTPUT:

  • a polyhedron exchange transformation on the subdomain

  • a substitution (dict)

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: ieq = [1/2, -1, 0]   # x0 <= 1/2
sage: T_induced,sub = T.induced_transformation(ieq)
sage: T_induced.pp()
Piecewise Affine Transformation given by a
Polyhedron partition of 6 atoms with 6 letters
defined by 6 affine maps:
Affine map 0:
      [0 1]     [  0]
x |-> [1 0] x + [2/3]
Affine map 1:
      [0 1]     [   0]
x |-> [1 0] x + [-1/3]
Affine map 2:
      [1 0]     [-1/3]
x |-> [0 1] x + [-1/3]
Affine map 3:
      [0 1]     [-1/3]
x |-> [1 0] x + [ 1/3]
Affine map 4:
      [1 0]     [ 1/3]
x |-> [0 1] x + [-2/3]
Affine map 5:
      [0 1]     [-2/3]
x |-> [1 0] x + [   0]
sage: sub
{0: [0], 1: [1], 2: [1, 1], 3: [0, 1, 1], 4: [0, 1, 1, 1], 5: [0, 1, 1, 1, 1]}
inverse()

Return the inverse of self.

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.pp()
Piecewise Affine Transformation given by a
Polyhedron partition of 2 atoms with 2 letters
defined by 2 affine maps:
Affine map 0:
      [0 1]     [  0]
x |-> [1 0] x + [2/3]
Affine map 1:
      [0 1]     [   0]
x |-> [1 0] x + [-1/3]
sage: T.inverse().pp()
Piecewise Affine Transformation given by a
Polyhedron partition of 2 atoms with 2 letters
defined by 2 affine maps:
Affine map 0:
      [0 1]     [-2/3]
x |-> [1 0] x + [   0]
Affine map 1:
      [0 1]     [1/3]
x |-> [1 0] x + [  0]
merge_atoms_with_same_transformation()

Return a new partition into convex polyhedrons where atoms mapped by the same transformation are merged if their union is convex.

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,1/2), (1,1), (h,1), (h,1/2)])
sage: r = Polyhedron([(1,0), (1,1/2), (h,1/2), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q, 2:r})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1, 2:f1})
sage: T
Piecewise Affine Transformation of
Polyhedron partition of 3 atoms with 3 letters
defined by 3 affine maps
sage: T.merge_atoms_with_same_transformation()
Piecewise Affine Transformation of
Polyhedron partition of 2 atoms with 2 letters
defined by 2 affine maps
partition()

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.partition()
Polyhedron partition of 2 atoms with 2 letters

This code also handle PETs:

sage: from slabbe import PolyhedronExchangeTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(h,0),(h,1),(0,1)])
sage: q = Polyhedron([(1,0),(h,0),(h,1),(1,1)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: d = {0:(1-h,0), 1:(-h,0)}
sage: T = PolyhedronExchangeTransformation(P, d)
sage: T.partition()
Polyhedron partition of 2 atoms with 2 letters
plot()

Return a image representating the domain and image partition side-to-side.

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.plot()
Graphics Array of size 1 x 2

Title is still placed correctly if size of domain changes:

sage: (5*T).plot()
Graphics Array of size 1 x 2

This code also works for PETs:

sage: from slabbe import PolyhedronExchangeTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(h,0),(h,1),(0,1)])
sage: q = Polyhedron([(1,0),(h,0),(h,1),(1,1)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: d = {0:(1-h,0), 1:(-h,0)}
sage: T = PolyhedronExchangeTransformation(P, d)
sage: T.plot()
Graphics Array of size 1 x 2
pp()

Pretty print

EXAMPLES:

sage: from slabbe import PolyhedronPartition
sage: from slabbe import PiecewiseAffineTransformation
sage: h = 1/3
sage: p = Polyhedron([(0,0),(0,1),(h,1),(h,0)])
sage: q = Polyhedron([(1,0), (1,1), (h,1), (h,0)])
sage: P = PolyhedronPartition({0:p, 1:q})
sage: F = AffineGroup(2, QQ)
sage: M = matrix(2, [0,1,1,0])
sage: f0 = F(M, (0, 2/3))
sage: f1 = F(M, (0, -1/3))
sage: T = PiecewiseAffineTransformation(P, {0:f0, 1:f1})
sage: T.pp()
Piecewise Affine Transformation given by a
Polyhedron partition of 2 atoms with 2 letters
defined by 2 affine maps:
Affine map 0:
      [0 1]     [  0]
x |-> [1 0] x + [2/3]
Affine map 1:
      [0 1]     [   0]
x |-> [1 0] x + [-1/3]