#8987: Add support for rational polyhedral fans
----------------------------------+-----------------------------------------
   Reporter:  novoselt            |       Owner:  mhampton  
       Type:  enhancement         |      Status:  needs_work
   Priority:  major               |   Milestone:  sage-4.4.4
  Component:  geometry            |    Keywords:            
     Author:  Andrey Novoseltsev  |    Upstream:  N/A       
   Reviewer:  Volker Braun        |      Merged:            
Work_issues:                      |  
----------------------------------+-----------------------------------------
Changes (by novoselt):

  * status:  needs_review => needs_work


Comment:

 Replying to [comment:9 vbraun]:
 > 1) I agree that we should test `==` without identifying `GL(n,Z)` images
 as it is expensive. But I'm afraid of the following, which is probably a
 common use case:
 {{{
 sage: diamond = lattice_polytope.octahedron(2)
 sage: P1xP1 = FaceFan(diamond)
 sage: Cone([(0,1), (1,0)]) in P1xP1(dim=2)
 False
 sage: Cone([(1,0), (0,1)]) in P1xP1(dim=2)
 True
 }}}

 You got a point here, I don't like this behaviour...

 > Also, note that `Fan()` is implicitly sorting:
 {{{
 sage: fan1 = Fan([Cone([(1,0), (0,1)])])
 sage: fan2 = Fan([Cone([(0,1), (1,0)])])
 sage: fan1==fan2
 True
 }}}

 There was no sorting, rays were determined first as a set (union of ray
 sets of all cones) and then converted to a tuple. I am not sure if the
 same set will always lead to the same tuple or it depends on something. I
 definitely don't want to force sorting - if a user gives rays in a
 specific order, then (s)he probably wants them in that order.


 > I would argue that is is ok to treat fans and cones a bit different, one
 will compare cones often but fans only seldomly. I'm happy with comparison
 of fans to depend on the order of the rays and generating cones, otherwise
 all derived quantities (like cohomology generators of toric varieties)
 will not be equal (only isomorphic). But for `cone1==cone2` to depend on
 the order of the rays is neither helpful nor intuitive.

 In general, I agree, but it may potentially lead to `cone1 == cone2` while
 `Fan([cone1]) != Fan([cone2])`. Are you OK with this?

 As I understand, it is not enough to just add `__eq__` method to cones,
 since it will conflict wiht `__cmp__` in the sense that it will be
 possible to have `cone1 < cone2` and `cone1 == cone2` at the same time,
 which is not cool. In fact, I do not see any good (and fast) way to
 compare cones in agreement with mathematical equivalence. It will require
 constructing `Polyhedron` objects for each cone to check strict convexity
 and deal with possible non-uniqueness of ray generators, this will make it
 highly undesirable to sort sequences of cones. It will also make hashing
 of cones either slow or inconsistent with equality check (which is
 accepted in Sage, but should not be the case unless necessary).

 Yeah... I think my position is: I will do this change if you still want it
 after arguments above, but I am very against it, especially since a method
 performing mathematical equality check is provided. A compromise is to add
 `__contains__` and `contains` to the `Fan` class so that one can do

 {{{
 sage: diamond = lattice_polytope.octahedron(2)
 sage: P1xP1 = FaceFan(diamond)
 sage: Cone([(0,1), (1,0)]) in P1xP1   # no (dim=2)
 True
 sage: Cone([(1,0), (0,1)]) in P1xP1   # no (dim=2)
 True
 }}}

 It would be also nice, perhaps, to be able to check points in such a way,
 i.e. if a point is in the support of a fan.

 > 2) `Fan()` should always error out if the cones are not generating
 cones. In particular, this one from the documentation
 > {{{
 > sage: cone1 = Cone([(1,0), (0,1)])
 > sage: cone2 = Cone([(0,1), (-1,-1)])
 > sage: cone3 = Cone([(-1,-1), (1,0)])
 > sage: P2 = Fan([cone1, cone2, cone2])
 > sage: P2.ngenerating_cones()
 > 2
 > }}}
 > should have raised an exception.
 >
 > In normal use you'll never want to type in all the cones of the fan
 since there are so many. But its easy to get confused and add a cone that
 turns out to be not a generating cone, and its nice to catch this when
 generating the `Fan`.
 >
 > There is certainly a need for a function that extracts the generating
 cones from a collection of cones, but I don't think it should be implicit
 in the Fan constructor.

 My intention was to write a fan constructor that will construct a fan if
 at all possible. (Similarly, the cone constructor by default will discard
 non-generating rays.) One may, for example, start with some fan and add
 more cones to it. In this case some of the old generating cones may become
 unnecessary. Or, if cones of a fan come one by one from a certain
 procedure (e.g. in computing GKZ decomposition) and it is not known in
 advance how to easily select generating cones (e.g. all generating cones
 are full-dimensional for complete fans), it would be nice if the fan
 constructor could automatically select necessary cones. If you really
 against silent discard, we can perhaps either add a warning about a non-
 generating cone present, or a default parameter `generating_cones=True` to
 `Fan(...)` and then throw an exception if there are such cones. If one
 (like me ;-)) wants to discard them, it will be possible to use
 `generating_cones=False` option.

 > 3) Rename `Cone_of_fan.fan_generating_cones()` to `star_generators()`.

 OK.

 > There are similar methods to this one that we probably want to add
 later, so let me just throw out some names:
 >   * `Cone_of_fan.faces()`: all subcones of the cone
 >   * `Cone_of_fan.facets()`: subcones of one dimension lower

 Note that these are inherited from plain cones. However, they are likely
 to be `ConeFace` objects and for fans it would make more sense to return
 other `Cone_of_fan` ones...

 >   * `Cone_of_fan.bounds()`: supercones of one dimension higher

 I don't like the name. I guess it means that `self` bounds those that are
 returned by this function. But it can also be interpreted as that it
 returns bounds of `self` in the sense its facets. How about `facet_of` for
 this one?

 >   * `Cone_of_fan.star()`: the star of the cone

 What exactly do you mean by `star` here? Do you want to get back the fan
 in the quotient lattice? Anything else attached to it? I think we can use
 coercion system to define the canonical map from the lattice of the
 original fan to this one.

 >   * `Cone_of_fan.adjacent()`: Adjacent cones (of the same dimension)

 Is this name standard? I see that it may be convenient to have such a
 function, but I am not sure I would guess from the name what it does.

 > 4) Do we need to know the set of ray indices, that is
 `set(cone.fan_rays())`, of a cone often? Right now there is the function
 `cone_to_rays(cone_index)` that is only used as a helper in
 `cone_lattice()`. If it is just a helper function, then it should be
 `_cone_to_rays()`. If you want to expose that functionality, it should be
 stored in the `Cone_of_fan` and retrieved via `cone.fan_rays_set()` or so.

 I think it is just a helper and can go to `_cone_to_rays` to clean up the
 namespace.

 > I also think that `cone.rays_idx()` would be a better name than
 `cone.fan_rays()`, but if you disagree then I can live with the current
 name as well :-)

 I disagree, because `idx` is cryptic (indices?) and `fan_rays` clearly
 tells one that it has something to do with the fan, even if it is not
 obvious that it will return indices rather than rays. (But what else can
 one expect? Rays themselves have no explicit relation to the fan.)

 > 5) similarly to 4), `ray_to_cones()` is not very self-explanatory.

 Agreed.

 > We obviously need a way to find out which cones contain a given set of
 rays. I would prefer one (or all) of the following
 >   * `ray_to_cone(ray_index)`: the 1-cone spanned by the ray

 I was tempted to say that
 {{{
 sage: fan(1)[i]
 }}}
 will do exactly this, but it will not since `fan(1)` purposefully returns
 the list of rays, rather than one-dimensional cones... Did you notice this
 convention? What do you think of it?

 Well, I think at least
 {{{
 sage: fan.cones(1)[i]
 }}}
 should do this work, even if it may fail to do so now because there is no
 particular sorting of 1-dimensional cones in the lattice. This actually
 seriously bugs me, since it was very annoying in `LatticePolytope` until I
 fixed it. Fixing it for cones and fans was on my todo list for the future,
 but I guess the best time to do it is now. Assuming that this will be
 done, I propose to get rid of `ray_to_cones` (or perhaps rename it to
 `_ray_to_cones`) and do not add `ray_to_cone`.

 >   * `smallest_cone_containing(*Nlist)`: the smallest cone containing all
 points (which can be specified by a ray index or as a N-lattice element).

 Can I remove `smallest_`? I think since `cone_containing` promises to
 return a single cone, it is quite reasonable to expect that it will be
 smallest (and of course it will be written in the documentation).

 > The functionality of `ray_to_cones` would then be provided by
 `ray_to_cone(i).star_generators()`.

 Or, as I propose
 {{{
 fan.cones(1)[i].star_generators()
 }}}

 Let me know what you think now!

 I am switching this ticket back to needs work. Regarding the last two
 tickets in the sequence - while I am working on this one, you can probably
 go ahead and review them too in the sense of comments. They will likely
 need some minor changes after updating this patch, but I currently don't
 plan to do anything else unless you request it.

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/8987#comment:11>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sage-trac?hl=en.

Reply via email to