Opened 4 years ago

Closed 3 years ago

#24152 closed defect (fixed)

Bug when converting a Sage polyhedron into Polymake pexpect

Reported by: SimonKing Owned by:
Priority: major Milestone: sage-8.2
Component: interfaces: optional Keywords: polymake, IMA-PolyGeom
Cc: vdelecroix, mkoeppe, jipilab Merged in:
Authors: Matthias Koeppe Reviewers: Jean-Philippe Labbé
Report Upstream: N/A Work issues:
Branch: a924632 (Commits, GitHub, GitLab) Commit: a9246328ef3f80bca645020f871cfc0835fb605c
Dependencies: Stopgaps:

Status badges

Description (last modified by mkoeppe)

Conversion of a Sage polyhedron into Polymake fails in the first attempt, but succeeds in the second attempt. I've put the example into the first comment rather than in the ticket description.

The fix is to install the File::Slurp module into perl5. This ticket updates the information about prerequisites (and how to install them) in build/pkg/polymake/SPKG.txt.

Change History (16)

comment:1 Changed 4 years ago by SimonKing

sage: eqns = [
....: [-100, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
....: [0, -1, 0, 0, 0, 0, -1, 1, 1, 0, 0, 0, 0],
....: [0, 0, 0, 0, 0, -1, 0, 0, 1, 1, -1, 0, 0],
....: [0, 0, 0, 0, -1, 0, -1, 1, 0, 1, 0, 0, 0],
....: [0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, 1, -1],
....: [0, 0, -1, -1, 0, 1, 0, 0, 0, 0, 0, 1, 0],
....: [0, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1],
....: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
....: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
....: ]
sage: ieqs = [
....: [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
....: [-1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
....: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
....: [-1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
....: [-1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
....: [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
....: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
....: [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
....: ]
sage: P = Polyhedron(eqns = eqns, ieqs=ieqs)
sage: PP = polymake(P)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-a1de8e99de0e> in <module>()
----> 1 PP = polymake(P)

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/interface.pyc in __call__(self, x, name)
    282             return cls(self, x, name=name)
    283         try:
--> 284             return self._coerce_from_special_method(x)
    285         except TypeError:
    286             raise

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/interface.pyc in _coerce_from_special_method(self, x)
    308             s = '_gp_'
    309         try:
--> 310             return (x.__getattribute__(s))(self)
    311         except AttributeError:
    312             return self(x._interface_init_())

/home/king/Sage/git/sage/src/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._polymake_ (build/cythonized/sage/structure/sage_object.c:11355)()
    937             import sage.interfaces.polymake
    938             G = sage.interfaces.polymake.polymake
--> 939         return self._interface_(G)
    940 
    941     def _polymake_init_(self):

/home/king/Sage/git/sage/src/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._interface_ (build/cythonized/sage/structure/sage_object.c:6528)()
    735         init_func = getattr(self, '_%s_init_' % nm, None)
    736         if init_func is not None:
--> 737             s = init_func()
    738         else:
    739             try:

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/geometry/polyhedron/base.pyc in _polymake_init_(self)
   6204         """
   6205         from sage.interfaces.polymake import polymake
-> 6206         polymake_field = polymake(self.base_ring().fraction_field())
   6207         polymake_class = "Polytope<{}>".format(polymake_field)
   6208         if self.is_empty():

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/interface.pyc in __call__(self, x, name)
    282             return cls(self, x, name=name)
    283         try:
--> 284             return self._coerce_from_special_method(x)
    285         except TypeError:
    286             raise

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/interface.pyc in _coerce_from_special_method(self, x)
    308             s = '_gp_'
    309         try:
--> 310             return (x.__getattribute__(s))(self)
    311         except AttributeError:
    312             return self(x._interface_init_())

/home/king/Sage/git/sage/src/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._polymake_ (build/cythonized/sage/structure/sage_object.c:11355)()
    937             import sage.interfaces.polymake
    938             G = sage.interfaces.polymake.polymake
--> 939         return self._interface_(G)
    940 
    941     def _polymake_init_(self):

/home/king/Sage/git/sage/src/sage/structure/sage_object.pyx in sage.structure.sage_object.SageObject._interface_ (build/cythonized/sage/structure/sage_object.c:6726)()
    741             except Exception:
    742                 raise NotImplementedError("coercion of object %s to %s not implemented:\n%s\n%s" % (repr(self), I))
--> 743         X = I(s)
    744         if c:
    745             try:

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/interface.pyc in __call__(self, x, name)
    280 
    281         if isinstance(x, string_types):
--> 282             return cls(self, x, name=name)
    283         try:
    284             return self._coerce_from_special_method(x)

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/expect.pyc in __init__(self, parent, value, is_name, name)
   1389             except (RuntimeError, ValueError) as x:
   1390                 self._session_number = -1
-> 1391                 raise_(TypeError, x, sys.exc_info()[2])
   1392             except BaseException:
   1393                 self._session_number = -1

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/expect.pyc in __init__(self, parent, value, is_name, name)
   1384         else:
   1385             try:
-> 1386                 self._name = parent._create(value, name=name)
   1387             # Convert ValueError and RuntimeError to TypeError for
   1388             # coercion to work properly.

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/polymake.pyc in _create(self, value, name)
    617         """
    618         name = self._next_var_name() if name is None else name
--> 619         self.set(name, value)
    620         # If value is a list, then @name is now equal to that list.
    621         # Otherwise, value is obtained by $name[0]. So, we modify

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/polymake.pyc in set(self, var, value)
    706             value = value.strip().rstrip(';').strip()
    707         cmd = '@%s%s(%s);'%(var,self._assign_symbol(), value)
--> 708         self.eval(cmd)
    709 
    710     def get(self, cmd):

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/expect.pyc in eval(self, code, strip, synchronize, locals, allow_use_file, split_lines, **kwds)
   1298                 elif split_lines:
   1299                     return '\n'.join([self._eval_line(L, allow_use_file=allow_use_file, **kwds)
-> 1300                                         for L in code.split('\n') if L != ''])
   1301                 else:
   1302                     return self._eval_line(code, allow_use_file=allow_use_file, **kwds)

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/polymake.pyc in _eval_line(self, line, allow_use_file, wait_for_prompt, restart_if_needed, **kwds)
    891         try:
    892             if not self.is_running():
--> 893                 self._start()
    894             E = self._expect
    895             try:

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/polymake.pyc in _start(self, alt_message)
    357         self.eval('use Scalar::Util qw(reftype);')
    358         self.eval('use Scalar::Util qw(blessed);')
--> 359         self.eval('use File::Slurp;')
    360 
    361     def _quit_string(self):

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/expect.pyc in eval(self, code, strip, synchronize, locals, allow_use_file, split_lines, **kwds)
   1298                 elif split_lines:
   1299                     return '\n'.join([self._eval_line(L, allow_use_file=allow_use_file, **kwds)
-> 1300                                         for L in code.split('\n') if L != ''])
   1301                 else:
   1302                     return self._eval_line(code, allow_use_file=allow_use_file, **kwds)

/home/king/Sage/git/sage/local/lib/python2.7/site-packages/sage/interfaces/polymake.pyc in _eval_line(self, line, allow_use_file, wait_for_prompt, restart_if_needed, **kwds)
   1057             warnings.warn(w, RuntimeWarning)
   1058         for e in p_errors:
-> 1059             raise PolymakeError(e)
   1060         return out
   1061 

TypeError: Can't locate File/Slurp.pm in @INC (you may need to install the File::Slurp module) (@INC contains: CODE(0x316ca38) /home/king/Sage/git/sage/local/share/polymake/perllib /home/king/Sage/git/sage/local/lib/polymake/perlx/5.22.1/x86_64-linux-gnu-thread-multi /home/king/Sage/git/sage/local/lib/polymake/perlx/5.22.1 /home/king/Sage/git/sage/local/lib/polymake/perlx /home/king/Sage/git/sage/local/lib/perl5/x86_64-linux-gnu-thread-multi /home/king/Sage/git/sage/local/lib/perl5 /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.22.1 /usr/local/share/perl/5.22.1 /usr/lib/x86_64-linux-gnu/perl5/5.22 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.22 /usr/share/perl/5.22 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base .) at input line 1.
BEGIN failed--compilation aborted at input line 1.
sage: PP = polymake(P)
sage: PP.VERTICES
1 1 1 46/3 1 49/3 46/3 0 49/3 49/3 49/3 0 1
1 45/2 45/2 1 1 47/2 1 0 47/2 2 2 0 1
1 45/2 1 1 1 2 1 0 47/2 2 47/2 0 45/2
1 1 1 1 45/2 2 1 0 2 47/2 47/2 0 45/2
1 1 45/2 1 45/2 47/2 1 0 2 47/2 2 0 1
sage: P.vertices_list()
[[1, 1, 46/3, 1, 49/3, 46/3, 0, 49/3, 49/3, 49/3, 0, 1],
 [45/2, 45/2, 1, 1, 47/2, 1, 0, 47/2, 2, 2, 0, 1],
 [45/2, 1, 1, 1, 2, 1, 0, 47/2, 2, 47/2, 0, 45/2],
 [1, 1, 1, 45/2, 2, 1, 0, 2, 47/2, 47/2, 0, 45/2],
 [1, 45/2, 1, 45/2, 47/2, 1, 0, 2, 47/2, 2, 0, 1]]
sage: PP.N_LATTICE_POINTS
1260
sage: P.integral_points_count()
1260

Note that a user reported that the above point count is incorrect in Sage, that's why I tested what polymake has to say (and find that it agrees with Sage).

So, the bug is that the first attempt of converting P fails, while the second attempt succeeds.

comment:2 Changed 4 years ago by SimonKing

  • Cc mkoeppe added

What does self.eval('use File::Slurp;') mean?

git blame tells me that the line was introduced by Matthias, so CCing him.

comment:3 follow-up: Changed 4 years ago by mkoeppe

It's loading a Perl package. http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm

We need it for stuff passed by file.

Sage does not control the Perl installation. Should perhaps add a line to the polymake SPKG.txt to alert the user that this package needs to be installed (in addition to a few others that are already listed).

comment:4 in reply to: ↑ 3 Changed 4 years ago by SimonKing

Replying to mkoeppe:

Sage does not control the Perl installation. Should perhaps add a line to the polymake SPKG.txt to alert the user that this package needs to be installed (in addition to a few others that are already listed).

... and HOW it can be installed. The search "perl install slurp" doesn't give me immediate answer.

comment:5 Changed 4 years ago by SimonKing

  1. I did sudo apt-get install libperl6-slurp-perl which I think is supposed to install the slurp module on ubuntu.
  2. The example from comment:1 doesn't work after installing slurp: Polymake still cannot find slurp.
  3. So, we should tell the user how to install slurp AND how to make polymake aware of it.
  4. All that still doesn't explain why the example fails once and then just works. Apparently the interface can do without slurp, but doesn't.

comment:6 follow-up: Changed 4 years ago by mkoeppe

Polymake uses Perl5, not 6.

comment:7 in reply to: ↑ 6 Changed 4 years ago by SimonKing

Replying to mkoeppe:

Polymake uses Perl5, not 6.

Which makes me repeat the question: How to install the slurp module?

$ apt-cache search perl slurp
hobbit-plugins - plugins for the Xymon network monitor
libdbix-simple-perl - Perl module implementing an simpler interface to DBI
libfile-policy-perl - Simple policy for file I/O functions
libfile-read-perl - interface for reading one or more files
libfile-slurp-perl - single call read & write file routines
libfile-slurp-tiny-perl - simple, sane and efficient file slurper
libfile-slurp-unicode-perl - Perl module to add Unicode support for the File::Slurp package
libfile-slurper-perl - simple, sane and efficient module to slurp a file
libperl6-slurp-perl - module implementing the Perl 6 'slurp' built-in
libtext-clip-perl - module to clip and extract text in clipboard-like way

Which of the above do I need to install? Hint: It isn't libfile-slurper-perl either.

comment:8 Changed 4 years ago by SimonKing

sudo apt-get install libfile-read-perl seems to do the trick. Afterwards, I get

sage: polymake(Polyhedron(eqns = eqns, ieqs=ieqs))
Polytope<Rational>[SAGE271]
sage: _.N_INTERIOR_LATTICE_POINTS
952
sage: __.N_LATTICE_POINTS
1260

Anyway.

I find it not good that the polymake interface depends on a specific perl module upon which polymake itself does not depend.

Is it really not possible to provide the same functionality (perhaps slower) if slurp is not installed? If there is a way around slurp then that should be used as a fallback.

And if there is no way around slurp, then the polymake interface should complain about it right in the beginning, giving specific advice on how to install slurp, alerting the user that (s)he has to take care about perl5, not perl6, and so on.

comment:9 Changed 4 years ago by SimonKing

  • Description modified (diff)

comment:10 Changed 4 years ago by mkoeppe

I'd guess (but haven't tested because I am not on Ubuntu) that it would simply be libfile-slurp-perl. The one that you picked probably installs it as a dependency.

The distribution-independent way of installing Perl packages, of course, is using CPAN.

Specific error handling for failure to load File::Slurp is a good idea.

One could, of course, provide a simplified version of read_file directly, instead of requiring File::Slurp. But I don't think it's worth it.

comment:11 Changed 3 years ago by mkoeppe

  • Branch set to u/mkoeppe/bug_when_converting_a_sage_polyhedron_into_polymake_pexpect

comment:12 Changed 3 years ago by mkoeppe

  • Authors set to Matthias Koeppe
  • Cc jipilab added
  • Commit set to a9246328ef3f80bca645020f871cfc0835fb605c
  • Description modified (diff)
  • Milestone changed from sage-8.1 to sage-8.2
  • Status changed from new to needs_review

New commits:

a924632polymake SPKG.txt: Mention File::Slurp and CPAN

comment:13 Changed 3 years ago by mkoeppe

Needs review and fixes the problem reported by jipilab on #24905.

comment:14 Changed 3 years ago by jipilab

  • Reviewers set to Jean-Philippe Labbé
  • Status changed from needs_review to positive_review

This solved the problem raised in #24905 and the above failing example works as well on my computer. This looks good to go.

comment:15 Changed 3 years ago by mkoeppe

  • Keywords IMA-PolyGeom added

comment:16 Changed 3 years ago by vbraun

  • Branch changed from u/mkoeppe/bug_when_converting_a_sage_polyhedron_into_polymake_pexpect to a9246328ef3f80bca645020f871cfc0835fb605c
  • Resolution set to fixed
  • Status changed from positive_review to closed
Note: See TracTickets for help on using tickets.