Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix support for minimal installation #11

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
34 changes: 34 additions & 0 deletions .github/workflows/ubuntu-minimal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Mathics (ubuntu-minimal)

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
env:
NO_CYTHON: 1
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [3.9]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev
python -m pip install --upgrade pip
# Can remove after next Mathics-Scanner release
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
- name: Install Mathics with full dependencies
run: |
make develop
- name: Test Mathics
run: |
make -j3 check
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Enhancements
* In assignment to messages associated with symbols, the attribute ``Protected`` is not having into account, following the standard in WMA. With this and the above change, Combinatorical 2.0 works as written.
* ``Share[]`` performs an explicit call to the Python garbage collection and returns the amount of memory free.
* Improving the compatibility of ``TeXForm`` and ``MathMLForm`` outputs with WMA. MatML tags around numbers appear as "<mn>" tags instead of "<mtext>", except in the case of ``InputForm`` expressions. In TeXForm some quotes around strings have been removed to conform to WMA. It is not clear whether this is the correct behavior.
* Revise ``Nintegrate[]`` to allow scipy to be optional.



Expand Down Expand Up @@ -97,6 +98,7 @@ Bugs
* Partial fix of ``FillSimplify``
* Streams used in MathicsOpen are now freed and their file descriptors now released. Issue #326.
* Some temporary files that were created are now removed from the filesystem. Issue #309.
* There were a number of small changes/fixes involving ``NIntegrate`` and its Method options. ``Nintegrate`` tests have been expanded.

4.0.1
-----
Expand Down
41 changes: 17 additions & 24 deletions mathics/algorithm/optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,10 @@
from_python,
)

from mathics.core.symbols import (
BaseElement,
SymbolPlus,
SymbolTimes,
SymbolTrue,
)
from mathics.core.symbols import SymbolTrue, BaseElement

from mathics.core.systemsymbols import (
SymbolAutomatic,
SymbolD,
SymbolInfinity,
SymbolLess,
SymbolLessEqual,
Expand Down Expand Up @@ -68,7 +62,7 @@ def find_minimum_newton1d(f, x0, x, opts, evaluation) -> (Number, bool):
else:
return x0, False
d1 = dynamic_scoping(
lambda ev: Expression(SymbolD, f, x).evaluate(ev), {x_name: None}, evaluation
lambda ev: Expression("D", f, x).evaluate(ev), {x_name: None}, evaluation
)
val_d1 = apply_N(d1.replace_vars({x_name: x0}), evaluation)
if not isinstance(val_d1, Number):
Expand All @@ -82,9 +76,7 @@ def find_minimum_newton1d(f, x0, x, opts, evaluation) -> (Number, bool):
)
else:
d2 = dynamic_scoping(
lambda ev: Expression(SymbolD, d1, x).evaluate(ev),
{x_name: None},
evaluation,
lambda ev: Expression("D", d1, x).evaluate(ev), {x_name: None}, evaluation
)
val_d2 = apply_N(d2.replace_vars({x_name: x0}), evaluation)
if not isinstance(val_d2, Number):
Expand Down Expand Up @@ -215,14 +207,12 @@ def find_root_secant(f, x0, x, opts, evaluation) -> (Number, bool):
while count < maxit:
if f0 == f1:
x1 = Expression(
SymbolPlus,
"Plus",
x0,
Expression(
SymbolTimes,
"Times",
Real(0.75),
Expression(
SymbolPlus, x1, Expression(SymbolTimes, Integer(-1), x0)
),
Expression("Plus", x1, Expression("Times", Integer(-1), x0)),
),
)
x1 = x1.evaluate(evaluation)
Expand All @@ -236,11 +226,11 @@ def find_root_secant(f, x0, x, opts, evaluation) -> (Number, bool):

inv_deltaf = from_python(1.0 / (f1 - f0))
num = Expression(
SymbolPlus,
Expression(SymbolTimes, x0, f1),
Expression(SymbolTimes, x1, f0, Integer(-1)),
"Plus",
Expression("Times", x0, f1),
Expression("Times", x1, f0, Integer(-1)),
)
x2 = Expression(SymbolTimes, num, inv_deltaf)
x2 = Expression("Times", num, inv_deltaf)
x2 = x2.evaluate(evaluation)
f2 = dynamic_scoping(
lambda ev: f.evaluate(evaluation), {x_name: x2}, evaluation
Expand Down Expand Up @@ -332,9 +322,9 @@ def sub(evaluation):
if minus is None:
evaluation.message("FindRoot", "dsing", x, x0)
return x0, False
x1 = Expression(
SymbolPlus, x0, Expression(SymbolTimes, Integer(-1), minus)
).evaluate(evaluation)
x1 = Expression("Plus", x0, Expression("Times", Integer(-1), minus)).evaluate(
evaluation
)
if not isinstance(x1, Number):
evaluation.message("FindRoot", "nnum", x, x0)
return x0, False
Expand All @@ -360,16 +350,19 @@ def sub(evaluation):
return x0, True


native_optimizer_messages = {}

native_local_optimizer_methods = {
"Automatic": find_minimum_newton1d,
"newton": find_minimum_newton1d,
"Newton": find_minimum_newton1d,
}

native_findroot_methods = {
"Automatic": find_root_newton,
"Newton": find_root_newton,
"Secant": find_root_secant,
}
native_findroot_messages = {}


def is_zero(
Expand Down
64 changes: 35 additions & 29 deletions mathics/builtin/numbers/calculus.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,7 +1287,6 @@ class _BaseFinder(Builtin):
"""

attributes = hold_all | protected
requires = ["scipy"]
methods = {}
messages = {
"snum": "Value `1` is not a number.",
Expand Down Expand Up @@ -1483,9 +1482,13 @@ class FindRoot(_BaseFinder):
)

try:
from mathics.algorithm.optimizers import native_findroot_methods
from mathics.algorithm.optimizers import (
native_findroot_methods,
native_findroot_messages,
)

methods.update(native_findroot_methods)
messages.update(native_findroot_messages)
except Exception:
pass
try:
Expand Down Expand Up @@ -1519,8 +1522,8 @@ class FindMinimum(_BaseFinder):
= {2., {x -> 3.}}
>> FindMinimum[Sin[x], {x, 1}]
= {-1., {x -> -1.5708}}
>> phi[x_?NumberQ]:=NIntegrate[u,{u,0,x}];
>> FindMinimum[phi[x]-x,{x,1.2}]
>> phi[x_?NumberQ]:=NIntegrate[u,{u,0,x}, Method->"Internal"];
>> Quiet[FindMinimum[phi[x]-x,{x, 1.2}, Method->"Newton"]]
= {-0.5, {x -> 1.00001}}
>> Clear[phi];
For a not so well behaving function, the result can be less accurate:
Expand All @@ -1532,9 +1535,13 @@ class FindMinimum(_BaseFinder):
methods = {}
summary_text = "local minimum optimization"
try:
from mathics.algorithm.optimizers import native_local_optimizer_methods
from mathics.algorithm.optimizers import (
native_local_optimizer_methods,
native_optimizer_messages,
)

methods.update(native_local_optimizer_methods)
messages.update(native_optimizer_messages)
except Exception:
pass
try:
Expand Down Expand Up @@ -1562,8 +1569,8 @@ class FindMaximum(_BaseFinder):
= {2., {x -> 3.}}
>> FindMaximum[Sin[x], {x, 1}]
= {1., {x -> 1.5708}}
>> phi[x_?NumberQ]:=NIntegrate[u,{u,0,x}];
>> FindMaximum[-phi[x]+x,{x,1.2}]
>> phi[x_?NumberQ]:=NIntegrate[u, {u, 0., x}, Method->"Internal"];
>> Quiet[FindMaximum[-phi[x] + x, {x, 1.2}, Method->"Newton"]]
= {0.5, {x -> 1.00001}}
>> Clear[phi];
For a not so well behaving function, the result can be less accurate:
Expand Down Expand Up @@ -2017,37 +2024,35 @@ class NIntegrate(Builtin):
<dt>'NIntegrate[$expr$, $interval$]'
<dd>returns a numeric approximation to the definite integral of $expr$ with limits $interval$ and with a precision of $prec$ digits.

<dt>'NIntegrate[$expr$, $interval1$, $interval2$, ...]'
<dd>returns a numeric approximation to the multiple integral of $expr$ with limits $interval1$, $interval2$ and with a precision of $prec$ digits.
<dt>'NIntegrate[$expr$, $interval1$, $interval2$, ...]'
<dd>returns a numeric approximation to the multiple integral of $expr$ with limits $interval1$, $interval2$ and with a precision of $prec$ digits.
</dl>

>> NIntegrate[Exp[-x],{x,0,Infinity},Tolerance->1*^-6]
>> NIntegrate[Exp[-x],{x,0,Infinity},Tolerance->1*^-6, Method->"Internal"]
= 1.
>> NIntegrate[Exp[x],{x,-Infinity, 0},Tolerance->1*^-6]
>> NIntegrate[Exp[x],{x,-Infinity, 0},Tolerance->1*^-6, Method->"Internal"]
= 1.
>> NIntegrate[Exp[-x^2/2.],{x,-Infinity, Infinity},Tolerance->1*^-6]
= 2.50663
>> NIntegrate[Exp[-x^2/2.],{x,-Infinity, Infinity},Tolerance->1*^-6, Method->"Internal"]
= 2.50664

>> Table[1./NIntegrate[x^k,{x,0,1},Tolerance->1*^-6], {k,0,6}]
: The specified method failed to return a number. Falling back into the internal evaluator.
= {1., 2., 3., 4., 5., 6., 7.}
"""

>> NIntegrate[1 / z, {z, -1 - I, 1 - I, 1 + I, -1 + I, -1 - I}, Tolerance->1.*^-4]
: Integration over a complex domain is not implemented yet
= NIntegrate[1 / z, {z, -1 - I, 1 - I, 1 + I, -1 + I, -1 - I}, Tolerance -> 0.0001]
## = 6.2832 I
# ## The Following tests fails if sympy is not installed.
# >> Table[1./NIntegrate[x^k,{x,0,1},Tolerance->1*^-6], {k,0,6}]
# : The specified method failed to return a number. Falling back into the internal evaluator.
# = {1., 2., 3., 4., 5., 6., 7.}

Integrate singularities with weak divergences:
>> Table[ NIntegrate[x^(1./k-1.), {x,0,1.}, Tolerance->1*^-6], {k,1,7.} ]
= {1., 2., 3., 4., 5., 6., 7.}
# >> NIntegrate[1 / z, {z, -1 - I, 1 - I, 1 + I, -1 + I, -1 - I}, Tolerance->1.*^-4]
# ## = 6.2832 I

Mutiple Integrals :
>> NIntegrate[x * y,{x, 0, 1}, {y, 0, 1}]
= 0.25
# Integrate singularities with weak divergences:
# >> Table[ NIntegrate[x^(1./k-1.), {x,0,1.}, Tolerance->1*^-6], {k,1,7.}]
# = {1., 2., 3., 4., 5., 6., 7.}

"""
# Mutiple Integrals :
# >> NIntegrate[x * y,{x, 0, 1}, {y, 0, 1}]
# = 0.25

requires = ["scipy"]
summary_text = "numerical integration in one or several variables"
messages = {
"bdmtd": "The Method option should be a built-in method name.",
Expand Down Expand Up @@ -2122,6 +2127,8 @@ def apply_with_func_domain(self, func, domain, evaluation, options):
method = method.value
elif isinstance(method, Symbol):
method = method.get_name()
# strip context
method = method[method.rindex("`") + 1 :]
else:
evaluation.message("NIntegrate", "bdmtd", method)
return
Expand Down Expand Up @@ -2155,7 +2162,6 @@ def apply_with_func_domain(self, func, domain, evaluation, options):

intvars = ListExpression(*coords)
integrand = Expression(SymbolCompile, intvars, func).evaluate(evaluation)

if len(integrand.elements) >= 3:
integrand = integrand.elements[2].cfunc
else:
Expand Down
31 changes: 18 additions & 13 deletions test/test_nintegrate.py → test/builtin/numbers/test_nintegrate.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NIntegrate[] tests


This also
"""
import importlib
import pytest
from .helper import evaluate


try:
import scipy.integrate

usescipy = True
except:
usescipy = False
from test.helper import evaluate

# From How to check if a Python module exists without importing it:
# https://stackoverflow.com/a/14050282/546218
scipy_integrate = importlib.find_loader("scipy.integrate")

if usescipy:
if scipy_integrate is not None:
methods = ["Automatic", "Romberg", "Internal", "NQuadrature"]

generic_tests_for_nintegrate = [
(r"NIntegrate[x^2, {x,0,1}, {method} ]", r"1/3.", ""),
(r"NIntegrate[x^2 y^(-1.+1/3.), {x,0,1},{y,0,1}, {method}]", r"1.", ""),
(r"NIntegrate[x^2 y^2, {y,0,1}, {x,0,1}, {method} ]", r"1/9.", ""),
# FIXME: improve singularity handling in NIntegrate
# (
# r"NIntegrate[x^2 y^(-1.+1/3.), {x,1.*^-9,1},{y, 1.*^-9,1}, {method}]",
# r"1.",
# "",
# ),
]

tests_for_nintegrate = sum(
Expand All @@ -35,6 +39,7 @@
else:
tests_for_nintegrate = [
(r"NIntegrate[x^2, {x,0,1}]", r"1/3.", ""),
(r"NIntegrate[x^2 y^2, {y,0,1}, {x,0,1}]", r"1/9.", ""),
# FIXME: this can integrate to Infinity
# (r"NIntegrate[x^2 y^(-.5), {x,0,1},{y,0,1}]", r"1.", ""),
]
Expand Down