Skip to content

Commit 97a0ea8

Browse files
authored
Merge branch 'master' into blank_patterns
2 parents 356df1f + d98cc2c commit 97a0ea8

File tree

15 files changed

+368
-214
lines changed

15 files changed

+368
-214
lines changed

SYMBOLS_MANIFEST.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ System`Complement
244244
System`Complex
245245
System`ComplexExpand
246246
System`ComplexInfinity
247+
System`ComplexPlot
248+
System`ComplexPlot3D
247249
System`Complexes
248250
System`CompositeQ
249251
System`Composition

mathics/builtin/arithmetic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
SympyObject,
4242
Test,
4343
)
44-
from mathics.core.convert.sympy import SympyExpression, from_sympy, sympy_symbol_prefix
44+
from mathics.core.convert.sympy import SympyExpression, from_sympy
4545
from mathics.core.element import BaseElement, ElementsProperties
4646
from mathics.core.evaluation import Evaluation
4747
from mathics.core.expression import Expression
@@ -62,6 +62,7 @@
6262
SymbolPlus,
6363
SymbolTimes,
6464
SymbolTrue,
65+
sympy_name,
6566
)
6667
from mathics.core.systemsymbols import (
6768
SymbolAnd,
@@ -377,7 +378,7 @@ def to_sympy(self, expr, **kwargs):
377378

378379
sympy_cases = (
379380
(expr.to_sympy(**kwargs), sympy_cond),
380-
(sympy.Symbol(sympy_symbol_prefix + "System`Undefined"), True),
381+
(sympy.Symbol(sympy_name(SymbolUndefined)), True),
381382
)
382383
return sympy.Piecewise(*sympy_cases)
383384

mathics/builtin/drawing/plot.py

Lines changed: 123 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
SymbolLog10,
3939
SymbolNone,
4040
SymbolRGBColor,
41+
SymbolSequence,
4142
SymbolStyle,
4243
)
4344
from mathics.eval.drawing.charts import draw_bar_chart, eval_chart
@@ -53,6 +54,7 @@
5354
get_plot_range,
5455
get_plot_range_option,
5556
)
57+
from mathics.eval.nevaluator import eval_N
5658

5759
# The vectorized plot function generates GraphicsComplex using NumericArray,
5860
# which no consumer will currently understand. So lets make it opt-in for now.
@@ -62,11 +64,19 @@
6264
# TODO: work out exactly how to deploy.
6365
use_vectorized_plot = os.getenv("MATHICS3_USE_VECTORIZED_PLOT", False)
6466
if use_vectorized_plot:
65-
from mathics.eval.drawing.plot3d_vectorized import eval_DensityPlot, eval_Plot3D
67+
from mathics.eval.drawing.plot3d_vectorized import (
68+
eval_ComplexPlot,
69+
eval_ComplexPlot3D,
70+
eval_DensityPlot,
71+
eval_Plot3D,
72+
)
6673
else:
67-
from mathics.eval.drawing.plot3d import eval_DensityPlot, eval_Plot3D
68-
69-
from mathics.eval.nevaluator import eval_N
74+
from mathics.eval.drawing.plot3d import (
75+
eval_ComplexPlot,
76+
eval_ComplexPlot3D,
77+
eval_DensityPlot,
78+
eval_Plot3D,
79+
)
7080

7181
# This tells documentation how to sort this module
7282
# Here we are also hiding "drawing" since this erroneously appears at the top level.
@@ -466,11 +476,15 @@ def __init__(self, expr, range_exprs, options, evaluation):
466476
self.error(expr, "invrange", range_expr)
467477
range = [range_expr.elements[0]]
468478
for limit_expr in range_expr.elements[1:3]:
469-
limit = limit_expr.round_to_float(evaluation)
470-
if limit is None:
479+
limit = eval_N(limit_expr, evaluation).to_python()
480+
if not isinstance(limit, (int, float, complex)):
471481
self.error(expr, "plln", limit_expr, range_expr)
472482
range.append(limit)
473-
if range[2] <= range[1]:
483+
if isinstance(limit, (int, float)) and range[2] <= range[1]:
484+
self.error(expr, "invrange", range_expr)
485+
if isinstance(limit, complex) and (
486+
range[2].real <= range[1].real or range[2].imag <= range[1].imag
487+
):
474488
self.error(expr, "invrange", range_expr)
475489
self.ranges.append(range)
476490

@@ -539,7 +553,9 @@ def check_plotpoints(steps):
539553

540554

541555
class _Plot3D(Builtin):
542-
"""Common base class for Plot3D and DensityPlot"""
556+
"""Common base class for Plot3D, DensityPlot, ComplexPlot, ComplexPlot3D"""
557+
558+
attributes = A_HOLD_ALL | A_PROTECTED
543559

544560
# Check for correct number of args
545561
eval_error = Builtin.generic_argument_error
@@ -565,29 +581,62 @@ class _Plot3D(Builtin):
565581
),
566582
}
567583

584+
# Plot3D, ComplexPlot3D
585+
options3d = Graphics3D.options | {
586+
"Axes": "True",
587+
"AspectRatio": "1",
588+
"Mesh": "Full",
589+
"PlotPoints": "None",
590+
"BoxRatios": "{1, 1, 0.4}",
591+
"MaxRecursion": "2",
592+
}
593+
594+
# DensityPlot, ComplexPlot
595+
options2d = Graphics.options | {
596+
"Axes": "False",
597+
"AspectRatio": "1",
598+
"Mesh": "None",
599+
"Frame": "True",
600+
"ColorFunction": "Automatic",
601+
"ColorFunctionScaling": "True",
602+
"PlotPoints": "None",
603+
"MaxRecursion": "0",
604+
# 'MaxRecursion': '2', # FIXME causes bugs in svg output see #303
605+
}
606+
568607
def eval(
569608
self,
570609
functions,
571-
xrange,
572-
yrange,
610+
ranges,
573611
evaluation: Evaluation,
574612
options: dict,
575613
):
576-
"""%(name)s[functions_, xrange_, yrange_, OptionsPattern[%(name)s]]"""
614+
"""%(name)s[functions_, ranges__, OptionsPattern[%(name)s]]"""
577615

578616
# TODO: test error for too many, too few, no args
579617

580618
# parse options, bailing out if anything is wrong
581619
try:
582-
plot_options = PlotOptions(self, [xrange, yrange], options, evaluation)
620+
ranges = ranges.elements if ranges.head is SymbolSequence else [ranges]
621+
plot_options = PlotOptions(self, ranges, options, evaluation)
583622
except ValueError:
584623
return None
585624

586-
# ask the subclass to get one or more functions as appropriate
587-
plot_options.functions = self.get_functions_param(functions)
625+
# TODO: consult many_functions variable set by subclass and error
626+
# if many_functions is False but multiple are supplied
627+
if functions.has_form("List", None):
628+
plot_options.functions = functions.elements
629+
else:
630+
plot_options.functions = [functions]
588631

589-
# delegate to subclass, which will call the appropriate eval_* function
590-
return self.do_eval(plot_options, evaluation, options)
632+
# subclass must set eval_function and graphics_class
633+
graphics = self.eval_function(plot_options, evaluation)
634+
if not graphics:
635+
return
636+
graphics_expr = graphics.generate(
637+
options_to_rules(options, self.graphics_class.options)
638+
)
639+
return graphics_expr
591640

592641

593642
class BarChart(_Chart):
@@ -712,6 +761,54 @@ class ColorDataFunction(Builtin):
712761
summary_text = "color scheme object"
713762

714763

764+
class ComplexPlot3D(_Plot3D):
765+
"""
766+
<url>:WMA link: https://reference.wolfram.com/language/ref/ComplexPlot3D.html</url>
767+
<dl>
768+
<dt>'Plot3D'[$f$, {$z$, $z_{min}$, $z_{max}$}]
769+
<dd>creates a three-dimensional plot of the magnitude of $f$ with $z$ ranging from $z_{min}$ to \
770+
$z_{max}$ with surface colored according to phase
771+
772+
See <url>:Drawing Option and Option Values:
773+
/doc/reference-of-built-in-symbols/graphics-and-drawing/drawing-options-and-option-values
774+
</url> for a list of Plot options.
775+
</dl>
776+
777+
"""
778+
779+
summary_text = "plots one or more complex functions as a surface"
780+
expected_args = 2
781+
options = _Plot3D.options3d | {"Mesh": "None"}
782+
783+
many_functions = True
784+
eval_function = staticmethod(eval_ComplexPlot3D)
785+
graphics_class = Graphics3D
786+
787+
788+
class ComplexPlot(_Plot3D):
789+
"""
790+
<url>:WMA link: https://reference.wolfram.com/language/ref/ComplexPlot.html</url>
791+
<dl>
792+
<dt>'Plot3D'[$f$, {$z$, $z_{min}$, $z_{max}$}]
793+
<dd>creates two-dimensional plot of $f$ with $z$ ranging from $z_{min}$ to \
794+
$z_{max}$ colored according to phase
795+
796+
See <url>:Drawing Option and Option Values:
797+
/doc/reference-of-built-in-symbols/graphics-and-drawing/drawing-options-and-option-values
798+
</url> for a list of Plot options.
799+
</dl>
800+
801+
"""
802+
803+
summary_text = "plots a complex function showing phase using colors"
804+
expected_args = 2
805+
options = _Plot3D.options2d
806+
807+
many_functions = False
808+
eval_function = staticmethod(eval_ComplexPlot)
809+
graphics_class = Graphics
810+
811+
715812
class DensityPlot(_Plot3D):
716813
"""
717814
<url>:WMA link: https://reference.wolfram.com/language/ref/DensityPlot.html</url>
@@ -736,35 +833,13 @@ class DensityPlot(_Plot3D):
736833
= -Graphics-
737834
"""
738835

739-
attributes = A_HOLD_ALL | A_PROTECTED
740-
741-
options = Graphics.options.copy()
742-
options.update(
743-
{
744-
"Axes": "False",
745-
"AspectRatio": "1",
746-
"Mesh": "None",
747-
"Frame": "True",
748-
"ColorFunction": "Automatic",
749-
"ColorFunctionScaling": "True",
750-
"PlotPoints": "None",
751-
"MaxRecursion": "0",
752-
# 'MaxRecursion': '2', # FIXME causes bugs in svg output see #303
753-
}
754-
)
755836
summary_text = "density plot for a function"
837+
expected_args = 3
838+
options = _Plot3D.options2d
756839

757-
# TODO: error if more than one function here
758-
def get_functions_param(self, functions):
759-
"""can only have one function"""
760-
return [functions]
761-
762-
# called by superclass
763-
def do_eval(self, plot_options, evaluation, options):
764-
"""called by superclass to call appropriate eval_* function"""
765-
graphics = eval_DensityPlot(plot_options, evaluation)
766-
graphics_expr = graphics.generate(options_to_rules(options, Graphics.options))
767-
return graphics_expr
840+
many_functions = False
841+
eval_function = staticmethod(eval_DensityPlot)
842+
graphics_class = Graphics
768843

769844

770845
class DiscretePlot(_Plot):
@@ -1878,30 +1953,10 @@ class Plot3D(_Plot3D):
18781953
= -Graphics3D-
18791954
"""
18801955

1881-
attributes = A_HOLD_ALL | A_PROTECTED
1882-
1883-
options = Graphics.options.copy()
1884-
options.update(
1885-
{
1886-
"Axes": "True",
1887-
"AspectRatio": "1",
1888-
"Mesh": "Full",
1889-
"PlotPoints": "None",
1890-
"BoxRatios": "{1, 1, 0.4}",
1891-
"MaxRecursion": "2",
1892-
}
1893-
)
18941956
summary_text = "plots 3D surfaces of one or more functions"
1957+
expected_args = 3
1958+
options = _Plot3D.options3d
18951959

1896-
def get_functions_param(self, functions):
1897-
"""May have a function or a list of functions"""
1898-
if functions.has_form("List", None):
1899-
return functions.elements
1900-
else:
1901-
return [functions]
1902-
1903-
def do_eval(self, plot_options, evaluation, options):
1904-
"""called by superclass to call appropriate eval_* function"""
1905-
graphics = eval_Plot3D(plot_options, evaluation)
1906-
graphics_expr = graphics.generate(options_to_rules(options, Graphics3D.options))
1907-
return graphics_expr
1960+
many_functions = True
1961+
eval_function = staticmethod(eval_Plot3D)
1962+
graphics_class = Graphics3D

mathics/builtin/functional/application.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from mathics.core.convert.sympy import SymbolFunction
1515
from mathics.core.evaluation import Evaluation
1616
from mathics.core.expression import Expression
17-
from mathics.core.symbols import Symbol, sympy_slot_prefix
17+
from mathics.core.symbols import SYMPY_SLOT_PREFIX, Symbol
1818
from mathics.core.systemsymbols import SymbolSlot
1919

2020
# This tells documentation how to sort this module
@@ -206,7 +206,7 @@ class Slot(SympyFunction, PrefixOperator):
206206

207207
def to_sympy(self, expr: Expression, **kwargs):
208208
index: Integer = expr.elements[0]
209-
return sympy.Symbol(f"{sympy_slot_prefix}{index.get_int_value()}")
209+
return sympy.Symbol(f"{SYMPY_SLOT_PREFIX}{index.get_int_value()}")
210210

211211

212212
class SlotSequence(PrefixOperator, Builtin):

mathics/builtin/numbers/algebra.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from mathics.core.attributes import A_LISTABLE, A_PROTECTED
3232
from mathics.core.builtin import Builtin
3333
from mathics.core.convert.python import from_bool
34-
from mathics.core.convert.sympy import SympyExpression, from_sympy, sympy_symbol_prefix
34+
from mathics.core.convert.sympy import SympyExpression, from_sympy
3535
from mathics.core.element import BaseElement
3636
from mathics.core.evaluation import Evaluation
3737
from mathics.core.expression import Expression
@@ -42,6 +42,7 @@
4242
from mathics.core.list import ListExpression
4343
from mathics.core.rules import BasePattern
4444
from mathics.core.symbols import (
45+
SYMPY_SYMBOL_PREFIX,
4546
Atom,
4647
Symbol,
4748
SymbolFalse,
@@ -183,7 +184,7 @@ def _expand(expr):
183184

184185
def store_sub_expr(expr):
185186
sub_exprs.append(expr)
186-
result = sympy.Symbol(sympy_symbol_prefix + str(len(sub_exprs) - 1))
187+
result = sympy.Symbol(SYMPY_SYMBOL_PREFIX + str(len(sub_exprs) - 1))
187188
return result
188189

189190
def get_sub_expr(expr):

mathics/builtin/numbers/calculus.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,14 @@
4141
from mathics.core.convert.expression import to_expression, to_mathics_list
4242
from mathics.core.convert.function import expression_to_callable_and_args
4343
from mathics.core.convert.python import from_python
44-
from mathics.core.convert.sympy import (
45-
SymbolRootSum,
46-
SympyExpression,
47-
from_sympy,
48-
sympy_symbol_prefix,
49-
)
44+
from mathics.core.convert.sympy import SymbolRootSum, SympyExpression, from_sympy
5045
from mathics.core.evaluation import Evaluation
5146
from mathics.core.expression import Expression
5247
from mathics.core.list import ListExpression
5348
from mathics.core.number import MACHINE_EPSILON, dps
5449
from mathics.core.rules import BasePattern
5550
from mathics.core.symbols import (
51+
SYMPY_SYMBOL_PREFIX,
5652
BaseElement,
5753
Symbol,
5854
SymbolFalse,
@@ -532,7 +528,7 @@ def to_sympy(self, expr, **kwargs):
532528
return
533529

534530
func = exprs[1].elements[0]
535-
sym_func = sympy.Function(str(sympy_symbol_prefix + func.__str__()))(*sym_args)
531+
sym_func = sympy.Function(str(SYMPY_SYMBOL_PREFIX + func.__str__()))(*sym_args)
536532

537533
counts = [element.get_int_value() for element in exprs[2].elements]
538534
if None in counts:

0 commit comments

Comments
 (0)