Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions mathics/builtin/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,26 @@ class OutputSizeLimit(Predefined):
To set no limit on output size, use $OutputSizeLimit = Infinity.
</dl>

>> $OutputSizeLimit = 100;
>> Table[i, {i, 1, 100}]
= {1, 2, 3, 4, 5, <<90>>, 96, 97, 98, 99, 100}
>> $OutputSizeLimit = 10;
>> $OutputSizeLimit = 50;

>> Table[i, {i, 1, 100}]
= {1, <<98>>, 100}
>> $OutputSizeLimit = Infinity;
= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, <<71>>, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}

#> {}
= {}


#> $OutputSizeLimit = 100;

#> Table[Graphics[Table[Circle[],{10}]], {5}]
= {-Graphics-, -Graphics-, -Graphics-, -Graphics-, -Graphics-}

#> Quiet[ImageAvailable = SameQ[Head[Image[{{0, 1}, {1, 0}}] // ToBoxes], ImageBox]];
#> If[ImageAvailable, Table[Image[{{1, 0}, {0, 1}}], {5}], {"-Image-", "-Image-", "-Image-", "-Image-", "-Image-"}]
= {-Image-, -Image-, -Image-, -Image-, -Image-}

#> $OutputSizeLimit = Infinity;

"""

name = '$OutputSizeLimit'
Expand Down
2 changes: 2 additions & 0 deletions mathics/builtin/inout.py
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,8 @@ class General(Builtin):
'invalidargs': "Invalid arguments.",

'notboxes': "`1` is not a valid box structure.",
'omit': "Parts of this output were omitted (see `1`). " +
"To generate the whole output, please set $OutputSizeLimit = Infinity.",

'pyimport': "`1`[] is not available. Your Python installation misses the \"`2`\" module.",
}
Expand Down
9 changes: 6 additions & 3 deletions mathics/core/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import itertools

from mathics import settings
from mathics.core.expression import ensure_context, KeyComparable, make_boxes_strategy
from mathics.core.expression import ensure_context, KeyComparable, make_boxes_strategy, Omissions

FORMATS = ['StandardForm', 'FullForm', 'TraditionalForm',
'OutputForm', 'InputForm',
Expand Down Expand Up @@ -178,7 +178,7 @@ def __init__(self, definitions=None,

self.quiet_all = False
self.format = format
self.boxes_strategy = make_boxes_strategy(None, self)
self.boxes_strategy = make_boxes_strategy(None, None, self)
self.catch_interrupt = catch_interrupt

def parse(self, query):
Expand Down Expand Up @@ -329,7 +329,8 @@ def format_output(self, expr, format=None):
old_boxes_strategy = self.boxes_strategy
try:
capacity = self.definitions.get_config_value('System`$OutputSizeLimit')
self.boxes_strategy = make_boxes_strategy(capacity, self)
omissions = Omissions()
self.boxes_strategy = make_boxes_strategy(capacity, omissions, self)

options = {}

Expand All @@ -356,6 +357,8 @@ def format_output(self, expr, format=None):
self.message('General', 'notboxes',
Expression('FullForm', result).evaluate(self))
boxes = None

omissions.warn(self)
finally:
self.boxes_strategy = old_boxes_strategy

Expand Down
78 changes: 70 additions & 8 deletions mathics/core/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ def system_symbols_dict(d):
return {ensure_context(k): v for k, v in six.iteritems(d)}


_layout_boxes = system_symbols(
'RowBox',
'SuperscriptBox',
'SubscriptBox',
'SubsuperscriptBox',
'FractionBox',
'SqrtBox')


class BoxError(Exception):
def __init__(self, box, form):
super(BoxError, self).__init__(
Expand Down Expand Up @@ -311,6 +320,9 @@ def format(self, evaluation, form):
'MakeBoxes', expr, Symbol(form)).evaluate(evaluation)
return result

def output_cost(self):
return 0

def is_free(self, form, evaluation):
from mathics.core.pattern import StopGenerator

Expand Down Expand Up @@ -1155,6 +1167,21 @@ def block(tex, only_subsup=False):
else:
raise BoxError(self, 'tex')

def output_cost(self):
name = self.get_head_name()

if name in ('System`ImageBox', 'System`GraphicsBox', 'System`Graphics3DBox'):
return 0 # always display

cost = sum(leaf.output_cost() for leaf in self.leaves)

if name == 'System`List':
return 2 + cost + len(self.leaves) # {a, b, c}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be better to compute the sum cost of leaves? Also, what about the size of ", " between leaves?

elif name in _layout_boxes:
return cost
else:
return cost + 2 + self.head.output_cost() + len(self.leaves) # XYZ[a, b, c]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here also

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cost is the sum cost of leaves (I renamed it now). len(self.leaves) measures the size of ",", which could be one or two (it assumes for now). we could pass the separator size into make_boxes and then pass it on to output_cost, but it's bulky and then should we really measure it that way? If $OutputSizeLimit is understood as number of printable characters (without spaces and layout elements like fractions, parentheses, ...), then it makes sense to always measure 1 for ",".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case I guess technically it should be max(len(self.leaves) - 1, 0).


def default_format(self, evaluation, form):
return '%s[%s]' % (self.head.default_format(evaluation, form),
', '.join([leaf.default_format(evaluation, form)
Expand Down Expand Up @@ -1435,6 +1462,9 @@ def __str__(self):
def do_copy(self):
return Symbol(self.name)

def output_cost(self):
return len(self.name)

def boxes_to_text(self, **options):
return str(self.name)

Expand Down Expand Up @@ -1596,6 +1626,9 @@ def boxes_to_text(self, **options):
def boxes_to_xml(self, **options):
return self.make_boxes('MathMLForm').boxes_to_xml(**options)

def output_cost(self):
return len(str(self.value))

def boxes_to_tex(self, **options):
return str(self.value)

Expand Down Expand Up @@ -1665,6 +1698,9 @@ def __new__(cls, numerator, denominator=None):
self.value = sympy.Rational(numerator, denominator)
return self

def output_cost(self):
return len(str(self.value))

def atom_to_boxes(self, f, evaluation):
return self.format(evaluation, f.get_name())

Expand Down Expand Up @@ -1771,6 +1807,9 @@ def __new__(cls, value, p=None):
else:
return PrecisionReal.__new__(PrecisionReal, value)

def output_cost(self):
return len(str(self.value))

def boxes_to_text(self, **options):
return self.make_boxes('System`OutputForm').boxes_to_text(**options)

Expand Down Expand Up @@ -2134,6 +2173,9 @@ def __new__(cls, value):
def __str__(self):
return '"%s"' % self.value

def output_cost(self):
return len(self.value)

def boxes_to_text(self, show_string_characters=False, output_size_limit=None, **options):
value = self.value
if (not show_string_characters and # nopep8
Expand Down Expand Up @@ -2289,7 +2331,8 @@ def boxes_to_text(self, **options):

def boxes_to_xml(self, **options):
new_options = dict((k, v) for k, v in options.items() if k != 'output_size_limit')
return super(Omitted, self).boxes_to_xml(**new_options)
s = super(Omitted, self).boxes_to_xml(**new_options)
return "<mtext mathcolor='#4040a0'>%s</mtext>" % s

def boxes_to_tex(self, **options):
new_options = dict((k, v) for k, v in options.items() if k != 'output_size_limit')
Expand Down Expand Up @@ -2342,9 +2385,25 @@ def make(self, items, form, segment=None):
raise NotImplementedError()


class Omissions:
def __init__(self):
self._omissions = []

def add(self, count):
n = len(self._omissions)
if n < 3:
self._omissions.append('<<%d>>' % count)
if n == 3:
self._omissions.append('...')

def warn(self, evaluation):
if self._omissions:
evaluation.message('General', 'omit', ', '.join(self._omissions))


class _UnlimitedMakeBoxesStrategy(_MakeBoxesStrategy):
def __init__(self):
pass
self.omissions_occured = False

def capacity(self):
return None
Expand All @@ -2365,11 +2424,12 @@ def __init__(self, capacity, side, both_sides, depth):


class _LimitedMakeBoxesStrategy(_MakeBoxesStrategy):
def __init__(self, capacity, evaluation):
def __init__(self, capacity, omissions, evaluation):
self._capacity = capacity
self._evaluation = evaluation
self._state = _LimitedMakeBoxesState(self._capacity, 1, True, 1)
self._unlimited = _UnlimitedMakeBoxesStrategy()
self._omissions = omissions

def capacity(self):
return self._capacity
Expand All @@ -2378,8 +2438,8 @@ def make(self, items, form, segment=None):
state = self._state
capacity = state.capacity

if capacity is None or len(items) < 1:
return self._unlimited(items, form, segment)
if capacity is None or len(items) <= 2:
return self._unlimited.make(items, form, segment)

left_leaves = []
right_leaves = []
Expand Down Expand Up @@ -2447,6 +2507,8 @@ def make(self, items, form, segment=None):
break

ellipsis_size = len(items) - (len(left_leaves) + len(right_leaves))
if ellipsis_size > 0 and self._omissions:
self._omissions.add(ellipsis_size)
ellipsis = [Omitted('<<%d>>' % ellipsis_size)] if ellipsis_size > 0 else []

if segment is not None:
Expand All @@ -2469,16 +2531,16 @@ def _evaluate(self, item, form, **kwargs):
# the simple solution; the problem is that it's redundant, as for {{{a}, b}, c}, we'd
# call boxes_to_xml first on {a}, then on {{a}, b}, then on {{{a}, b}, c}. a good fix
# is not simple though, so let's keep it this way for now.
cost = len(box.boxes_to_xml(evaluation=self._evaluation)) # evaluate len as XML
cost = box.output_cost()

return box, cost
finally:
self._state = old_state


def make_boxes_strategy(capacity, evaluation):
def make_boxes_strategy(capacity, omissions, evaluation):
if capacity is None:
return _UnlimitedMakeBoxesStrategy()
else:
return _LimitedMakeBoxesStrategy(capacity, evaluation)
return _LimitedMakeBoxesStrategy(capacity, omissions, evaluation)