Skip to content

Commit

Permalink
Trac #28022: Downgrade rubiks to optional
Browse files Browse the repository at this point in the history
See discussion in
https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg
/sage-devel/lc-HqYCFrDw/r4TlV6qCBAAJ

URL: https://trac.sagemath.org/28022
Reported by: isuruf
Ticket author(s): Frédéric Chapoton, John Palmieri
Reviewer(s): Dima Pasechnik, Matthias Koeppe
  • Loading branch information
Release Manager committed Sep 9, 2020
2 parents 363f858 + a8ea381 commit 07a863d
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 59 deletions.
2 changes: 1 addition & 1 deletion build/pkgs/rubiks/SPKG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Dik T. Winter (MIT License)
solution to the 3x3x3 Rubik's cube
- size222 - solves a 2x2x2 Rubik's cube

Eric Dietz (GPL) http://www.wrongway.org/?rubiksource
Eric Dietz (GPL) https://web.archive.org/web/20121212175710/http://www.wrongway.org/?rubiksource

- cu2 - A fast, non-optimal 2x2x2 solver
- cubex - A fast, non-optimal 3x3x3 solver
Expand Down
2 changes: 1 addition & 1 deletion build/pkgs/rubiks/type
Original file line number Diff line number Diff line change
@@ -1 +1 @@
standard
optional
15 changes: 15 additions & 0 deletions src/sage/doctest/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,20 @@ def has_imagemagick():
from sage.features.imagemagick import ImageMagick
return ImageMagick().is_present()

def has_rubiks():
"""
Test if the rubiks package (``cu2``, ``cubex``, ``dikcube``,
``mcube``, ``optimal``, and ``size222``) is available.
EXAMPLES::
sage: from sage.doctest.external import has_rubiks
sage: has_rubiks() # optional -- rubiks
FeatureTestResult('Rubiks', True)
"""
from sage.features.rubiks import Rubiks
return Rubiks().is_present()

def external_software():
"""
Return the alphabetical list of external software supported by this module.
Expand Down Expand Up @@ -345,6 +359,7 @@ class AvailableSoftware(object):
'matlab',
'octave',
'pandoc',
'rubiks',
'scilab']
sage: 'internet' in available_software # random, optional - internet
True
Expand Down
192 changes: 192 additions & 0 deletions src/sage/features/rubiks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
r"""
Check for rubiks
"""
# ****************************************************************************
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************
from . import Feature, Executable, FeatureTestResult

class cu2(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``cu2``
EXAMPLES::
sage: from sage.features.rubiks import cu2
sage: cu2().is_present() # optional: rubiks
FeatureTestResult('cu2', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import cu2
sage: isinstance(cu2(), cu2)
True
"""
Executable.__init__(self, "cu2", executable="cu2",
spkg="rubiks")


class size222(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``size222``
EXAMPLES::
sage: from sage.features.rubiks import size222
sage: size222().is_present() # optional: rubiks
FeatureTestResult('size222', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import size222
sage: isinstance(size222(), size222)
True
"""
Executable.__init__(self, "size222", executable="size222",
spkg="rubiks")


class optimal(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``optimal``
EXAMPLES::
sage: from sage.features.rubiks import optimal
sage: optimal().is_present() # optional: rubiks
FeatureTestResult('optimal', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import optimal
sage: isinstance(optimal(), optimal)
True
"""
Executable.__init__(self, "optimal", executable="optimal",
spkg="rubiks")


class mcube(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``mcube``
EXAMPLES::
sage: from sage.features.rubiks import mcube
sage: mcube().is_present() # optional: rubiks
FeatureTestResult('mcube', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import mcube
sage: isinstance(mcube(), mcube)
True
"""
Executable.__init__(self, "mcube", executable="mcube",
spkg="rubiks")


class dikcube(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``dikcube``
EXAMPLES::
sage: from sage.features.rubiks import dikcube
sage: dikcube().is_present() # optional: rubiks
FeatureTestResult('dikcube', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import dikcube
sage: isinstance(dikcube(), dikcube)
True
"""
Executable.__init__(self, "dikcube", executable="dikcube",
spkg="rubiks")


class cubex(Executable):
r"""
A :class:`sage.features.Executable` describing the presence of
``cubex``
EXAMPLES::
sage: from sage.features.rubiks import cubex
sage: cubex().is_present() # optional: rubiks
FeatureTestResult('cubex', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import cubex
sage: isinstance(cubex(), cubex)
True
"""
Executable.__init__(self, "cubex", executable="cubex",
spkg="rubiks")


class Rubiks(Feature):
r"""
A :class:`sage.features.Feature` describing the presence of
``cu2``, ``cubex``, ``dikcube``, ``mcube``, ``optimal``, and
``size222``.
EXAMPLES::
sage: from sage.features.rubiks import Rubiks
sage: Rubiks().is_present() # optional: rubiks
FeatureTestResult('Rubiks', True)
"""
def __init__(self):
r"""
TESTS::
sage: from sage.features.rubiks import Rubiks
sage: isinstance(Rubiks(), Rubiks)
True
"""
Feature.__init__(self, "Rubiks",
spkg="rubiks")

def _is_present(self):
r"""
EXAMPLES::
sage: from sage.features.rubiks import Rubiks
sage: Rubiks()._is_present() # optional: rubiks
FeatureTestResult('Rubiks', True)
"""
test = (cu2()._is_present() and
size222()._is_present() and
optimal()._is_present() and
mcube()._is_present() and
dikcube()._is_present() and
cubex()._is_present())
if not test:
return test
else:
return FeatureTestResult(self, True)
41 changes: 24 additions & 17 deletions src/sage/groups/perm_gps/cubegroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ def parse(self, mv, check=True):
mv = mv.strip().replace(" ","*").replace("**", "*").replace("'", "-1").replace('^','').replace('(','').replace(')','')
M = mv.split("*")
for m in M:
if len(m) == 0:
if not m:
pass
elif len(m) == 1:
g *= map[m[0]]
Expand Down Expand Up @@ -837,7 +837,7 @@ def move(self, mv):
g = self.parse(mv)
return g, self.facets(g)

def display2d(self,mv):
def display2d(self, mv):
r"""
Print the 2d representation of ``self``.
Expand Down Expand Up @@ -950,7 +950,7 @@ def plot_cube(self, mv, title=True, colors = [lpurple, yellow, red, green, orang
return P
return clrs

def plot3d_cube(self,mv,title=True):
def plot3d_cube(self, mv, title=True):
r"""
Displays `F,U,R` faces of the cube after the given move ``mv``. Mostly
included for the purpose of drawing pictures and checking moves.
Expand Down Expand Up @@ -993,7 +993,7 @@ def plot3d_cube(self,mv,title=True):
return P
return P

def legal(self,state,mode="quiet"):
def legal(self, state, mode="quiet"):
r"""
Return 1 (true) if the dictionary ``state`` (in the
same format as returned by the faces method) represents a legal
Expand Down Expand Up @@ -1023,9 +1023,9 @@ def legal(self,state,mode="quiet"):
else:
return res

def solve(self,state, algorithm='default'):
def solve(self, state, algorithm='default'):
r"""
Solves the cube in the ``state``, given as a dictionary
Solve the cube in the ``state``, given as a dictionary
as in ``legal``. See the ``solve`` method
of the RubiksCube class for more details.
Expand Down Expand Up @@ -1082,7 +1082,7 @@ def solve(self,state, algorithm='default'):
return C.solve(algorithm)

hom = self._gap_().EpimorphismFromFreeGroup()
soln = hom.PreImagesRepresentative(gap(str(g)))
soln = hom.PreImagesRepresentative(str(g))
sol = str(soln)
names = self.gen_names()
for i in range(6):
Expand Down Expand Up @@ -1190,7 +1190,7 @@ class RubiksCube(SageObject):
+--------------+
sage: C.show()
sage: C.solve(algorithm='gap') # long time
'L R'
'L*R'
sage: C == RubiksCube("L*R")
True
"""
Expand Down Expand Up @@ -1241,7 +1241,7 @@ def undo(self):
sage: D.undo() == C
True
"""
if len(self._history) == 0:
if not self._history:
raise ValueError("no moves to undo")
g = self._history[-1]
return RubiksCube(self._state * ~g, self._history[:-1], self.colors)
Expand Down Expand Up @@ -1307,7 +1307,7 @@ def show(self):
"""
self.plot().show()

def cubie(self, size, gap, x,y,z, colors, stickers=True):
def cubie(self, size, gap, x, y, z, colors, stickers=True):
"""
Return the cubie at `(x,y,z)`.
Expand Down Expand Up @@ -1413,22 +1413,30 @@ def solve(self, algorithm="hybrid", timeout=15):
(may take a long time)
- ``gap`` - Use GAP word solution (can be slow)
Any choice other than ``gap`` requires the optional package
``rubiks``. Otherwise, the ``gap`` algorithm is used.
EXAMPLES::
sage: C = RubiksCube("R U F L B D")
sage: C.solve()
sage: C.solve() # optional - rubiks
'R U F L B D'
Dietz's program is much faster, but may give highly non-optimal
solutions::
sage: s = C.solve('dietz'); s
sage: s = C.solve('dietz'); s # optional - rubiks
"U' L' L' U L U' L U D L L D' L' D L' D' L D L' U' L D' L' U L' B' U' L' U B L D L D' U' L' U L B L B' L' U L U' L' F' L' F L' F L F' L' D' L' D D L D' B L B' L B' L B F' L F F B' L F' B D' D' L D B' B' L' D' B U' U' L' B' D' F' F' L D F'"
sage: C2 = RubiksCube(s)
sage: C == C2
sage: C2 = RubiksCube(s) # optional - rubiks
sage: C == C2 # optional - rubiks
True
"""
import sage.interfaces.rubik # here to avoid circular referencing
from sage.features.rubiks import Rubiks
if Rubiks().is_present():
import sage.interfaces.rubik # here to avoid circular referencing
else:
algorithm = 'gap'

if algorithm == "default":
algorithm = "hybrid"

Expand All @@ -1455,7 +1463,7 @@ def solve(self, algorithm="hybrid", timeout=15):

elif algorithm == "gap":
solver = CubeGroup()
return solver.solve(self._state)
return solver.solve(self._state, algorithm="gap")

else:
raise ValueError("Unrecognized algorithm: %s" % algorithm)
Expand Down Expand Up @@ -1491,4 +1499,3 @@ def scramble(self, moves=30):
last_move = move
all.append(move)
return self.move(' '.join(all))

Loading

0 comments on commit 07a863d

Please sign in to comment.