Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2bfcc4881 | ||
|
|
c213d1f0fe | ||
|
|
2ab32186c1 |
@@ -1,10 +0,0 @@
|
||||
This folder currently holds ``durations.json`` and ``blacklisted.json`` which are used in our pytest configuration.
|
||||
``durations.json`` allows the two options ``--quickcheck`` and ``--veryquickcheck`` to be passed to ``pytest``.
|
||||
Currently, the list of slow tests is updated intermittently by running::
|
||||
|
||||
$ ./.ci/generate_durations_log.sh
|
||||
$ ./.ci/parse_durations_log.py
|
||||
$ git commit -am "pytest: Updated .ci/durations.json"
|
||||
|
||||
make sure you have a C compiler, a Fortran compiler, numpy, scipy, cython, matplotlib & sage installed (the latter can
|
||||
be tricky, in which case just make sure that no sage tests are removed in the git diff).
|
||||
+195
-850
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash -e
|
||||
#!/bin/bash
|
||||
ABS_REPO_PATH=$(unset CDPATH && cd "$(dirname "$0")/.." && echo $PWD)
|
||||
cat <<EOF >${ABS_REPO_PATH}/.ci/blacklisted.json
|
||||
{
|
||||
@@ -10,4 +10,4 @@ cat <<EOF >${ABS_REPO_PATH}/.ci/blacklisted.json
|
||||
]
|
||||
}
|
||||
EOF
|
||||
${PYTHON:-python} -m pytest -ra --durations 0 --verbose | tee $ABS_REPO_PATH/.ci/durations.log
|
||||
python3 -m pytest -ra --durations 0 --verbose | tee $ABS_REPO_PATH/.ci/durations.log
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
custom: ['https://numfocus.salsalabs.org/donate-to-sympy/index.html']
|
||||
@@ -7,12 +7,9 @@
|
||||
# track a file that is ignored for some reason, you have to use
|
||||
# "git add -f". See "git help gitignore" for more information.
|
||||
|
||||
# Virtualenv
|
||||
/.venv/
|
||||
|
||||
# Regular Python bytecode file
|
||||
*.pyc
|
||||
__pycache__/
|
||||
|
||||
# Optimized Python bytecode file
|
||||
*.pyo
|
||||
@@ -20,12 +17,6 @@ __pycache__/
|
||||
# Vim's swap files
|
||||
*.sw[op]
|
||||
|
||||
# VSCode settings file
|
||||
.vscode/
|
||||
|
||||
# PyCharm settings file
|
||||
/.idea/
|
||||
|
||||
# Generated files from Jython
|
||||
*$py.class
|
||||
|
||||
|
||||
+10
-30
@@ -8,7 +8,7 @@ env:
|
||||
matrix:
|
||||
- SPLIT="1/2" TEST_SYMPY="true"
|
||||
- SPLIT="2/2" TEST_SYMPY="true"
|
||||
- TEST_DOCTESTS="true" FASTCACHE="false" TEST_SETUP="true" TEST_EXAMPLES="true"
|
||||
- TEST_DOCTESTS="true" FASTCACHE="false" TEST_SETUP="true"
|
||||
global:
|
||||
- secure: "YIEZal9EBTL+fg2YmoZoS8Bvt3eAVUOZjb38CtqpzR2CCSXWoUk35KG23m2NknlY1iKfYJyt7XWBszT/VKOQEbWQq7PIakV4vIByrWacgBxy1x3WC+rZoW7TX+JJiL+y942qIYbMoNMMB8xFpE5RDLSjSecMpFhJJXoafVTvju8="
|
||||
dist: trusty
|
||||
@@ -31,7 +31,7 @@ matrix:
|
||||
dist: xenial
|
||||
sudo: true
|
||||
env:
|
||||
- TEST_DOCTESTS="true" FASTCACHE="false" TEST_SETUP="true" TEST_EXAMPLES="true"
|
||||
- TEST_DOCTESTS="true" FASTCACHE="false" TEST_SETUP="true"
|
||||
- python: 3.7
|
||||
dist: xenial
|
||||
sudo: true
|
||||
@@ -44,26 +44,11 @@ matrix:
|
||||
- SPLIT="2/2" TEST_SYMPY="true"
|
||||
|
||||
- stage: test
|
||||
python: 3.8-dev
|
||||
dist: xenial
|
||||
sudo: true
|
||||
env:
|
||||
- TEST_DOCTESTS="true" FASTCACHE="false" TEST_SETUP="true" TEST_EXAMPLES="true"
|
||||
- python: 3.8-dev
|
||||
dist: xenial
|
||||
sudo: true
|
||||
env:
|
||||
- SPLIT="1/2" TEST_SYMPY="true"
|
||||
- python: 3.8-dev
|
||||
dist: xenial
|
||||
sudo: true
|
||||
env:
|
||||
- SPLIT="2/2" TEST_SYMPY="true"
|
||||
- python: 2.7
|
||||
python: 2.7
|
||||
env:
|
||||
- TEST_ASCII="true"
|
||||
# space separated list of optional dependencies(conda packages) to install and test
|
||||
- TEST_OPT_DEPENDENCY="numpy scipy gmpy2 matplotlib>=2.2 theano llvmlite autowrap cython wurlitzer python-symengine=0.3.* tensorflow numexpr ipython antlr-python-runtime>=4.7,<4.8 antlr>=4.7,<4.8 cloudpickle python=2.7 pyglet pycosat python-clang"
|
||||
- TEST_OPT_DEPENDENCY="numpy scipy gmpy2 matplotlib>=2.2 theano llvmlite autowrap cython wurlitzer python-symengine=0.3.* tensorflow numexpr ipython antlr-python-runtime>=4.7,<4.8 antlr>=4.7,<4.8 cloudpickle python=2.7 pyglet"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -78,7 +63,7 @@ matrix:
|
||||
# and we aren't actually using the Travis Python anyway.
|
||||
env:
|
||||
- TEST_ASCII="true"
|
||||
- TEST_OPT_DEPENDENCY="numpy scipy gmpy2 matplotlib theano llvmlite autowrap cython wurlitzer python-symengine=0.3.* tensorflow numexpr ipython antlr-python-runtime>=4.7,<4.8 antlr>=4.7,<4.8 cloudpickle pyglet pycosat lfortran python-clang"
|
||||
- TEST_OPT_DEPENDENCY="numpy scipy gmpy2 matplotlib theano llvmlite autowrap cython wurlitzer python-symengine=0.3.* tensorflow numexpr ipython antlr-python-runtime>=4.7,<4.8 antlr>=4.7,<4.8 cloudpickle pyglet"
|
||||
- TEST_SAGE="true"
|
||||
addons:
|
||||
apt:
|
||||
@@ -166,7 +151,7 @@ matrix:
|
||||
# duplicated here and in that section below.
|
||||
- python: "pypy"
|
||||
env:
|
||||
- TEST_DOCTESTS="true" TEST_EXAMPLES="true"
|
||||
- TEST_DOCTESTS="true"
|
||||
- FASTCACHE="false"
|
||||
addons:
|
||||
apt:
|
||||
@@ -244,9 +229,6 @@ before_install:
|
||||
pip install coverage;
|
||||
coverage debug sys;
|
||||
fi
|
||||
- if [ "$TRAVIS_PYTHON_VERSION" == "3.8-dev" ]; then
|
||||
pip uninstall -y Cython;
|
||||
fi
|
||||
- |
|
||||
if [[ -n "${TEST_OPT_DEPENDENCY}" ]]; then
|
||||
# We do this conditionally because it saves us some downloading if the
|
||||
@@ -278,13 +260,14 @@ before_install:
|
||||
conda clean --all;
|
||||
fi
|
||||
if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then
|
||||
conda install matchpy;
|
||||
# Change this back to conda once matchpy 0.5.0 gets updated on conda-forge
|
||||
pip install matchpy;
|
||||
fi
|
||||
elif [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then
|
||||
pip list | grep "numpy" && pip uninstall -y numpy;
|
||||
fi
|
||||
- if [[ "${TEST_SPHINX}" == "true" ]]; then
|
||||
pip install "sphinx" "docutils" doctr matplotlib sphinx-math-dollar;
|
||||
pip install "sphinx" "docutils" doctr matplotlib;
|
||||
fi
|
||||
|
||||
install:
|
||||
@@ -299,9 +282,6 @@ install:
|
||||
- pip install mpmath
|
||||
# -We:invalid makes invalid escape sequences error in Python 3.6. See
|
||||
# -#12028.
|
||||
# SyntaxWarning flag for catching errors in Python3.8
|
||||
# Issue - #16973. -We:invalid can be dropped from 3.8 onwards, but
|
||||
# it needs to be there for earlier versions.
|
||||
- |
|
||||
if [[ "${TEST_SETUP}" == "true" ]]; then
|
||||
# The install cycle below is to test installation on systems without
|
||||
@@ -311,7 +291,7 @@ install:
|
||||
~/.venv-no-setuptools/bin/pip uninstall -y setuptools;
|
||||
~/.venv-no-setuptools/bin/python -We:invalid setup.py -q install;
|
||||
fi
|
||||
python -We:invalid -We::SyntaxWarning -m compileall -f -q sympy/;
|
||||
python -We:invalid -m compileall -f -q sympy/;
|
||||
python -We:invalid setup.py -q install;
|
||||
pip list --format=columns;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ hard_dependencies = ['mpmath']
|
||||
# These libraries are optional, but are always imported at SymPy import time
|
||||
# when they are installed. External libraries should only be added to this
|
||||
# list if they are required for core SymPy functionality.
|
||||
hard_optional_dependencies = ['gmpy', 'gmpy2', 'fastcache', 'pycosat']
|
||||
hard_optional_dependencies = ['gmpy', 'gmpy2', 'fastcache']
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
+6
-19
@@ -137,8 +137,9 @@ test_list = [
|
||||
# ipython
|
||||
'*ipython*',
|
||||
|
||||
# antlr, lfortran, clang
|
||||
'sympy/parsing/',
|
||||
# antlr
|
||||
'sympy/parsing/tests/test_autolev',
|
||||
'sympy/parsing/tests/test_latex',
|
||||
|
||||
# matchpy
|
||||
'*rubi*',
|
||||
@@ -150,11 +151,6 @@ test_list = [
|
||||
|
||||
# cloudpickle
|
||||
'pickling',
|
||||
|
||||
# pycosat
|
||||
'sympy/logic',
|
||||
'sympy/assumptions',
|
||||
|
||||
]
|
||||
|
||||
blacklist = [
|
||||
@@ -184,19 +180,15 @@ doctest_list = [
|
||||
# ipython
|
||||
'*ipython*',
|
||||
|
||||
# antlr, lfortran, clang
|
||||
'sympy/parsing/',
|
||||
# antlr
|
||||
'sympy/parsing/autolev',
|
||||
'sympy/parsing/latex',
|
||||
|
||||
# matchpy
|
||||
'*rubi*',
|
||||
|
||||
# codegen
|
||||
'sympy/codegen/',
|
||||
|
||||
# pycosat
|
||||
'sympy/logic',
|
||||
'sympy/assumptions',
|
||||
|
||||
]
|
||||
|
||||
if not (sympy.test(*test_list, blacklist=blacklist) and sympy.doctest(*doctest_list)):
|
||||
@@ -250,8 +242,3 @@ fi
|
||||
if [[ "${TEST_COVERAGE}" == "true" ]]; then
|
||||
unset COVERAGE_PROCESS_START
|
||||
fi
|
||||
|
||||
if [[ "${TEST_EXAMPLES}" == "true" ]]; then
|
||||
# No need to change directory if executed after the rst doctest
|
||||
examples/all.py -q
|
||||
fi
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# heuristics to know where a multi-line expression is
|
||||
# broken. As a result you can't use more than one space
|
||||
# in a sequence. However you can and must indent your
|
||||
# expression using standard Python rules.
|
||||
# expression using standard Pyhon rules.
|
||||
#
|
||||
# You can retrieve the last output using '_' built-in
|
||||
# symbol. If the previous command did not generate
|
||||
|
||||
@@ -7,7 +7,6 @@ Debian/Ubuntu
|
||||
To make the html documentation, install the prerequisites::
|
||||
|
||||
apt-get install python-sphinx texlive-latex-recommended dvipng librsvg2-bin imagemagick docbook2x graphviz
|
||||
python -m pip install sphinx-math-dollar
|
||||
|
||||
and do::
|
||||
|
||||
@@ -31,7 +30,6 @@ Fedora
|
||||
Fedora (and maybe other RPM based distributions), install the prerequisites::
|
||||
|
||||
dnf install python3-sphinx librsvg2 ImageMagick docbook2X texlive-dvipng-bin texlive-scheme-medium librsvg2-tools
|
||||
python -m pip install sphinx-math-dollar
|
||||
|
||||
After that, run::
|
||||
|
||||
@@ -49,30 +47,6 @@ And view it at::
|
||||
|
||||
_build/html/index.html
|
||||
|
||||
Mac
|
||||
---
|
||||
|
||||
- Install MacTex: http://www.tug.org/mactex/
|
||||
- Install homebrew: https://brew.sh/
|
||||
|
||||
Install these packages with homebrew:
|
||||
|
||||
```bash
|
||||
brew install imagemagick graphviz docbook librsvg
|
||||
```
|
||||
|
||||
Install these packages with either pip or conda:
|
||||
|
||||
```bash
|
||||
python -m pip install mpmath matplotlib sphinx sphinx-math-dollar
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
conda install -c conda-forge mpmpath matplotlib sphinx sphinx-math-dollar
|
||||
```
|
||||
|
||||
Windows 10
|
||||
----------
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("classic.css");
|
||||
@import url("basic.css");
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans);
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
+2
-83
@@ -12,9 +12,6 @@
|
||||
# serve to show the default value.
|
||||
|
||||
import sys
|
||||
import inspect
|
||||
import os
|
||||
import subprocess
|
||||
import sympy
|
||||
|
||||
# If your extensions are in another directory, add it here.
|
||||
@@ -25,7 +22,7 @@ sys.path = ['ext'] + sys.path
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.linkcode', 'sphinx_math_dollar',
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode',
|
||||
'sphinx.ext.mathjax', 'numpydoc', 'sympylive',
|
||||
'sphinx.ext.graphviz', 'matplotlib.sphinxext.plot_directive']
|
||||
|
||||
@@ -207,83 +204,5 @@ texinfo_documents = [
|
||||
]
|
||||
|
||||
# Use svg for graphviz
|
||||
|
||||
graphviz_output_format = 'svg'
|
||||
|
||||
|
||||
# Requried for linkcode extension.
|
||||
# Get commit hash from the external file.
|
||||
commit_hash_filepath = '../commit_hash.txt'
|
||||
commit_hash = None
|
||||
if os.path.isfile(commit_hash_filepath):
|
||||
with open(commit_hash_filepath, 'r') as f:
|
||||
commit_hash = f.readline()
|
||||
|
||||
# Get commit hash from the external file.
|
||||
if not commit_hash:
|
||||
try:
|
||||
commit_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
|
||||
commit_hash = commit_hash.decode('ascii')
|
||||
commit_hash = commit_hash.rstrip()
|
||||
except:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"Failed to get the git commit hash as the command " \
|
||||
"'git rev-parse HEAD' is not working. The commit hash will be " \
|
||||
"assumed as the SymPy master, but the lines may be misleading " \
|
||||
"or nonexistent as it is not the correct branch the doc is " \
|
||||
"built with. Check your installation of 'git' if you want to " \
|
||||
"resolve this warning.")
|
||||
commit_hash = 'master'
|
||||
|
||||
fork = 'sympy'
|
||||
blobpath = \
|
||||
"https://github.com/{}/sympy/blob/{}/sympy/".format(fork, commit_hash)
|
||||
|
||||
|
||||
def linkcode_resolve(domain, info):
|
||||
"""Determine the URL corresponding to Python object."""
|
||||
if domain != 'py':
|
||||
return
|
||||
|
||||
modname = info['module']
|
||||
fullname = info['fullname']
|
||||
|
||||
submod = sys.modules.get(modname)
|
||||
if submod is None:
|
||||
return
|
||||
|
||||
obj = submod
|
||||
for part in fullname.split('.'):
|
||||
try:
|
||||
obj = getattr(obj, part)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
# strip decorators, which would resolve to the source of the decorator
|
||||
# possibly an upstream bug in getsourcefile, bpo-1764286
|
||||
try:
|
||||
unwrap = inspect.unwrap
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
obj = unwrap(obj)
|
||||
|
||||
try:
|
||||
fn = inspect.getsourcefile(obj)
|
||||
except Exception:
|
||||
fn = None
|
||||
if not fn:
|
||||
return
|
||||
|
||||
try:
|
||||
source, lineno = inspect.getsourcelines(obj)
|
||||
except Exception:
|
||||
lineno = None
|
||||
|
||||
if lineno:
|
||||
linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1)
|
||||
else:
|
||||
linespec = ""
|
||||
|
||||
fn = os.path.relpath(fn, start=os.path.dirname(sympy.__file__))
|
||||
return blobpath + fn + linespec
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ For easier use, there is a syntactic sugar for expressions.
|
||||
For example, ``cos(x) + 1`` is equal to ``cos(x).__add__(1)`` is equal to
|
||||
``Add(cos(x), Integer(1))``
|
||||
|
||||
This works because Python allows you to override the behavior of built-in
|
||||
This works becauses Python allows you to override the behavior of built-in
|
||||
operations ``+, *, -, /`` etc., by defining methods with names ``__add__``,
|
||||
``__mul__`` etc., so ``cos(x) + 1`` is evaluated as ``cos(x).__add__(1)``.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
========
|
||||
Algebras
|
||||
========
|
||||
===============
|
||||
Algebras Module
|
||||
===============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
======
|
||||
Common
|
||||
======
|
||||
|
||||
.. automodule:: sympy.assumptions.handlers.common
|
||||
:members:
|
||||
@@ -11,8 +11,6 @@ Contents
|
||||
:maxdepth: 3
|
||||
|
||||
calculus.rst
|
||||
common.rst
|
||||
matrices.rst
|
||||
ntheory.rst
|
||||
order.rst
|
||||
sets.rst
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
========
|
||||
Matrices
|
||||
========
|
||||
|
||||
.. automodule:: sympy.assumptions.handlers.matrices
|
||||
:members:
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _assumptions_module:
|
||||
|
||||
===========
|
||||
Assumptions
|
||||
===========
|
||||
==================
|
||||
Assumptions Module
|
||||
==================
|
||||
|
||||
.. automodule:: sympy.assumptions
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ Calculus
|
||||
.. automodule:: sympy.calculus.singularities
|
||||
:members:
|
||||
|
||||
.. _finite_diff:
|
||||
|
||||
.. automodule:: sympy.calculus.finite_diff
|
||||
:members:
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
===============
|
||||
Category Theory
|
||||
===============
|
||||
Category Theory Module
|
||||
======================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _codegen_prose:
|
||||
|
||||
===============
|
||||
Code Generation
|
||||
===============
|
||||
================================================
|
||||
Structural Details of Code Generation with SymPy
|
||||
================================================
|
||||
|
||||
Several submodules in SymPy allow one to generate directly compilable and
|
||||
executable code in a variety of different programming languages from SymPy
|
||||
@@ -164,8 +164,8 @@ An example of Mathematica code printer::
|
||||
|
||||
>>> expr = summation(expr, (n, -1, 1))
|
||||
>>> mathematica_code(expr)
|
||||
T*(x[-T]*Sin[(T + t)/T]/(T + t) + x[T]*Sin[(-T + t)/T]/(-T + t) + x[0]*Sin[t/T
|
||||
]/t)
|
||||
T*x[-T]*Sin[(T + t)/T]/(T + t) + T*x[T]*Sin[(-T + t)/T]/(-T + t) + T*x[0]*Sin[
|
||||
t/T]/t
|
||||
|
||||
We can go through a common expression in different languages we support and see
|
||||
how it works::
|
||||
@@ -548,9 +548,6 @@ Classes and functions for rewriting expressions (sympy.codegen.rewriting)
|
||||
.. automodule:: sympy.codegen.rewriting
|
||||
:members:
|
||||
|
||||
.. automodule:: sympy.codegen.matrix_nodes
|
||||
:members:
|
||||
|
||||
Tools for simplifying expressions using approximations (sympy.codegen.approximations)
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
@@ -615,10 +612,3 @@ Fortran utilities (sympy.codegen.futils)
|
||||
|
||||
.. automodule:: sympy.codegen.futils
|
||||
:members:
|
||||
|
||||
|
||||
Array utilities (sympy.codegen.array_utils)
|
||||
-------------------------------------------
|
||||
|
||||
.. automodule:: sympy.codegen.array_utils
|
||||
:members:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _combinatiorics-docs:
|
||||
|
||||
=============
|
||||
Combinatorics
|
||||
=============
|
||||
====================
|
||||
Combinatorics Module
|
||||
====================
|
||||
|
||||
Contents
|
||||
========
|
||||
@@ -23,4 +23,3 @@ Contents
|
||||
testutil.rst
|
||||
tensor_can.rst
|
||||
fp_groups.rst
|
||||
pc_groups.rst
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
Polycyclic Groups
|
||||
=================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This module presents the functionality designed for computing with
|
||||
polycyclic groups(PcGroup for short). The name of the corresponding
|
||||
SymPy object is ``PolycyclicGroup``. The functions or classes described
|
||||
here are studied under **Computational Group Theory**.
|
||||
|
||||
Overview of functionalities
|
||||
```````````````````````````
|
||||
|
||||
* The construction of PolycyclicGroup from a given PermutationGroup.
|
||||
|
||||
* Computation of polycyclic generating sequence(pcgs for short) and
|
||||
polycyclic series(pc_series).
|
||||
|
||||
* Computation of relative order for polycyclic series.
|
||||
|
||||
* Implementation of class Collector which can be treated as a base for polycylic groups.
|
||||
|
||||
* Implementation of polycyclic group presentation(pc_presentation for short).
|
||||
|
||||
* Computation of exponent vector, depth and leading exponent for a given element
|
||||
of a polycyclic group.
|
||||
|
||||
For a description of fundamental algorithms of polycyclic groups, we
|
||||
often make use of *Handbook of Computational Group Theory*.
|
||||
|
||||
|
||||
The Construction of Polycyclic Groups
|
||||
-------------------------------------
|
||||
|
||||
Given a Permutation Group, A Polycyclic Group is constructed by computing the
|
||||
corresponding polycylic generating sequence, polycyclic series and it's
|
||||
relative order.
|
||||
|
||||
Attributes of PolycyclicGroup
|
||||
`````````````````````````````
|
||||
|
||||
* ``pc_sequence`` : Polycyclic sequence is formed by collecting all the missing
|
||||
generators between the adjacent groups in the derived series of given
|
||||
permutation group.
|
||||
|
||||
* ``pc_series`` : Polycyclic series is formed by adding all the missing generators
|
||||
of ``der[i+1]`` in ``der[i]``, where ``der`` represents derived series.
|
||||
|
||||
* ``relative_order`` : A list, computed by the ratio of adjacent groups in pc_series.
|
||||
|
||||
* ``collector`` : By default, it is None. Collector class provides the polycyclic presentation.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> len(PcGroup.pcgs)
|
||||
4
|
||||
>>> pc_series = PcGroup.pc_series
|
||||
>>> pc_series[0]==G
|
||||
True
|
||||
>>> gen = pc_series[len(pc_series)-1].generators[0]
|
||||
>>> gen.is_identity
|
||||
True
|
||||
>>> PcGroup.relative_order
|
||||
[2, 3, 2, 2]
|
||||
|
||||
|
||||
The Construction of Collector
|
||||
-----------------------------
|
||||
|
||||
Collector is one of the attributes of class PolycyclicGroup.
|
||||
|
||||
Attributes of Collector
|
||||
```````````````````````
|
||||
|
||||
Collector posses all the attributes of PolycyclicGroup, In addition there are
|
||||
few more attributes which are defined below:
|
||||
|
||||
* ``free_group`` : free_group provides the mapping of polycyclic generating sequence with
|
||||
the free group elements.
|
||||
|
||||
* ``pc_presentation`` : Provides the presentation of polycyclic groups with the
|
||||
help of power and conjugate relators.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> G = SymmetricGroup(3)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> Collector = PcGroup.collector
|
||||
>>> Collector.free_group
|
||||
<free group on the generators (x0, x1)>
|
||||
>>> Collector.pc_presentation
|
||||
{x0**2: (), x1**3: (), x0**-1*x1*x0: x1**2}
|
||||
|
||||
|
||||
Computation of Minimal Uncollected Subword
|
||||
``````````````````````````````````````````
|
||||
|
||||
A word ``V`` defined on generators in the free_group of pc_group is a minimal
|
||||
uncollected subword of the word ``W`` if ``V`` is a subword of ``W`` and it has one of
|
||||
the following form:
|
||||
|
||||
* `v = {x_{i+1}}^{a_j}x_i`
|
||||
* `v = {x_{i+1}}^{a_j}{x_i}^{-1}`
|
||||
* `v = {x_i}^{a_j}`
|
||||
|
||||
`a_j \notin \{0, \ldots \mathrm{relative\_order}[j]-1\}`.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> from sympy.combinatorics.free_groups import free_group
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> F, x1, x2 = free_group("x1, x2")
|
||||
>>> word = x2**2*x1**7
|
||||
>>> collector.minimal_uncollected_subword(word)
|
||||
((x2, 2),)
|
||||
|
||||
|
||||
Computation of Subword Index
|
||||
````````````````````````````
|
||||
|
||||
For a given word and it's subword, subword_index computes the
|
||||
starting and ending index of the subword in the word.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> from sympy.combinatorics.free_groups import free_group
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> F, x1, x2 = free_group("x1, x2")
|
||||
>>> word = x2**2*x1**7
|
||||
>>> w = x2**2*x1
|
||||
>>> collector.subword_index(word, w)
|
||||
(0, 3)
|
||||
>>> w = x1**7
|
||||
>>> collector.subword_index(word, w)
|
||||
(2, 9)
|
||||
|
||||
|
||||
Computation of Collected Word
|
||||
`````````````````````````````
|
||||
|
||||
A word ``W`` is called collected, if ``W`` `= {x_{i_1}}^{a_1} \ldots {x_{i_r}}^{a_r}`
|
||||
with `i_1 < i_2< \ldots < i_r` and `a_j` is in `\{1 \ldots s_{j-1}\}`, where `s_j`
|
||||
represents the respective relative order.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> from sympy.combinatorics.perm_groups import PermutationGroup
|
||||
>>> from sympy.combinatorics.free_groups import free_group
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3")
|
||||
>>> word = x3*x2*x1*x0
|
||||
>>> collected_word = collector.collected_word(word)
|
||||
>>> free_to_perm = {}
|
||||
>>> free_group = collector.free_group
|
||||
>>> for sym, gen in zip(free_group.symbols, collector.pcgs):
|
||||
... free_to_perm[sym] = gen
|
||||
>>> G1 = PermutationGroup()
|
||||
>>> for w in word:
|
||||
... sym = w[0]
|
||||
... perm = free_to_perm[sym]
|
||||
... G1 = PermutationGroup([perm] + G1.generators)
|
||||
>>> G2 = PermutationGroup()
|
||||
>>> for w in collected_word:
|
||||
... sym = w[0]
|
||||
... perm = free_to_perm[sym]
|
||||
... G2 = PermutationGroup([perm] + G2.generators)
|
||||
>>> G1 == G2
|
||||
True
|
||||
|
||||
|
||||
Computation of Polycyclic Presentation
|
||||
--------------------------------------
|
||||
|
||||
The computation of presentation starts from the bottom of the pcgs and polycyclic series.
|
||||
Storing all the previous generators from pcgs and then taking the last generator
|
||||
as the generator which acts as a conjugator and conjugates all the previous
|
||||
generators in the list.
|
||||
|
||||
To get a clear picture, start with an example of SymmetricGroup(4). For S(4) there are 4
|
||||
generators in pcgs say `[x_0, x_1, x_2, x_3]` and the relative_order vector is [2, 3, 2, 2].
|
||||
Starting from bottom of this sequence the presentation is computed in order as below.
|
||||
|
||||
using only `[x_3]` from ``pcgs`` and ``pc_series[4]`` compute:
|
||||
|
||||
* `x_3^2`
|
||||
|
||||
using only `[x_3]` from ``pcgs`` and ``pc_series[3]`` compute:
|
||||
|
||||
* `x_2^2`
|
||||
* `x_2^{-1}x_3x_2`
|
||||
|
||||
using `[x_3, x_2]` from ``pcgs`` and ``pc_series[2]`` compute:
|
||||
|
||||
* `x_1^3`
|
||||
* `x_1^{-1}x_3x_1`
|
||||
* `x_1^{-1}x_2x_1`
|
||||
|
||||
using `[x_3, x_2, x_1]` from ``pcgs`` and ``pc_series[1]`` compute:
|
||||
|
||||
* `x_0^2`
|
||||
* `x_0^{-1}x_3x_0`
|
||||
* `x_0^{-1}x_2x_0`
|
||||
* `x_0^{-1}x_1x_0`
|
||||
|
||||
|
||||
One thing to note is same group can have different pcgs due to variying derived_series which,
|
||||
results in different polycyclic presentations.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> from sympy.combinatorics.permutations import Permutation
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> pcgs = PcGroup.pcgs
|
||||
>>> len(pcgs)
|
||||
4
|
||||
>>> free_group = collector.free_group
|
||||
>>> pc_resentation = collector.pc_presentation
|
||||
>>> free_to_perm = {}
|
||||
>>> for s, g in zip(free_group.symbols, pcgs):
|
||||
... free_to_perm[s] = g
|
||||
>>> for k, v in pc_resentation.items():
|
||||
... k_array = k.array_form
|
||||
... if v != ():
|
||||
... v_array = v.array_form
|
||||
... lhs = Permutation()
|
||||
... for gen in k_array:
|
||||
... s = gen[0]
|
||||
... e = gen[1]
|
||||
... lhs = lhs*free_to_perm[s]**e
|
||||
... if v == ():
|
||||
... assert lhs.is_identity
|
||||
... continue
|
||||
... rhs = Permutation()
|
||||
... for gen in v_array:
|
||||
... s = gen[0]
|
||||
... e = gen[1]
|
||||
... rhs = rhs*free_to_perm[s]**e
|
||||
... assert lhs == rhs
|
||||
|
||||
|
||||
Computation of Exponent Vector
|
||||
------------------------------
|
||||
|
||||
Any generator of the polycyclic group can be represented with the help of it's
|
||||
polycyclic generating sequence. Hence, the length of exponent vector is equal to
|
||||
the length of the pcgs.
|
||||
|
||||
A given generator ``g`` of the polycyclic group, can be represented as
|
||||
`g = x_1^{e_1} \ldots x_n^{e_n}`, where `x_i` represents polycyclic generators
|
||||
and ``n`` is the number of generators in the free_group equal to the length of pcgs.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> from sympy.combinatorics.permutations import Permutation
|
||||
>>> G = SymmetricGroup(4)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> pcgs = PcGroup.pcgs
|
||||
>>> collector.exponent_vector(G[0])
|
||||
[1, 0, 0, 0]
|
||||
>>> exp = collector.exponent_vector(G[1])
|
||||
>>> g = Permutation()
|
||||
>>> for i in range(len(exp)):
|
||||
... g = g*pcgs[i]**exp[i] if exp[i] else g
|
||||
>>> assert g == G[1]
|
||||
|
||||
|
||||
Depth of Polycyclic generator
|
||||
`````````````````````````````
|
||||
|
||||
Depth of a given polycyclic generator is defined as the index of the first
|
||||
non-zero entry in the exponent vector.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> G = SymmetricGroup(3)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> collector.depth(G[0])
|
||||
2
|
||||
>>> collector.depth(G[1])
|
||||
1
|
||||
|
||||
|
||||
Computation of Leading Exponent
|
||||
```````````````````````````````
|
||||
|
||||
Leading exponent represents the exponent of polycyclic generator at the above depth.
|
||||
|
||||
>>> from sympy.combinatorics.named_groups import SymmetricGroup
|
||||
>>> G = SymmetricGroup(3)
|
||||
>>> PcGroup = G.polycyclic_group()
|
||||
>>> collector = PcGroup.collector
|
||||
>>> collector.leading_exponent(G[1])
|
||||
1
|
||||
|
||||
|
||||
Bibliography
|
||||
------------
|
||||
|
||||
.. [Ho05] Derek F. Holt,
|
||||
Handbook of Computational Group Theory.
|
||||
In the series 'Discrete Mathematics and its Applications',
|
||||
`Chapman & Hall/CRC 2005, xvi + 514 p <https://www.crcpress.com/Handbook-of-Computational-Group-Theory/Holt-Eick-OBrien/p/book/9781584883722>`_.
|
||||
@@ -1,6 +1,5 @@
|
||||
========
|
||||
Concrete
|
||||
========
|
||||
Concrete Mathematics
|
||||
====================
|
||||
|
||||
Hypergeometric terms
|
||||
--------------------
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
====
|
||||
Core
|
||||
====
|
||||
SymPy Core
|
||||
==========
|
||||
|
||||
sympify
|
||||
-------
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
============
|
||||
Cryptography
|
||||
============
|
||||
Basic Cryptography Module
|
||||
=========================
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -142,7 +141,3 @@ substitutions at different times in the message.
|
||||
.. autofunction:: encipher_gm
|
||||
|
||||
.. autofunction:: decipher_gm
|
||||
|
||||
.. autofunction:: encipher_railfence
|
||||
|
||||
.. autofunction:: decipher_railfence
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=====================
|
||||
Differential Geometry
|
||||
=====================
|
||||
Differential Geometry Module
|
||||
============================
|
||||
|
||||
.. module:: sympy.diffgeom
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
========
|
||||
Discrete
|
||||
========
|
||||
Discrete Module
|
||||
===============
|
||||
|
||||
The ``discrete`` module in SymPy implements methods to compute discrete
|
||||
transforms and convolutions of finite sequences.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.. _evalf-label:
|
||||
|
||||
====================
|
||||
Numerical Evaluation
|
||||
Numerical evaluation
|
||||
====================
|
||||
|
||||
Basics
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
.. _functions:
|
||||
|
||||
=========
|
||||
Functions
|
||||
=========
|
||||
Functions Module
|
||||
****************
|
||||
|
||||
.. module:: sympy.functions
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
========
|
||||
Geometry
|
||||
========
|
||||
===============
|
||||
Geometry Module
|
||||
===============
|
||||
|
||||
|
||||
Introduction
|
||||
|
||||
@@ -36,7 +36,7 @@ Here `D` is the Differential Operator and `L` is called the annihilator
|
||||
of the function.
|
||||
|
||||
A unique holonomic function can be defined from the annihilator and a set of
|
||||
initial conditions.
|
||||
initial conditons.
|
||||
For instance:
|
||||
|
||||
.. math::
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Converting other representations to holonomic
|
||||
=============================================
|
||||
|
||||
.. currentmodule:: sympy.holonomic.holonomic
|
||||
.. module:: sympy.holonomic.holonomic
|
||||
|
||||
Converting hypergeometric functions
|
||||
-----------------------------------
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _holonomic-docs:
|
||||
|
||||
=========
|
||||
Holonomic
|
||||
=========
|
||||
===================
|
||||
Holonomic Functions
|
||||
===================
|
||||
|
||||
.. automodule:: sympy.holonomic
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ a holonomic functions.
|
||||
>>> p * q
|
||||
HolonomicFunction((2) + (-2)*Dx + (1)*Dx**2, x, 0, [0, 1])
|
||||
|
||||
.. currentmodule:: sympy.holonomic.holonomic
|
||||
.. module:: sympy.holonomic.holonomic
|
||||
|
||||
Integration and Differentiation
|
||||
-------------------------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Representation of holonomic functions in SymPy
|
||||
==============================================
|
||||
|
||||
.. currentmodule:: sympy.holonomic.holonomic
|
||||
.. module:: sympy.holonomic.holonomic
|
||||
|
||||
Class :class:`DifferentialOperator` is used to represent the annihilator
|
||||
but we create differential operators easily using the function
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. currentmodule:: sympy.holonomic.holonomic
|
||||
.. module:: sympy.holonomic.holonomic
|
||||
|
||||
Uses and Current limitations
|
||||
============================
|
||||
|
||||
+23
-23
@@ -16,49 +16,49 @@ access any SymPy module, or use the list below:
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
algebras.rst
|
||||
assumptions/index.rst
|
||||
calculus/index.rst
|
||||
categories.rst
|
||||
codegen.rst
|
||||
combinatorics/index.rst
|
||||
concrete.rst
|
||||
core.rst
|
||||
combinatorics/index.rst
|
||||
ntheory.rst
|
||||
crypto.rst
|
||||
diffgeom.rst
|
||||
solvers/diophantine.rst
|
||||
concrete.rst
|
||||
discrete.rst
|
||||
evalf.rst
|
||||
codegen.rst
|
||||
numeric-computation.rst
|
||||
functions/index.rst
|
||||
geometry/index.rst
|
||||
holonomic/index.rst
|
||||
simplify/hyperexpand.rst
|
||||
solvers/inequalities.rst
|
||||
integrals/integrals.rst
|
||||
liealgebras/index.rst
|
||||
logic.rst
|
||||
matrices/index.rst
|
||||
ntheory.rst
|
||||
numeric-computation.rst
|
||||
evalf.rst
|
||||
solvers/ode.rst
|
||||
parsing.rst
|
||||
solvers/pde.rst
|
||||
physics/index.rst
|
||||
plotting.rst
|
||||
polys/index.rst
|
||||
printing.rst
|
||||
plotting.rst
|
||||
assumptions/index.rst
|
||||
rewriting.rst
|
||||
series/index.rst
|
||||
sets.rst
|
||||
simplify/simplify.rst
|
||||
solvers/solvers.rst
|
||||
solvers/solveset.rst
|
||||
simplify/hyperexpand.rst
|
||||
stats.rst
|
||||
solvers/ode.rst
|
||||
solvers/pde.rst
|
||||
solvers/solvers.rst
|
||||
solvers/diophantine.rst
|
||||
solvers/inequalities.rst
|
||||
solvers/solveset.rst
|
||||
tensor/index.rst
|
||||
rewriting.rst
|
||||
utilities/index.rst
|
||||
parsing.rst
|
||||
calculus/index.rst
|
||||
physics/index.rst
|
||||
categories.rst
|
||||
diffgeom.rst
|
||||
vector/index.rst
|
||||
algebras.rst
|
||||
|
||||
Contributions to Docs
|
||||
Contributions to docs
|
||||
---------------------
|
||||
|
||||
All contributions are welcome. If you'd like to improve something, look into
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=========
|
||||
Integrals
|
||||
=========
|
||||
Symbolic Integrals
|
||||
==================
|
||||
|
||||
.. module:: sympy.integrals
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
===========
|
||||
Lie Algebra
|
||||
===========
|
||||
==================
|
||||
Lie Algebra Module
|
||||
==================
|
||||
|
||||
.. automodule:: sympy.liealgebras
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=====
|
||||
Logic
|
||||
=====
|
||||
Logic Module
|
||||
============
|
||||
|
||||
.. module:: sympy.logic
|
||||
|
||||
|
||||
@@ -34,10 +34,6 @@ Matrix Expressions Core Reference
|
||||
:members:
|
||||
.. autoclass:: MatPow
|
||||
:members:
|
||||
.. autoclass:: HadamardProduct
|
||||
:members:
|
||||
.. autoclass:: HadamardPower
|
||||
:members:
|
||||
.. autoclass:: Inverse
|
||||
:members:
|
||||
.. autoclass:: Transpose
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=============
|
||||
Number Theory
|
||||
=============
|
||||
====================
|
||||
|
||||
.. module:: sympy.ntheory.generate
|
||||
|
||||
@@ -50,8 +49,6 @@ Ntheory Functions Reference
|
||||
|
||||
.. autofunction:: factorint
|
||||
|
||||
.. autofunction:: factorrat
|
||||
|
||||
.. autofunction:: primefactors
|
||||
|
||||
.. autofunction:: divisors
|
||||
@@ -88,20 +85,6 @@ Ntheory Functions Reference
|
||||
.. autoclass:: primeomega
|
||||
:members:
|
||||
|
||||
.. autofunction:: mersenne_prime_exponent
|
||||
|
||||
.. autofunction:: is_perfect
|
||||
|
||||
.. autofunction:: is_mersenne_prime
|
||||
|
||||
.. autofunction:: abundance
|
||||
|
||||
.. autofunction:: is_abundant
|
||||
|
||||
.. autofunction:: is_deficient
|
||||
|
||||
.. autofunction:: is_amicable
|
||||
|
||||
.. module:: sympy.ntheory.modular
|
||||
|
||||
.. autofunction:: symmetric_residue
|
||||
@@ -130,18 +113,8 @@ Ntheory Functions Reference
|
||||
|
||||
.. module:: sympy.ntheory.primetest
|
||||
|
||||
.. autofunction:: is_euler_pseudoprime
|
||||
|
||||
.. autofunction:: is_square
|
||||
|
||||
.. autofunction:: mr
|
||||
|
||||
.. autofunction:: is_lucas_prp
|
||||
|
||||
.. autofunction:: is_strong_lucas_prp
|
||||
|
||||
.. autofunction:: is_extra_strong_lucas_prp
|
||||
|
||||
.. autofunction:: isprime
|
||||
|
||||
.. module:: sympy.ntheory.residue_ntheory
|
||||
@@ -154,8 +127,6 @@ Ntheory Functions Reference
|
||||
|
||||
.. autofunction:: sqrt_mod
|
||||
|
||||
.. autofunction:: sqrt_mod_iter
|
||||
|
||||
.. autofunction:: quadratic_residues
|
||||
|
||||
.. autofunction:: nthroot_mod
|
||||
@@ -179,7 +150,3 @@ Ntheory Functions Reference
|
||||
.. module:: sympy.ntheory.egyptian_fraction
|
||||
|
||||
.. autofunction:: egyptian_fraction
|
||||
|
||||
.. module:: sympy.ntheory.bbp_pi
|
||||
|
||||
.. autofunction:: pi_hex_digits
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=======
|
||||
Parsing
|
||||
=======
|
||||
Parsing input
|
||||
=============
|
||||
|
||||
Parsing Functions Reference
|
||||
---------------------------
|
||||
@@ -91,27 +90,6 @@ change between releases, and become stricter, more relaxed, or some mix.
|
||||
|
||||
.. autoclass:: sympy.parsing.latex.LaTeXParsingError
|
||||
|
||||
|
||||
SymPy Expression Reference
|
||||
--------------------------
|
||||
|
||||
.. module:: sympy.parsing.sym_expr
|
||||
|
||||
.. autoclass:: SymPyExpression
|
||||
:members:
|
||||
|
||||
`Fortran` Parsing Reference
|
||||
---------------------------------
|
||||
|
||||
.. module:: sympy.parsing.fortran.fortran_parser
|
||||
|
||||
.. autoclass:: ASR2PyVisitor
|
||||
:members:
|
||||
|
||||
.. autofunction:: call_visitor
|
||||
|
||||
.. autofunction:: src_to_sympy
|
||||
|
||||
Runtime Installation
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ final loading can be displayed:
|
||||
- 258⋅<x> + 52⋅<x> - 8⋅<x> + 50⋅<x - 5> + 8⋅<x - 5> - 12⋅<x - 9>
|
||||
|
||||
At this point, the beam is fully defined and the internal shear and bending
|
||||
moments are calculated:
|
||||
moments are calculated like so:
|
||||
|
||||
.. plot::
|
||||
:context:
|
||||
@@ -246,7 +246,7 @@ Example 2
|
||||
---------
|
||||
|
||||
There is a beam of length 30 meters. A moment of magnitude 120 Nm is
|
||||
applied in the clockwise direction at the end of the beam. A point load
|
||||
applied in the clockwise direction at the end of the beam. A pointload
|
||||
of magnitude 8 N is applied from the top of the beam at the starting
|
||||
point. There are two simple supports below the beam. One at the end
|
||||
and another one at a distance of 10 meters from the start. The
|
||||
@@ -377,7 +377,7 @@ Example 4
|
||||
An overhanging beam of length 8 meters is pinned at 1 meter from starting point
|
||||
and supported by a roller 1 meter before the other end. It is subjected
|
||||
to a distributed constant load of 10 KN/m from the starting point till
|
||||
2 meters away from it. Two point loads of 20KN and 8KN are applied at
|
||||
2 meters away from it. Two pointloads of 20KN and 8KN are applied at
|
||||
5 meters and 7.5 meters away from the starting point respectively.
|
||||
|
||||
::
|
||||
@@ -738,7 +738,7 @@ Example 9
|
||||
---------
|
||||
|
||||
There is a cantilever beam of length 4 meters. For first 2 meters
|
||||
its moment of inertia is ``1.5*I`` and ``I`` for the rest.
|
||||
its moment of inertia is ``1.5*I`` and ``I`` for the other end.
|
||||
A pointload of magnitude 20 N is applied from the top at its free end.
|
||||
|
||||
::
|
||||
@@ -791,7 +791,7 @@ Example 10
|
||||
|
||||
A combined beam, with constant flexural rigidity ``E*I``, is formed by joining
|
||||
a Beam of length ``2*l`` to the right of another Beam of length ``l``. The whole beam
|
||||
is fixed at both of its ends. A point load of magnitude ``P`` is also applied
|
||||
is fixed at both of its both end. A point load of magnitude ``P`` is also applied
|
||||
from the top at a distance of ``2*l`` from starting point.
|
||||
|
||||
::
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _physics-docs:
|
||||
|
||||
=======
|
||||
Physics
|
||||
=======
|
||||
==============
|
||||
Physics Module
|
||||
==============
|
||||
|
||||
.. automodule:: sympy.physics
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ The parser can be used as follows::
|
||||
>>> sympy_code = parse_autolev(open('double_pendulum.al'), include_numeric=True)
|
||||
|
||||
# The include_pydy flag is False by default. Setting it to True will
|
||||
# enable PyDy simulation code to be outputted if applicable.
|
||||
# enable PyDy simulation code to be outputed if applicable.
|
||||
|
||||
>>> print(sympy_code)
|
||||
import sympy.physics.mechanics as me
|
||||
|
||||
@@ -223,42 +223,40 @@ The following example shows how to use the momenta functions in
|
||||
One begins by creating the requisite symbols to describe the system. Then
|
||||
the reference frame is created and the kinematics are done. ::
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, Particle, Point, outer
|
||||
>>> from sympy.physics.mechanics import linear_momentum, angular_momentum
|
||||
>>> m, M, l1 = symbols('m M l1')
|
||||
>>> q1d = dynamicsymbols('q1d')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> Ac = O.locatenew('Ac', l1 * N.x)
|
||||
>>> P = Ac.locatenew('P', l1 * N.x)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> a.set_ang_vel(N, q1d * N.z)
|
||||
>>> Ac.v2pt_theory(O, N, a)
|
||||
l1*q1d*N.y
|
||||
>>> P.v2pt_theory(O, N, a)
|
||||
2*l1*q1d*N.y
|
||||
>> from sympy import symbols
|
||||
>> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame
|
||||
>> from sympy.physics.mechanics import RigidBody, Particle, Point, outer
|
||||
>> from sympy.physics.mechanics import linear_momentum, angular_momentum
|
||||
>> m, M, l1 = symbols('m M l1')
|
||||
>> q1d = dynamicsymbols('q1d')
|
||||
>> N = ReferenceFrame('N')
|
||||
>> O = Point('O')
|
||||
>> O.set_vel(N, 0 * N.x)
|
||||
>> Ac = O.locatenew('Ac', l1 * N.x)
|
||||
>> P = Ac.locatenew('P', l1 * N.x)
|
||||
>> a = ReferenceFrame('a')
|
||||
>> a.set_ang_vel(N, q1d * N.z)
|
||||
>> Ac.v2pt_theory(O, N, a)
|
||||
>> P.v2pt_theory(O, N, a)
|
||||
|
||||
Finally, the bodies that make up the system are created. In this case the
|
||||
system consists of a particle Pa and a RigidBody A. ::
|
||||
|
||||
>>> Pa = Particle('Pa', P, m)
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, M, (I, Ac))
|
||||
>> Pa = Particle('Pa', P, m)
|
||||
>> I = outer(N.z, N.z)
|
||||
>> A = RigidBody('A', Ac, a, M, (I, Ac))
|
||||
|
||||
Then one can either choose to evaluate the momenta of individual components
|
||||
of the system or of the entire system itself. ::
|
||||
|
||||
>>> linear_momentum(N,A)
|
||||
>> linear_momentum(N,A)
|
||||
M*l1*q1d*N.y
|
||||
>>> angular_momentum(O, N, Pa)
|
||||
>> angular_momentum(O, N, Pa)
|
||||
4*l1**2*m*q1d*N.z
|
||||
>>> linear_momentum(N, A, Pa)
|
||||
>> linear_momentum(N, A, Pa)
|
||||
(M*l1*q1d + 2*l1*m*q1d)*N.y
|
||||
>>> angular_momentum(O, N, A, Pa)
|
||||
(M*l1**2*q1d + 4*l1**2*m*q1d + q1d)*N.z
|
||||
>> angular_momentum(O, N, A, Pa)
|
||||
(4*l1**2*m*q1d + q1d)*N.z
|
||||
|
||||
It should be noted that the user can determine either momenta in any frame
|
||||
in :mod:`mechanics` as the user is allowed to specify the reference frame when
|
||||
@@ -327,34 +325,33 @@ The following example shows how to use the energy functions in
|
||||
As was discussed above in the momenta functions, one first creates the system
|
||||
by going through an identical procedure. ::
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame, outer
|
||||
>>> from sympy.physics.mechanics import RigidBody, Particle
|
||||
>>> from sympy.physics.mechanics import kinetic_energy, potential_energy, Point
|
||||
>>> m, M, l1, g, h, H = symbols('m M l1 g h H')
|
||||
>>> omega = dynamicsymbols('omega')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> Ac = O.locatenew('Ac', l1 * N.x)
|
||||
>>> P = Ac.locatenew('P', l1 * N.x)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> a.set_ang_vel(N, omega * N.z)
|
||||
>>> Ac.v2pt_theory(O, N, a)
|
||||
l1*omega*N.y
|
||||
>>> P.v2pt_theory(O, N, a)
|
||||
2*l1*omega*N.y
|
||||
>>> Pa = Particle('Pa', P, m)
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, M, (I, Ac))
|
||||
>> from sympy import symbols
|
||||
>> from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame, outer
|
||||
>> from sympy.physics.mechanics import RigidBody, Particle, mechanics_printing
|
||||
>> from symp.physics.mechanics import kinetic_energy, potential_energy, Point
|
||||
>> mechanics_printing()
|
||||
>> m, M, l1, g, h, H = symbols('m M l1 g h H')
|
||||
>> omega = dynamicsymbols('omega')
|
||||
>> N = ReferenceFrame('N')
|
||||
>> O = Point('O')
|
||||
>> O.set_vel(N, 0 * N.x)
|
||||
>> Ac = O.locatenew('Ac', l1 * N.x)
|
||||
>> P = Ac.locatenew('P', l1 * N.x)
|
||||
>> a = ReferenceFrame('a')
|
||||
>> a.set_ang_vel(N, omega * N.z)
|
||||
>> Ac.v2pt_theory(O, N, a)
|
||||
>> P.v2pt_theory(O, N, a)
|
||||
>> Pa = Particle('Pa', P, m)
|
||||
>> I = outer(N.z, N.z)
|
||||
>> A = RigidBody('A', Ac, a, M, (I, Ac))
|
||||
|
||||
The user can then determine the kinetic energy of any number of entities of the
|
||||
system: ::
|
||||
|
||||
>>> kinetic_energy(N, Pa)
|
||||
2*l1**2*m*omega(t)**2
|
||||
>>> kinetic_energy(N, Pa, A)
|
||||
M*l1**2*omega(t)**2/2 + 2*l1**2*m*omega(t)**2 + omega(t)**2/2
|
||||
>> kinetic_energy(N, Pa)
|
||||
2*l1**2*m*q1d**2
|
||||
>> kinetic_energy(N, Pa, A)
|
||||
M*l1**2*q1d**2/2 + 2*l1**2*m*q1d**2 + q1d**2/2
|
||||
|
||||
It should be noted that the user can determine either kinetic energy relative
|
||||
to any frame in :mod:`mechanics` as the user is allowed to specify the
|
||||
@@ -366,15 +363,14 @@ every entity of the system using the :mod:`potential_energy` property. The
|
||||
potential energy of any number of entities comprising the system can then be
|
||||
determined: ::
|
||||
|
||||
>>> Pa.potential_energy = m * g * h
|
||||
>>> A.potential_energy = M * g * H
|
||||
>>> potential_energy(A, Pa)
|
||||
>> Pa.potential_energy = m * g * h
|
||||
>> A.potential_energy = M * g * H
|
||||
>> potential_energy(A, Pa)
|
||||
H*M*g + g*h*m
|
||||
|
||||
One can also determine the Lagrangian for this system: ::
|
||||
|
||||
>>> from sympy.physics.mechanics import Lagrangian
|
||||
>>> Lagrangian(N, Pa, A)
|
||||
-H*M*g + M*l1**2*omega(t)**2/2 - g*h*m + 2*l1**2*m*omega(t)**2 + omega(t)**2/2
|
||||
>> Lagrangian(Pa, A)
|
||||
-H*M*g + M*l1**2*q1d**2/2 - g*h*m + 2*l1**2*m*q1d**2 + q1d**2/2
|
||||
|
||||
Please refer to the docstrings to learn more about each function.
|
||||
|
||||
@@ -108,9 +108,9 @@ the outcoming result):
|
||||
|
||||
>>> from sympy.physics.units import convert_to
|
||||
>>> convert_to(q, day)
|
||||
71.5112118495813*pi*day
|
||||
71.5123904642338*pi*day
|
||||
>>> convert_to(q, day).n()
|
||||
224.659097795948*day
|
||||
224.662800523082*day
|
||||
|
||||
We could also have the solar mass and the day as units coming from the
|
||||
astrophysical system, but we wanted to show how to create a unit that one needs.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
========
|
||||
Plotting
|
||||
========
|
||||
Plotting Module
|
||||
===============
|
||||
|
||||
.. module:: sympy.plotting.plot
|
||||
|
||||
@@ -88,8 +87,8 @@ Series Classes
|
||||
|
||||
|
||||
|
||||
Pyglet Plotting
|
||||
---------------
|
||||
Pyglet Plotting Module
|
||||
======================
|
||||
|
||||
.. module:: sympy.plotting.pygletplot
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _polys-docs:
|
||||
|
||||
=======================
|
||||
Polynomial Manipulation
|
||||
=======================
|
||||
===============================
|
||||
Polynomials Manipulation Module
|
||||
===============================
|
||||
|
||||
Computations with polynomials are at the core of computer algebra and
|
||||
having a fast and robust polynomials manipulation module is a key for
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
========
|
||||
Printing
|
||||
========
|
||||
Printing System
|
||||
===============
|
||||
|
||||
See the :ref:`tutorial-printing` section in tutorial for introduction into
|
||||
See the :ref:`tutorial-printing` section in Tutorial for introduction into
|
||||
printing.
|
||||
|
||||
This guide documents the printing system in SymPy and how it works
|
||||
@@ -14,7 +13,8 @@ Printer Class
|
||||
.. automodule:: sympy.printing.printer
|
||||
|
||||
The main class responsible for printing is ``Printer`` (see also its
|
||||
`source code <https://github.com/sympy/sympy/blob/master/sympy/printing/printer.py>`_):
|
||||
`source code
|
||||
<https://github.com/sympy/sympy/blob/master/sympy/printing/printer.py>`_):
|
||||
|
||||
.. autoclass:: Printer
|
||||
:members: doprint, _print, set_global_settings, order
|
||||
@@ -185,10 +185,10 @@ An example where line wrapping is required:
|
||||
|
||||
>>> expr = sqrt(1-x**2).series(x,n=20).removeO()
|
||||
>>> print(fcode(expr))
|
||||
-715.0d0/65536.0d0*x**18 - 429.0d0/32768.0d0*x**16 - 33.0d0/
|
||||
@ 2048.0d0*x**14 - 21.0d0/1024.0d0*x**12 - 7.0d0/256.0d0*x**10 -
|
||||
@ 5.0d0/128.0d0*x**8 - 1.0d0/16.0d0*x**6 - 1.0d0/8.0d0*x**4 - 1.0d0
|
||||
@ /2.0d0*x**2 + 1
|
||||
-715.0d0/65536.0d0*x**18 - 429.0d0/32768.0d0*x**16 - 33.0d0/
|
||||
@ 2048.0d0*x**14 - 21.0d0/1024.0d0*x**12 - 7.0d0/256.0d0*x**10 -
|
||||
@ 5.0d0/128.0d0*x**8 - 1.0d0/16.0d0*x**6 - 1.0d0/8.0d0*x**4 - 1.0d0
|
||||
@ /2.0d0*x**2 + 1
|
||||
|
||||
In case of line wrapping, it is handy to include the assignment so that lines
|
||||
are wrapped properly when the assignment part is added.
|
||||
@@ -301,20 +301,6 @@ Mathematica code printing
|
||||
|
||||
.. autofunction:: sympy.printing.mathematica.mathematica_code
|
||||
|
||||
Maple code printing
|
||||
-------------------
|
||||
|
||||
.. module:: sympy.printing.maple
|
||||
|
||||
.. autoclass:: sympy.printing.maple.MapleCodePrinter
|
||||
:members:
|
||||
|
||||
.. autoattribute:: MapleCodePrinter.printmethod
|
||||
|
||||
.. autofunction:: sympy.printing.maple.maple_code
|
||||
|
||||
.. autofunction:: sympy.printing.maple.print_maple_code
|
||||
|
||||
Javascript Code printing
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
==============
|
||||
Term Rewriting
|
||||
Term rewriting
|
||||
==============
|
||||
|
||||
Term rewriting is a very general class of functionalities which are used to
|
||||
@@ -62,7 +61,7 @@ use ``rewrite()`` to do specific type of rewrite::
|
||||
Common Subexpression Detection and Collection
|
||||
---------------------------------------------
|
||||
|
||||
.. currentmodule:: sympy.simplify.cse_main
|
||||
.. module:: sympy.simplify.cse_main
|
||||
|
||||
Before evaluating a large expression, it is often useful to identify common
|
||||
subexpressions, collect them and evaluate them at once. This is implemented
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _series-docs:
|
||||
|
||||
======
|
||||
Series
|
||||
======
|
||||
=============
|
||||
Series Module
|
||||
=============
|
||||
|
||||
The series module implements series expansions as a function and many related
|
||||
functions.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
====
|
||||
Sets
|
||||
====
|
||||
===========
|
||||
|
||||
.. automodule:: sympy.sets.sets
|
||||
|
||||
@@ -94,27 +93,3 @@ ComplexRegion
|
||||
:members:
|
||||
|
||||
.. autofunction:: normalize_theta_set
|
||||
|
||||
Iteration over sets
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For set unions, `\{a, b\} \cup \{x, y\}` can be treated as
|
||||
`\{a, b, x, y\}` for iteration regardless of the distinctiveness of
|
||||
the elements, however, for set intersections, assuming that
|
||||
`\{a, b\} \cap \{x, y\}` is `\varnothing` or `\{a, b \}` would not
|
||||
always be valid, since some of `a`, `b`, `x` or `y` may or may not be
|
||||
the elements of the intersection.
|
||||
|
||||
Iterating over the elements of a set involving intersection, complement,
|
||||
or symmetric difference yields (possibly duplicate) elements of the set
|
||||
provided that all elements are known to be the elements of the set.
|
||||
If any element cannot be determined to be a member of a set then the
|
||||
iteration gives ``TypeError``.
|
||||
This happens in the same cases where ``x in y`` would give an error.
|
||||
|
||||
There are some reasons to implement like this, even if it breaks the
|
||||
consistency with how the python set iterator works.
|
||||
We keep in mind that sympy set comprehension like ``FiniteSet(*s)`` from
|
||||
a existing sympy sets could be a common usage.
|
||||
And this approach would make ``FiniteSet(*s)`` to be consistent with any
|
||||
symbolic set processing methods like ``FiniteSet(*simplify(s))``.
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
========================
|
||||
Hypergeometric Expansion
|
||||
========================
|
||||
Details on the Hypergeometric Function Expansion Module
|
||||
#######################################################
|
||||
|
||||
This page describes how the function :func:`hyperexpand` and related code
|
||||
work. For usage, see the documentation of the symplify module.
|
||||
|
||||
Hypergeometric Function Expansion Algorithm
|
||||
===========================================
|
||||
*******************************************
|
||||
|
||||
This section describes the algorithm used to expand hypergeometric functions.
|
||||
Most of it is based on the papers [Roach1996]_ and [Roach1997]_.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
========
|
||||
Simplify
|
||||
========
|
||||
********
|
||||
|
||||
.. currentmodule:: sympy.simplify.simplify
|
||||
|
||||
@@ -16,10 +15,6 @@ nthroot
|
||||
-------
|
||||
.. autofunction:: nthroot
|
||||
|
||||
kroneckersimp
|
||||
-------------
|
||||
.. autofunction:: kroneckersimp
|
||||
|
||||
besselsimp
|
||||
----------
|
||||
.. autofunction:: besselsimp
|
||||
|
||||
@@ -64,10 +64,6 @@ the various ODE solving methods. For this reason, they are documented here.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_sol_simplicity
|
||||
|
||||
:obj:`factorable`
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_factorable
|
||||
|
||||
:obj:`1st_exact`
|
||||
^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_1st_exact
|
||||
@@ -88,14 +84,6 @@ the various ODE solving methods. For this reason, they are documented here.
|
||||
^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_1st_linear
|
||||
|
||||
:obj:`2nd_linear_airy`
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_2nd_linear_airy
|
||||
|
||||
:obj:`2nd_linear_bessel`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_2nd_linear_bessel
|
||||
|
||||
:obj:`Bernoulli`
|
||||
^^^^^^^^^^^^^^^^
|
||||
.. autofunction:: sympy.solvers.ode.ode_Bernoulli
|
||||
|
||||
@@ -524,7 +524,7 @@ How are symbolic parameters handled in solveset?
|
||||
>>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)
|
||||
Interval(0, 2)
|
||||
>>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)
|
||||
Union(Interval(1, 2), Interval(-sqrt(2), -1))
|
||||
Union(Interval(-sqrt(2), -1), Interval(1, 2))
|
||||
|
||||
|
||||
References
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
=====
|
||||
Stats
|
||||
=====
|
||||
===========
|
||||
|
||||
.. automodule:: sympy.stats
|
||||
|
||||
@@ -8,7 +7,7 @@ Random Variable Types
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Finite Types
|
||||
------------
|
||||
---------------
|
||||
.. autofunction:: DiscreteUniform
|
||||
.. autofunction:: Die
|
||||
.. autofunction:: Bernoulli
|
||||
@@ -19,7 +18,7 @@ Finite Types
|
||||
.. autofunction:: Rademacher
|
||||
|
||||
Discrete Types
|
||||
--------------
|
||||
-----------------
|
||||
.. autofunction:: Geometric
|
||||
.. autofunction:: Poisson
|
||||
.. autofunction:: Logarithmic
|
||||
@@ -28,7 +27,7 @@ Discrete Types
|
||||
.. autofunction:: Zeta
|
||||
|
||||
Continuous Types
|
||||
----------------
|
||||
-------------------
|
||||
|
||||
.. autofunction:: Arcsin
|
||||
.. autofunction:: Benini
|
||||
@@ -70,22 +69,6 @@ Continuous Types
|
||||
.. autofunction:: WignerSemicircle
|
||||
.. autofunction:: ContinuousRV
|
||||
|
||||
Joint Types
|
||||
-----------
|
||||
.. autofunction:: JointRV
|
||||
.. autofunction:: GeneralizedMultivariateLogGamma
|
||||
.. autofunction:: GeneralizedMultivariateLogGammaOmega
|
||||
.. autofunction:: Multinomial
|
||||
.. autofunction:: MultivariateBeta
|
||||
.. autofunction:: MultivariateEwens
|
||||
.. autofunction:: MultivariateT
|
||||
.. autofunction:: NegativeMultinomial
|
||||
.. autofunction:: NormalGamma
|
||||
|
||||
Stochastic Processes
|
||||
--------------------
|
||||
.. autofunction:: DiscreteMarkovChain
|
||||
|
||||
Interface
|
||||
^^^^^^^^^
|
||||
|
||||
@@ -94,7 +77,6 @@ Interface
|
||||
.. autofunction:: E
|
||||
.. autoclass:: Expectation
|
||||
.. autofunction:: density
|
||||
.. autofunction:: entropy
|
||||
.. autofunction:: given
|
||||
.. autofunction:: where
|
||||
.. autofunction:: variance
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.. _tensor_module:
|
||||
|
||||
======
|
||||
Tensor
|
||||
======
|
||||
=============
|
||||
Tensor Module
|
||||
=============
|
||||
|
||||
.. automodule:: sympy.tensor
|
||||
|
||||
|
||||
@@ -5,12 +5,25 @@ Tensor
|
||||
|
||||
.. module:: sympy.tensor.tensor
|
||||
|
||||
.. autoclass:: _TensorManager
|
||||
:members:
|
||||
|
||||
.. autoclass:: TensorIndexType
|
||||
:members:
|
||||
|
||||
.. autoclass:: TensorIndex
|
||||
:members:
|
||||
|
||||
.. autofunction:: tensor_indices
|
||||
|
||||
.. autoclass:: TensorSymmetry
|
||||
:members:
|
||||
|
||||
.. autofunction:: tensorsymmetry
|
||||
|
||||
.. autoclass:: TensorType
|
||||
:members:
|
||||
|
||||
.. autoclass:: TensorHead
|
||||
:members:
|
||||
|
||||
@@ -25,17 +38,8 @@ Tensor
|
||||
|
||||
.. autofunction:: canon_bp
|
||||
|
||||
.. autofunction:: tensor_mul
|
||||
|
||||
.. autofunction:: riemann_cyclic_replace
|
||||
|
||||
.. autofunction:: riemann_cyclic
|
||||
|
||||
.. autoclass:: TensorSymmetry
|
||||
:members:
|
||||
|
||||
.. autofunction:: tensorsymmetry
|
||||
|
||||
.. autoclass:: TensorType
|
||||
:members:
|
||||
|
||||
.. autoclass:: _TensorManager
|
||||
:members:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
======
|
||||
Vector
|
||||
======
|
||||
=============
|
||||
Vector Module
|
||||
=============
|
||||
|
||||
The vector module provides tools for basic vector math
|
||||
and differential calculus with respect to 3D Cartesian coordinate
|
||||
|
||||
@@ -101,7 +101,7 @@ Coefficients of `c_i` evaluated at `x_i + 2*h`:
|
||||
>>> m32 = P(x0+2*h, x0, c, n).diff(c[1])
|
||||
>>> m33 = P(x0+2*h, x0, c, n).diff(c[2])
|
||||
|
||||
Matrix of the coefficients is 3x3 in this case:
|
||||
Matrix of the coeffcients is 3x3 in this case:
|
||||
|
||||
>>> M = Matrix([[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]])
|
||||
|
||||
@@ -143,7 +143,7 @@ at `x_i` and thus using points at `x_{i-1}`, `x_{i}`, and `x_{i+1}`. So here i
|
||||
>>> m31 = P(x0+h, x0, c, n).diff(c[0])
|
||||
>>> m32 = P(x0+h, x0, c, n).diff(c[1])
|
||||
>>> m33 = P(x0+h, x0, c, n).diff(c[2])
|
||||
>>> # matrix of the coefficients is 3x3 in this case
|
||||
>>> # matrix of the coeffcients is 3x3 in this case
|
||||
>>> M = Matrix([[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]])
|
||||
|
||||
Now that we have the matrix of coefficients we next form the right-hand-side and solve by inverting `M`:
|
||||
@@ -219,7 +219,7 @@ degree polynomial P coefficients of `c_i` evaluated at `x_i, x_{i-1},` and `x_{i
|
||||
>>> m32 = P(xN-2*h, xN, c, n).diff(c[1])
|
||||
>>> m33 = P(xN-2*h, xN, c, n).diff(c[2])
|
||||
|
||||
Next we construct the `3 \times 3` matrix of the coefficients:
|
||||
Next we construct the `3 \times 3` matrix of the coeffcients:
|
||||
|
||||
>>> M = Matrix([[m11, m12, m13], [m21, m22, m23], [m31, m32, m33]])
|
||||
>>> # matrix of the function values...actually a vector of right hand sides
|
||||
@@ -247,8 +247,7 @@ Also, we note that output of formats appropriate to Fortran, C, etc. may be d
|
||||
given above.
|
||||
|
||||
Next we show how to perform these and many other discritizations of derivatives, but using a
|
||||
much more efficient approach originally due to Bengt Fornberg and now incorporated into SymPy.
|
||||
much more efficient approach originally due to Bengt Fornberg and now incorported into SymPy.
|
||||
|
||||
|
||||
:ref:`calculus-finite-differences`
|
||||
|
||||
:ref:`Finite difference weights <finite_diff>`
|
||||
|
||||
@@ -196,11 +196,11 @@ definite integrals. Here is a sampling of some of the power of ``integrate``.
|
||||
⎮ sin⎝x ⎠ dx
|
||||
⌡
|
||||
>>> integ.doit()
|
||||
⎛√2⋅x⎞
|
||||
3⋅√2⋅√π⋅S⎜────⎟⋅Γ(3/4)
|
||||
⎝ √π ⎠
|
||||
──────────────────────
|
||||
8⋅Γ(7/4)
|
||||
⎛√2⋅x⎞
|
||||
3⋅√2⋅√π⋅fresnels⎜────⎟⋅Γ(3/4)
|
||||
⎝ √π ⎠
|
||||
─────────────────────────────
|
||||
8⋅Γ(7/4)
|
||||
|
||||
>>> integ = Integral(x**y*exp(-x), (x, 0, oo))
|
||||
>>> integ
|
||||
@@ -322,8 +322,6 @@ The ``O`` notation supports arbitrary limit points (other than 0):
|
||||
-5 + ──────── + ──────── + ──────── + ──────── + x + O⎝(x - 6) ; x → 6⎠
|
||||
2 6 24 120
|
||||
|
||||
.. _calculus-finite-differences:
|
||||
|
||||
Finite differences
|
||||
==================
|
||||
|
||||
|
||||
@@ -30,38 +30,37 @@ expression looks like internally by using ``srepr``
|
||||
The easiest way to tear this apart is to look at a diagram of the expression
|
||||
tree:
|
||||
|
||||
.. This comes from dotprint(x**2 + x*y, labelfunc=srepr)
|
||||
.. This comes from dotprint(2**x + x*y, labelfunc=srepr)
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph{
|
||||
|
||||
# Graph style
|
||||
"ordering"="out"
|
||||
"rankdir"="TD"
|
||||
|
||||
#########
|
||||
# Nodes #
|
||||
#########
|
||||
|
||||
"Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))_()" ["color"="black", "label"="Add", "shape"="ellipse"];
|
||||
"Pow(Symbol('x'), Integer(2))_(0,)" ["color"="black", "label"="Pow", "shape"="ellipse"];
|
||||
"Symbol('x')_(0, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"];
|
||||
"Integer(2)_(0, 1)" ["color"="black", "label"="Integer(2)", "shape"="ellipse"];
|
||||
"Mul(Symbol('x'), Symbol('y'))_(1,)" ["color"="black", "label"="Mul", "shape"="ellipse"];
|
||||
"Symbol('x')_(1, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"];
|
||||
"Symbol('y')_(1, 1)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"];
|
||||
"Symbol(x)_(0, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"];
|
||||
"Integer(2)_(1, 1)" ["color"="black", "label"="Integer(2)", "shape"="ellipse"];
|
||||
"Symbol(y)_(0, 1)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"];
|
||||
"Symbol(x)_(1, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"];
|
||||
"Mul(Symbol(x), Symbol(y))_(0,)" ["color"="black", "label"="Mul", "shape"="ellipse"];
|
||||
"Pow(Symbol(x), Integer(2))_(1,)" ["color"="black", "label"="Pow", "shape"="ellipse"];
|
||||
"Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" ["color"="black", "label"="Add", "shape"="ellipse"];
|
||||
|
||||
#########
|
||||
# Edges #
|
||||
#########
|
||||
|
||||
"Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))_()" -> "Pow(Symbol('x'), Integer(2))_(0,)";
|
||||
"Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))_()" -> "Mul(Symbol('x'), Symbol('y'))_(1,)";
|
||||
"Pow(Symbol('x'), Integer(2))_(0,)" -> "Symbol('x')_(0, 0)";
|
||||
"Pow(Symbol('x'), Integer(2))_(0,)" -> "Integer(2)_(0, 1)";
|
||||
"Mul(Symbol('x'), Symbol('y'))_(1,)" -> "Symbol('x')_(1, 0)";
|
||||
"Mul(Symbol('x'), Symbol('y'))_(1,)" -> "Symbol('y')_(1, 1)";
|
||||
"Mul(Symbol(x), Symbol(y))_(0,)" -> "Symbol(x)_(0, 0)";
|
||||
"Mul(Symbol(x), Symbol(y))_(0,)" -> "Symbol(y)_(0, 1)";
|
||||
"Pow(Symbol(x), Integer(2))_(1,)" -> "Symbol(x)_(1, 0)";
|
||||
"Pow(Symbol(x), Integer(2))_(1,)" -> "Integer(2)_(1, 1)";
|
||||
"Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" -> "Mul(Symbol(x), Symbol(y))_(0,)";
|
||||
"Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" -> "Pow(Symbol(x), Integer(2))_(1,)";
|
||||
}
|
||||
|
||||
.. note::
|
||||
@@ -83,16 +82,16 @@ expression, 2, we got ``Integer(2)``. ``Integer`` is the SymPy class for
|
||||
integers. It is similar to the Python built-in type ``int``, except that
|
||||
``Integer`` plays nicely with other SymPy types.
|
||||
|
||||
When we write ``x**2``, this creates a ``Pow`` object. ``Pow`` is short for
|
||||
When we write ``2**x``, this creates a ``Pow`` object. ``Pow`` is short for
|
||||
"power".
|
||||
|
||||
>>> srepr(x**2)
|
||||
"Pow(Symbol('x'), Integer(2))"
|
||||
>>> srepr(2**x)
|
||||
"Pow(Integer(2), Symbol('x'))"
|
||||
|
||||
We could have created the same object by calling ``Pow(x, 2)``
|
||||
We could have created the same object by calling ``Pow(2, x)``
|
||||
|
||||
>>> Pow(x, 2)
|
||||
x**2
|
||||
>>> Pow(2, x)
|
||||
2**x
|
||||
|
||||
Note that in the ``srepr`` output, we see ``Integer(2)``, the SymPy version of
|
||||
integers, even though technically, we input ``2``, a Python int. In general,
|
||||
@@ -105,7 +104,7 @@ function that does this is ``sympify`` [#sympify-fn]_.
|
||||
>>> type(sympify(2))
|
||||
<class 'sympy.core.numbers.Integer'>
|
||||
|
||||
We have seen that ``x**2`` is represented as ``Pow(x, 2)``. What about
|
||||
We have seen that ``2**x`` is represented as ``Pow(2, x)``. What about
|
||||
``x*y``? As we might expect, this is the multiplication of ``x`` and ``y``.
|
||||
The SymPy class for multiplication is ``Mul``.
|
||||
|
||||
@@ -117,13 +116,13 @@ Thus, we could have created the same object by writing ``Mul(x, y)``.
|
||||
>>> Mul(x, y)
|
||||
x*y
|
||||
|
||||
Now we get to our final expression, ``x**2 + x*y``. This is the addition of
|
||||
our last two objects, ``Pow(x, 2)``, and ``Mul(x, y)``. The SymPy class for
|
||||
Now we get to our final expression, ``2**x + x*y``. This is the addition of
|
||||
our last two objects, ``Pow(2, x)``, and ``Mul(x, y)``. The SymPy class for
|
||||
addition is ``Add``, so, as you might expect, to create this object, we use
|
||||
``Add(Pow(x, 2), Mul(x, y))``.
|
||||
``Add(Pow(2, x), Mul(x, y))``.
|
||||
|
||||
>>> Add(Pow(x, 2), Mul(x, y))
|
||||
x**2 + x*y
|
||||
>>> Add(Pow(2, x), Mul(x, y))
|
||||
2**x + x*y
|
||||
|
||||
SymPy expression trees can have many branches, and can be quite deep or quite
|
||||
broad. Here is a more complicated example
|
||||
|
||||
@@ -151,7 +151,7 @@ raise it to the ``-1`` power.
|
||||
>>> N**-1
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
NonInvertibleMatrixError: Matrix det == 0; not invertible.
|
||||
ValueError: Matrix det == 0; not invertible.
|
||||
|
||||
To take the transpose of a Matrix, use ``T``.
|
||||
|
||||
@@ -427,7 +427,7 @@ guaranteed to be accurate in some limited domain of numerics and symbols,
|
||||
and any complicated expressions beyond its decidability are treated as ``None``,
|
||||
which behaves similarly to logical ``False``.
|
||||
|
||||
The list of methods using zero testing procedures are as follows:
|
||||
The list of methods using zero testing procedures are as followings.
|
||||
|
||||
``echelon_form`` , ``is_echelon`` , ``rank`` , ``rref`` , ``nullspace`` ,
|
||||
``eigenvects`` , ``inverse_ADJ`` , ``inverse_GE`` , ``inverse_LU`` ,
|
||||
|
||||
@@ -221,7 +221,6 @@ imported from ``sympy.printing.mathml``.
|
||||
``print_mathml()`` prints the output. If you want the string, use the
|
||||
function ``mathml()``.
|
||||
|
||||
|
||||
Dot
|
||||
---
|
||||
|
||||
@@ -230,33 +229,27 @@ format, which can be rendered with Graphviz. See the
|
||||
:ref:`tutorial-manipulation` section for some examples of the output of this
|
||||
printer.
|
||||
|
||||
Here is an example of the raw output of the ``dotprint()`` function
|
||||
|
||||
>>> from sympy.printing.dot import dotprint
|
||||
>>> from sympy.abc import x
|
||||
>>> print(dotprint(x+2))
|
||||
>>> print(dotprint(x+2))
|
||||
digraph{
|
||||
<BLANKLINE>
|
||||
# Graph style
|
||||
"ordering"="out"
|
||||
"rankdir"="TD"
|
||||
<BLANKLINE>
|
||||
#########
|
||||
# Nodes #
|
||||
#########
|
||||
<BLANKLINE>
|
||||
"Add(Integer(2), Symbol('x'))_()" ["color"="black", "label"="Add", "shape"="ellipse"];
|
||||
"Add(Integer(2), Symbol(x))_()" ["color"="black", "label"="Add", "shape"="ellipse"];
|
||||
"Integer(2)_(0,)" ["color"="black", "label"="2", "shape"="ellipse"];
|
||||
"Symbol('x')_(1,)" ["color"="black", "label"="x", "shape"="ellipse"];
|
||||
<BLANKLINE>
|
||||
"Symbol(x)_(1,)" ["color"="black", "label"="x", "shape"="ellipse"];
|
||||
#########
|
||||
# Edges #
|
||||
#########
|
||||
<BLANKLINE>
|
||||
"Add(Integer(2), Symbol('x'))_()" -> "Integer(2)_(0,)";
|
||||
"Add(Integer(2), Symbol('x'))_()" -> "Symbol('x')_(1,)";
|
||||
"Add(Integer(2), Symbol(x))_()" -> "Integer(2)_(0,)";
|
||||
"Add(Integer(2), Symbol(x))_()" -> "Symbol(x)_(1,)";
|
||||
}
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#srepr-fn] SymPy does not use the Python builtin ``repr()`` function for
|
||||
|
||||
@@ -110,7 +110,7 @@ In the ``solveset`` module, the non linear system of equations is solved using
|
||||
>>> nonlinsolve([x**2 + 1, y**2 + 1], [x, y])
|
||||
{(-ⅈ, -ⅈ), (-ⅈ, ⅈ), (ⅈ, -ⅈ), (ⅈ, ⅈ)}
|
||||
|
||||
3. When both real and complex solution are present:
|
||||
3. When both real and complex solution is present:
|
||||
|
||||
>>> from sympy import sqrt
|
||||
>>> system = [x**2 - 2*y**2 -2, x*y - 2]
|
||||
@@ -118,11 +118,17 @@ In the ``solveset`` module, the non linear system of equations is solved using
|
||||
>>> nonlinsolve(system, vars)
|
||||
{(-2, -1), (2, 1), (-√2⋅ⅈ, √2⋅ⅈ), (√2⋅ⅈ, -√2⋅ⅈ)}
|
||||
|
||||
>>> n = Dummy('n')
|
||||
>>> system = [exp(x) - sin(y), 1/y - 3]
|
||||
>>> nonlinsolve(system, vars)
|
||||
{({2⋅n⋅ⅈ⋅π + log(sin(1/3)) | n ∊ ℤ}, 1/3)}
|
||||
>>> real_soln = (log(sin(S(1)/3)), S(1)/3)
|
||||
>>> img_lamda = Lambda(n, 2*n*I*pi + Mod(log(sin(S(1)/3)), 2*I*pi))
|
||||
>>> complex_soln = (ImageSet(img_lamda, S.Integers), S(1)/3)
|
||||
>>> soln = FiniteSet(real_soln, complex_soln)
|
||||
>>> nonlinsolve(system, [x, y]) == soln
|
||||
True
|
||||
|
||||
4. When the system is positive-dimensional system (has infinitely many solutions):
|
||||
4. If non linear system of equations is Positive dimensional system (A system with
|
||||
infinitely many solutions is said to be positive-dimensional):
|
||||
|
||||
>>> nonlinsolve([x*y, x*y - x], [x, y])
|
||||
{(0, y)}
|
||||
@@ -142,14 +148,14 @@ In the ``solveset`` module, the non linear system of equations is solved using
|
||||
``solve`` can be used for such cases:
|
||||
|
||||
>>> solve([x**2 - y**2/exp(x)], [x, y], dict=True)
|
||||
⎡⎧ ⎛-y ⎞⎫ ⎧ ⎛y⎞⎫⎤
|
||||
⎢⎨x: 2⋅W⎜───⎟⎬, ⎨x: 2⋅W⎜─⎟⎬⎥
|
||||
⎣⎩ ⎝ 2 ⎠⎭ ⎩ ⎝2⎠⎭⎦
|
||||
⎡⎧ ⎛y⎞⎫⎤
|
||||
⎢⎨x: 2⋅LambertW⎜─⎟⎬⎥
|
||||
⎣⎩ ⎝2⎠⎭⎦
|
||||
|
||||
3. Currently ``nonlinsolve`` is not properly capable of solving the system of equations
|
||||
having trigonometric functions.
|
||||
|
||||
``solve`` can be used for such cases (but does not give all solution):
|
||||
``solve`` can be used for such cases(not all solution):
|
||||
|
||||
>>> solve([sin(x + y), cos(x - y)], [x, y])
|
||||
⎡⎛-3⋅π 3⋅π⎞ ⎛-π π⎞ ⎛π 3⋅π⎞ ⎛3⋅π π⎞⎤
|
||||
@@ -179,7 +185,7 @@ multiplicity 1 and ``3`` is a root of multiplicity 2.
|
||||
``solve`` can be used for such cases:
|
||||
|
||||
>>> solve(x*exp(x) - 1, x )
|
||||
[W(1)]
|
||||
[LambertW(1)]
|
||||
|
||||
|
||||
.. _tutorial-dsolve:
|
||||
|
||||
@@ -9,7 +9,7 @@ computation of the digits of pi.
|
||||
from mpmath import libmp, pi
|
||||
|
||||
import math
|
||||
from sympy.core.compatibility import clock
|
||||
from time import clock
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ Suggested Usage: python -i pyglet_plotting.py
|
||||
|
||||
|
||||
from sympy import symbols, sin, cos, pi, sqrt
|
||||
from sympy.core.compatibility import range, clock
|
||||
from sympy.core.compatibility import range
|
||||
from sympy.plotting.pygletplot import PygletPlot
|
||||
|
||||
from time import sleep
|
||||
from time import sleep, clock
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -23,7 +23,7 @@ def main():
|
||||
|
||||
show( limit(x*(sqrt(x**2 + 1) - x), x, oo), Rational(1)/2 )
|
||||
|
||||
show( limit(x - sqrt3(x**3 - 1), x, oo), Rational(0) )
|
||||
show( limit(x - sqrt(x**3 - 1), x, oo), Rational(0) )
|
||||
|
||||
show( limit(log(1 + exp(x))/x, x, -oo), Rational(0) )
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ def main():
|
||||
eq = Eq(D(Xi(r, phi, theta), r, 2) + 2/r * D(Xi(r, phi, theta), r) +
|
||||
1/(r**2 * sin(phi)**2) * D(Xi(r, phi, theta), theta, 2) +
|
||||
cos(phi)/(r**2 * sin(phi)) * D(Xi(r, phi, theta), phi) +
|
||||
1/r**2 * D(Xi(r, phi, theta), phi, 2), 0)
|
||||
1/r**2 * D(Xi(r, phi, theta), phi, 2))
|
||||
pprint(eq)
|
||||
|
||||
pprint("We can either separate this equation in regards with variable r:")
|
||||
|
||||
@@ -21,7 +21,7 @@ def sample2d(f, x_args):
|
||||
try:
|
||||
f = sympify(f)
|
||||
except SympifyError:
|
||||
raise ValueError("f could not be interpreted as a SymPy function")
|
||||
raise ValueError("f could not be interpretted as a SymPy function")
|
||||
try:
|
||||
x, x_min, x_max, x_n = x_args
|
||||
except (TypeError, IndexError):
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"There are a number of steps needed to construct matrix $A$. Let us consider an example from https://dl.acm.org/citation.cfm?id=550525 to \n",
|
||||
"There are a number of steps needed to contruct matrix $A$. Let us consider an example from https://dl.acm.org/citation.cfm?id=550525 to \n",
|
||||
"show the construction."
|
||||
]
|
||||
},
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"From the definition, it is clear that the resultant will equal zero if and only if $p$ and $q$ have at least one common root. Thus, the resultant becomes very useful in identifying whether common roots exist. "
|
||||
"From the definition, it is clear that the resultant will equal zero if and only if $p$ and $q$ have at least one common root. Thus, the resultant becomes very usefull in identifying whether common roots exist. "
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1110,6 +1110,8 @@ git_whitelist = {
|
||||
# This is just a distribute version of setup.py. Used mainly for setup.py
|
||||
# develop, which we don't care about in the release tarball
|
||||
'setupegg.py',
|
||||
# Example on how to use tox to test Sympy. For development.
|
||||
'tox.ini.sample',
|
||||
# pytest stuff
|
||||
'conftest.py',
|
||||
# Encrypted deploy key for deploying dev docs to GitHub
|
||||
|
||||
@@ -33,12 +33,8 @@ import sys
|
||||
import os
|
||||
import shutil
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
from distutils.command.sdist import sdist
|
||||
|
||||
|
||||
min_mpmath_version = '0.19'
|
||||
mpmath_version = '0.19'
|
||||
|
||||
# This directory
|
||||
dir_setup = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -62,11 +58,11 @@ except ImportError:
|
||||
from distutils.version import LooseVersion
|
||||
try:
|
||||
import mpmath
|
||||
if mpmath.__version__ < LooseVersion(min_mpmath_version):
|
||||
if mpmath.__version__ < LooseVersion(mpmath_version):
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
print("Please install the mpmath package with a version >= %s"
|
||||
% min_mpmath_version)
|
||||
% mpmath_version)
|
||||
sys.exit(-1)
|
||||
|
||||
PY3 = sys.version_info[0] > 2
|
||||
@@ -126,8 +122,6 @@ modules = [
|
||||
'sympy.parsing.autolev._antlr',
|
||||
'sympy.parsing.autolev.test-examples',
|
||||
'sympy.parsing.autolev.test-examples.pydy-example-repo',
|
||||
'sympy.parsing.c',
|
||||
'sympy.parsing.fortran',
|
||||
'sympy.parsing.latex',
|
||||
'sympy.parsing.latex._antlr',
|
||||
'sympy.physics',
|
||||
@@ -310,37 +304,6 @@ class antlr(Command):
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
class sdist_sympy(sdist):
|
||||
def run(self):
|
||||
# Fetch git commit hash and write down to commit_hash.txt before
|
||||
# shipped in tarball.
|
||||
commit_hash = None
|
||||
commit_hash_filepath = 'doc/commit_hash.txt'
|
||||
try:
|
||||
commit_hash = \
|
||||
subprocess.check_output(['git', 'rev-parse', 'HEAD'])
|
||||
commit_hash = commit_hash.decode('ascii')
|
||||
commit_hash = commit_hash.rstrip()
|
||||
print('Commit hash found : {}.'.format(commit_hash))
|
||||
print('Writing it to {}.'.format(commit_hash_filepath))
|
||||
except:
|
||||
pass
|
||||
|
||||
if commit_hash:
|
||||
with open(commit_hash_filepath, 'w') as f:
|
||||
f.write(commit_hash)
|
||||
|
||||
super(sdist_sympy, self).run()
|
||||
|
||||
try:
|
||||
os.remove(commit_hash_filepath)
|
||||
print(
|
||||
'Successfully removed temporary file {}.'
|
||||
.format(commit_hash_filepath))
|
||||
except OSError as e:
|
||||
print("Error deleting %s - %s." % (e.filename, e.strerror))
|
||||
|
||||
|
||||
# Check that this list is uptodate against the result of the command:
|
||||
# python bin/generate_test_list.py
|
||||
tests = [
|
||||
@@ -446,7 +409,6 @@ if __name__ == '__main__':
|
||||
'clean': clean,
|
||||
'audit': audit,
|
||||
'antlr': antlr,
|
||||
'sdist': sdist_sympy,
|
||||
},
|
||||
classifiers=[
|
||||
'License :: OSI Approved :: BSD License',
|
||||
@@ -458,15 +420,14 @@ if __name__ == '__main__':
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy',
|
||||
],
|
||||
install_requires=[
|
||||
'mpmath>=%s' % min_mpmath_version,
|
||||
'mpmath>=%s' % mpmath_version,
|
||||
],
|
||||
**extra_kwargs
|
||||
)
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ from .algebras import *
|
||||
# from combinatorics import *
|
||||
# This module is slow to import:
|
||||
#from physics import units
|
||||
from .plotting import plot, textplot, plot_backends, plot_implicit, plot_parametric
|
||||
from .plotting import plot, textplot, plot_backends, plot_implicit
|
||||
from .printing import *
|
||||
from .interactive import init_session, init_printing
|
||||
|
||||
|
||||
+46
-38
@@ -27,28 +27,6 @@ you still need to use ``Symbol('foo')`` or ``symbols('foo')``.
|
||||
You can freely mix usage of ``sympy.abc`` and ``Symbol``/``symbols``, though
|
||||
sticking with one and only one way to get the symbols does tend to make the code
|
||||
more readable.
|
||||
|
||||
The module also defines some special names to help detect which names clash
|
||||
with the default SymPy namespace.
|
||||
|
||||
``_clash1`` defines all the single letter variables that clash with
|
||||
SymPy objects; ``_clash2`` defines the multi-letter clashing symbols;
|
||||
and ``_clash`` is the union of both. These can be passed for ``locals``
|
||||
during sympification if one desires Symbols rather than the non-Symbol
|
||||
objects for those names.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import S
|
||||
>>> from sympy.abc import _clash1, _clash2, _clash
|
||||
>>> S("Q & C", locals=_clash1)
|
||||
C & Q
|
||||
>>> S('pi(x)', locals=_clash2)
|
||||
pi(x)
|
||||
>>> S('pi(C, Q)', locals=_clash)
|
||||
pi(C, Q)
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import print_function, division
|
||||
@@ -92,20 +70,50 @@ _greek = list(greeks) # make a copy, so we can mutate it
|
||||
_greek.remove("lambda")
|
||||
_greek.append("lamda")
|
||||
|
||||
ns = {}
|
||||
exec_('from sympy import *', ns)
|
||||
_clash1 = {}
|
||||
_clash2 = {}
|
||||
while ns:
|
||||
_k, _ = ns.popitem()
|
||||
if _k in _greek:
|
||||
_clash2[_k] = Symbol(_k)
|
||||
_greek.remove(_k)
|
||||
elif _k in _latin:
|
||||
_clash1[_k] = Symbol(_k)
|
||||
_latin.remove(_k)
|
||||
_clash = {}
|
||||
_clash.update(_clash1)
|
||||
_clash.update(_clash2)
|
||||
def clashing():
|
||||
"""Return the clashing-symbols dictionaries.
|
||||
|
||||
del _latin, _greek, Symbol, _k
|
||||
``clash1`` defines all the single letter variables that clash with
|
||||
SymPy objects; ``clash2`` defines the multi-letter clashing symbols;
|
||||
and ``clash`` is the union of both. These can be passed for ``locals``
|
||||
during sympification if one desires Symbols rather than the non-Symbol
|
||||
objects for those names.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import S
|
||||
>>> from sympy.abc import _clash1, _clash2, _clash
|
||||
>>> S("Q & C", locals=_clash1)
|
||||
And(C, Q)
|
||||
>>> S('pi(x)', locals=_clash2)
|
||||
pi(x)
|
||||
>>> S('pi(C, Q)', locals=_clash)
|
||||
pi(C, Q)
|
||||
|
||||
Note: if changes are made to the docstring examples they can only
|
||||
be tested after removing "clashing" from the list of deleted items
|
||||
at the bottom of this file which removes this function from the
|
||||
namespace.
|
||||
"""
|
||||
|
||||
ns = {}
|
||||
exec_('from sympy import *', ns)
|
||||
clash1 = {}
|
||||
clash2 = {}
|
||||
while ns:
|
||||
k, _ = ns.popitem()
|
||||
if k in _greek:
|
||||
clash2[k] = Symbol(k)
|
||||
_greek.remove(k)
|
||||
elif k in _latin:
|
||||
clash1[k] = Symbol(k)
|
||||
_latin.remove(k)
|
||||
clash = {}
|
||||
clash.update(clash1)
|
||||
clash.update(clash2)
|
||||
return clash1, clash2, clash
|
||||
|
||||
_clash1, _clash2, _clash = clashing()
|
||||
|
||||
del _latin, _greek, clashing, Symbol
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
# https://en.wikipedia.org/wiki/Quaternion
|
||||
from __future__ import print_function
|
||||
|
||||
from sympy import S, Rational
|
||||
from sympy import Rational
|
||||
from sympy import re, im, conjugate
|
||||
from sympy import sqrt, sin, cos, acos, exp, ln
|
||||
from sympy import trigsimp
|
||||
from sympy import integrate
|
||||
from sympy import Matrix
|
||||
from sympy import Matrix, Add, Mul
|
||||
from sympy import sympify
|
||||
from sympy.core.compatibility import SYMPY_INTS
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.numbers import Integer
|
||||
|
||||
|
||||
class Quaternion(Expr):
|
||||
@@ -108,8 +110,8 @@ class Quaternion(Expr):
|
||||
(x, y, z) = vector
|
||||
norm = sqrt(x**2 + y**2 + z**2)
|
||||
(x, y, z) = (x / norm, y / norm, z / norm)
|
||||
s = sin(angle * S.Half)
|
||||
a = cos(angle * S.Half)
|
||||
s = sin(angle * Rational(1, 2))
|
||||
a = cos(angle * Rational(1, 2))
|
||||
b = x * s
|
||||
c = y * s
|
||||
d = z * s
|
||||
@@ -194,16 +196,6 @@ class Quaternion(Expr):
|
||||
def __neg__(self):
|
||||
return Quaternion(-self._a, -self._b, -self._c, -self.d)
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self * sympify(other)**-1
|
||||
|
||||
__div__ = __truediv__
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
return sympify(other) * self**-1
|
||||
|
||||
__rdiv__ = __rtruediv__
|
||||
|
||||
def _eval_Integral(self, *args):
|
||||
return self.integrate(*args)
|
||||
|
||||
@@ -254,12 +246,14 @@ class Quaternion(Expr):
|
||||
|
||||
# If q2 is a number or a sympy expression instead of a quaternion
|
||||
if not isinstance(q2, Quaternion):
|
||||
if q1.real_field and q2.is_complex:
|
||||
if q1.real_field:
|
||||
if q2.is_complex:
|
||||
return Quaternion(re(q2) + q1.a, im(q2) + q1.b, q1.c, q1.d)
|
||||
elif q2.is_commutative:
|
||||
return Quaternion(q1.a + q2, q1.b, q1.c, q1.d)
|
||||
else:
|
||||
# q2 is something strange, do not evaluate:
|
||||
return Add(q1, q2)
|
||||
else:
|
||||
raise ValueError("Only commutative expressions can be added with a Quaternion.")
|
||||
return Quaternion(q1.a + q2, q1.b, q1.c, q1.d)
|
||||
|
||||
return Quaternion(q1.a + q2.a, q1.b + q2.b, q1.c + q2.c, q1.d
|
||||
+ q2.d)
|
||||
@@ -327,14 +321,14 @@ class Quaternion(Expr):
|
||||
========
|
||||
|
||||
>>> from sympy.algebras.quaternion import Quaternion
|
||||
>>> from sympy import Symbol
|
||||
>>> from sympy import symbols
|
||||
>>> q1 = Quaternion(1, 2, 3, 4)
|
||||
>>> q2 = Quaternion(5, 6, 7, 8)
|
||||
>>> Quaternion._generic_mul(q1, q2)
|
||||
(-60) + 12*i + 30*j + 24*k
|
||||
>>> Quaternion._generic_mul(q1, 2)
|
||||
2 + 4*i + 6*j + 8*k
|
||||
>>> x = Symbol('x', real = True)
|
||||
>>> x = symbols('x', real = True)
|
||||
>>> Quaternion._generic_mul(q1, x)
|
||||
x + 2*x*i + 3*x*j + 4*x*k
|
||||
|
||||
@@ -355,21 +349,24 @@ class Quaternion(Expr):
|
||||
|
||||
# If q1 is a number or a sympy expression instead of a quaternion
|
||||
if not isinstance(q1, Quaternion):
|
||||
if q2.real_field and q1.is_complex:
|
||||
return Quaternion(re(q1), im(q1), 0, 0) * q2
|
||||
elif q1.is_commutative:
|
||||
return Quaternion(q1 * q2.a, q1 * q2.b, q1 * q2.c, q1 * q2.d)
|
||||
if q2.real_field:
|
||||
if q1.is_complex:
|
||||
return q2 * Quaternion(re(q1), im(q1), 0, 0)
|
||||
else:
|
||||
return Mul(q1, q2)
|
||||
else:
|
||||
raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
|
||||
return Quaternion(q1 * q2.a, q1 * q2.b, q1 * q2.c, q1 * q2.d)
|
||||
|
||||
|
||||
# If q2 is a number or a sympy expression instead of a quaternion
|
||||
if not isinstance(q2, Quaternion):
|
||||
if q1.real_field and q2.is_complex:
|
||||
if q1.real_field:
|
||||
if q2.is_complex:
|
||||
return q1 * Quaternion(re(q2), im(q2), 0, 0)
|
||||
elif q2.is_commutative:
|
||||
return Quaternion(q2 * q1.a, q2 * q1.b, q2 * q1.c, q2 * q1.d)
|
||||
else:
|
||||
return Mul(q1, q2)
|
||||
else:
|
||||
raise ValueError("Only commutative expressions can be multiplied with a Quaternion.")
|
||||
return Quaternion(q2 * q1.a, q2 * q1.b, q2 * q1.c, q2 * q1.d)
|
||||
|
||||
return Quaternion(-q1.b*q2.b - q1.c*q2.c - q1.d*q2.d + q1.a*q2.a,
|
||||
q1.b*q2.a + q1.c*q2.d - q1.d*q2.c + q1.a*q2.b,
|
||||
@@ -424,23 +421,22 @@ class Quaternion(Expr):
|
||||
>>> q.pow(4)
|
||||
668 + (-224)*i + (-336)*j + (-448)*k
|
||||
"""
|
||||
p = sympify(p)
|
||||
q = self
|
||||
if p == -1:
|
||||
return q.inverse()
|
||||
res = 1
|
||||
|
||||
if not p.is_Integer:
|
||||
return NotImplemented
|
||||
|
||||
if p < 0:
|
||||
q, p = q.inverse(), -p
|
||||
|
||||
if not (isinstance(p, (Integer, SYMPY_INTS))):
|
||||
return NotImplemented
|
||||
|
||||
while p > 0:
|
||||
if p % 2 == 1:
|
||||
if p & 1:
|
||||
res = q * res
|
||||
|
||||
p = p//2
|
||||
p = p >> 1
|
||||
q = q * q
|
||||
|
||||
return res
|
||||
@@ -601,8 +597,14 @@ class Quaternion(Expr):
|
||||
2*pi/3
|
||||
"""
|
||||
q = self
|
||||
if q.a.is_negative:
|
||||
q = q * -1
|
||||
try:
|
||||
# Skips it if it doesn't know whether q.a is negative
|
||||
if q.a < 0:
|
||||
# avoid error with acos
|
||||
# axis and angle of rotation of q and q*-1 will be the same
|
||||
q = q * -1
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
q = q.normalize()
|
||||
angle = trigsimp(2 * acos(q.a))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from sympy import symbols, re, im, I, Abs, Symbol, \
|
||||
cos, sin, sqrt, conjugate, log, acos, E, pi, \
|
||||
Matrix, diff, integrate, trigsimp, S, Rational
|
||||
from sympy.algebras.quaternion import Quaternion
|
||||
from sympy import symbols, re, im, Add, Mul, I, Abs
|
||||
from sympy import cos, sin, sqrt, conjugate, log, acos, E, pi
|
||||
from sympy.utilities.pytest import raises
|
||||
from sympy import Matrix
|
||||
from sympy import diff, integrate, trigsimp
|
||||
from sympy import S, Rational
|
||||
|
||||
x, y, z, w = symbols("x y z w")
|
||||
|
||||
@@ -10,18 +12,14 @@ def test_quaternion_construction():
|
||||
q = Quaternion(x, y, z, w)
|
||||
assert q + q == Quaternion(2*x, 2*y, 2*z, 2*w)
|
||||
|
||||
q2 = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3),
|
||||
pi*Rational(2, 3))
|
||||
assert q2 == Quaternion(S.Half, S.Half,
|
||||
S.Half, S.Half)
|
||||
q2 = Quaternion.from_axis_angle((sqrt(3)/3, sqrt(3)/3, sqrt(3)/3), 2*pi/3)
|
||||
assert q2 == Quaternion(Rational(1, 2), Rational(1, 2),
|
||||
Rational(1, 2), Rational(1, 2))
|
||||
|
||||
M = Matrix([[cos(x), -sin(x), 0], [sin(x), cos(x), 0], [0, 0, 1]])
|
||||
q3 = trigsimp(Quaternion.from_rotation_matrix(M))
|
||||
assert q3 == Quaternion(sqrt(2)*sqrt(cos(x) + 1)/2, 0, 0, sqrt(-2*cos(x) + 2)/2)
|
||||
|
||||
nc = Symbol('nc', commutative=False)
|
||||
raises(ValueError, lambda: Quaternion(x, y, nc, w))
|
||||
|
||||
|
||||
def test_quaternion_complex_real_addition():
|
||||
a = symbols("a", complex=True)
|
||||
@@ -34,9 +32,9 @@ def test_quaternion_complex_real_addition():
|
||||
assert 1 + q == Quaternion(1 + x, y, z, w)
|
||||
assert I + q == Quaternion(x, 1 + y, z, w)
|
||||
assert b + q == Quaternion(x + b, y, z, w)
|
||||
raises(ValueError, lambda: c + q)
|
||||
raises(ValueError, lambda: q * c)
|
||||
raises(ValueError, lambda: c * q)
|
||||
assert c + q == Add(c, Quaternion(x, y, z, w), evaluate=False)
|
||||
assert q * c == Mul(Quaternion(x, y, z, w), c, evaluate=False)
|
||||
assert c * q == Mul(c, Quaternion(x, y, z, w), evaluate=False)
|
||||
|
||||
assert -q == Quaternion(-x, -y, -z, -w)
|
||||
|
||||
@@ -49,12 +47,6 @@ def test_quaternion_complex_real_addition():
|
||||
Quaternion((2 + 3*I)*(3 + 4*I), (2 + 3*I)*(2 + 5*I), 0, (2 + 3*I)*(7 + 8*I))
|
||||
assert q2 * (2 + 3*I) == Quaternion(-10, 11, 38, -5)
|
||||
|
||||
q1 = Quaternion(1, 2, 3, 4)
|
||||
q0 = Quaternion(0, 0, 0, 0)
|
||||
assert q1 + q0 == q1
|
||||
assert q1 - q0 == q1
|
||||
assert q1 - q1 == q0
|
||||
|
||||
|
||||
def test_quaternion_functions():
|
||||
q = Quaternion(x, y, z, w)
|
||||
@@ -65,14 +57,10 @@ def test_quaternion_functions():
|
||||
assert q.norm() == sqrt(w**2 + x**2 + y**2 + z**2)
|
||||
assert q.normalize() == Quaternion(x, y, z, w) / sqrt(w**2 + x**2 + y**2 + z**2)
|
||||
assert q.inverse() == Quaternion(x, -y, -z, -w) / (w**2 + x**2 + y**2 + z**2)
|
||||
assert q.inverse() == q.pow(-1)
|
||||
raises(ValueError, lambda: q0.inverse())
|
||||
assert q.pow(2) == Quaternion(-w**2 + x**2 - y**2 - z**2, 2*x*y, 2*x*z, 2*w*x)
|
||||
assert q**(2) == Quaternion(-w**2 + x**2 - y**2 - z**2, 2*x*y, 2*x*z, 2*w*x)
|
||||
assert q1.pow(-2) == Quaternion(Rational(-7, 225), Rational(-1, 225), Rational(-1, 150), Rational(-2, 225))
|
||||
assert q1**(-2) == Quaternion(Rational(-7, 225), Rational(-1, 225), Rational(-1, 150), Rational(-2, 225))
|
||||
assert q1.pow(-2) == Quaternion(-S(7)/225, -S(1)/225, -S(1)/150, -S(2)/225)
|
||||
assert q1.pow(-0.5) == NotImplemented
|
||||
raises(TypeError, lambda: q1**(-0.5))
|
||||
|
||||
assert q1.exp() == \
|
||||
Quaternion(E * cos(sqrt(29)),
|
||||
@@ -96,11 +84,7 @@ def test_quaternion_functions():
|
||||
assert integrate(Quaternion(x, x, x, x), x) == \
|
||||
Quaternion(x**2 / 2, x**2 / 2, x**2 / 2, x**2 / 2)
|
||||
|
||||
assert Quaternion.rotate_point((1, 1, 1), q1) == (S.One / 5, 1, S(7) / 5)
|
||||
n = Symbol('n')
|
||||
raises(TypeError, lambda: q1**n)
|
||||
n = Symbol('n', integer=True)
|
||||
raises(TypeError, lambda: q1**n)
|
||||
assert Quaternion.rotate_point((1, 1, 1), q1) == (S(1) / 5, 1, S(7) / 5)
|
||||
|
||||
|
||||
def test_quaternion_conversions():
|
||||
@@ -111,14 +95,14 @@ def test_quaternion_conversions():
|
||||
4 * sqrt(29)/29),
|
||||
2 * acos(sqrt(30)/30))
|
||||
|
||||
assert q1.to_rotation_matrix() == Matrix([[Rational(-2, 3), Rational(2, 15), Rational(11, 15)],
|
||||
[Rational(2, 3), Rational(-1, 3), Rational(2, 3)],
|
||||
[Rational(1, 3), Rational(14, 15), Rational(2, 15)]])
|
||||
assert q1.to_rotation_matrix() == Matrix([[-S(2)/3, S(2)/15, S(11)/15],
|
||||
[S(2)/3, -S(1)/3, S(2)/3],
|
||||
[S(1)/3, S(14)/15, S(2)/15]])
|
||||
|
||||
assert q1.to_rotation_matrix((1, 1, 1)) == Matrix([[Rational(-2, 3), Rational(2, 15), Rational(11, 15), Rational(4, 5)],
|
||||
[Rational(2, 3), Rational(-1, 3), Rational(2, 3), S.Zero],
|
||||
[Rational(1, 3), Rational(14, 15), Rational(2, 15), Rational(-2, 5)],
|
||||
[S.Zero, S.Zero, S.Zero, S.One]])
|
||||
assert q1.to_rotation_matrix((1, 1, 1)) == Matrix([[-S(2)/3, S(2)/15, S(11)/15, S(4)/5],
|
||||
[S(2)/3, -S(1)/3, S(2)/3, S(0)],
|
||||
[S(1)/3, S(14)/15, S(2)/15, -S(2)/5],
|
||||
[S(0), S(0), S(0), S(1)]])
|
||||
|
||||
theta = symbols("theta", real=True)
|
||||
q2 = Quaternion(cos(theta/2), 0, 0, sin(theta/2))
|
||||
@@ -152,22 +136,3 @@ def test_quaternion_rotation_iss1593():
|
||||
[1, 0, 0],
|
||||
[0, cos(x), -sin(x)],
|
||||
[0, sin(x), cos(x)]]))
|
||||
|
||||
|
||||
def test_quaternion_multiplication():
|
||||
q1 = Quaternion(3 + 4*I, 2 + 5*I, 0, 7 + 8*I, real_field = False)
|
||||
q2 = Quaternion(1, 2, 3, 5)
|
||||
q3 = Quaternion(1, 1, 1, y)
|
||||
|
||||
assert Quaternion._generic_mul(4, 1) == 4
|
||||
assert Quaternion._generic_mul(4, q1) == Quaternion(12 + 16*I, 8 + 20*I, 0, 28 + 32*I)
|
||||
assert q2.mul(2) == Quaternion(2, 4, 6, 10)
|
||||
assert q2.mul(q3) == Quaternion(-5*y - 4, 3*y - 2, 9 - 2*y, y + 4)
|
||||
assert q2.mul(q3) == q2*q3
|
||||
|
||||
z = symbols('z', complex=True)
|
||||
z_quat = Quaternion(re(z), im(z), 0, 0)
|
||||
q = Quaternion(*symbols('q:4', real=True))
|
||||
|
||||
assert z * q == z_quat * q
|
||||
assert q * z == q * z_quat
|
||||
|
||||
+45
-79
@@ -8,10 +8,9 @@ from sympy.core.cache import cacheit
|
||||
from sympy.core.decorators import deprecated
|
||||
from sympy.core.relational import Relational
|
||||
from sympy.logic.boolalg import (to_cnf, And, Not, Or, Implies, Equivalent,
|
||||
BooleanFunction, BooleanAtom)
|
||||
BooleanFunction, BooleanAtom)
|
||||
from sympy.logic.inference import satisfiable
|
||||
from sympy.utilities.decorator import memoize_property
|
||||
from sympy.assumptions.cnf import CNF, EncodedCNF, Literal
|
||||
|
||||
|
||||
# Deprecated predicates should be added to this list
|
||||
@@ -529,7 +528,7 @@ class AssumptionKeys(object):
|
||||
False
|
||||
>>> ask(~Q.zero(I))
|
||||
True
|
||||
>>> ask(Q.nonzero(oo))
|
||||
>>> ask(Q.nonzero(oo)) #doctest: +SKIP
|
||||
False
|
||||
|
||||
"""
|
||||
@@ -554,7 +553,6 @@ class AssumptionKeys(object):
|
||||
========
|
||||
|
||||
>>> from sympy import Q, ask, I
|
||||
|
||||
>>> ask(Q.nonpositive(-1))
|
||||
True
|
||||
>>> ask(Q.nonpositive(0))
|
||||
@@ -1213,28 +1211,6 @@ def _extract_facts(expr, symbol, check_reversed_rel=True):
|
||||
return expr.func(*args)
|
||||
|
||||
|
||||
def _extract_all_facts(expr, symbol):
|
||||
facts = set()
|
||||
if isinstance(symbol, Relational):
|
||||
symbols = (symbol, symbol.reversed)
|
||||
else:
|
||||
symbols = (symbol,)
|
||||
for clause in expr.clauses:
|
||||
args = []
|
||||
for literal in clause:
|
||||
if isinstance(literal.lit, AppliedPredicate):
|
||||
if literal.lit.arg in symbols:
|
||||
# Add literal if it has 'symbol' in it
|
||||
args.append(Literal(literal.lit.func, literal.is_Not))
|
||||
else:
|
||||
# If any of the literals doesn't have 'symbol' don't add the whole clause.
|
||||
break
|
||||
else:
|
||||
if args:
|
||||
facts.add(frozenset(args))
|
||||
return CNF(facts)
|
||||
|
||||
|
||||
def ask(proposition, assumptions=True, context=global_assumptions):
|
||||
"""
|
||||
Method for inferring properties about objects.
|
||||
@@ -1263,7 +1239,7 @@ def ask(proposition, assumptions=True, context=global_assumptions):
|
||||
Relations in assumptions are not implemented (yet), so the following
|
||||
will not give a meaningful result.
|
||||
|
||||
>>> ask(Q.positive(x), Q.is_true(x > 0))
|
||||
>>> ask(Q.positive(x), Q.is_true(x > 0)) # doctest: +SKIP
|
||||
|
||||
It is however a work in progress.
|
||||
|
||||
@@ -1281,54 +1257,54 @@ def ask(proposition, assumptions=True, context=global_assumptions):
|
||||
else:
|
||||
key, expr = Q.is_true, sympify(proposition)
|
||||
|
||||
assump = CNF.from_prop(assumptions)
|
||||
assump.extend(context)
|
||||
assumptions = And(assumptions, And(*context))
|
||||
assumptions = to_cnf(assumptions)
|
||||
|
||||
local_facts = _extract_all_facts(assump, expr)
|
||||
local_facts = _extract_facts(assumptions, expr)
|
||||
|
||||
known_facts_cnf = get_all_known_facts()
|
||||
known_facts_cnf = get_known_facts_cnf()
|
||||
known_facts_dict = get_known_facts_dict()
|
||||
|
||||
enc_cnf = EncodedCNF()
|
||||
enc_cnf.from_cnf(CNF(known_facts_cnf))
|
||||
enc_cnf.add_from_cnf(local_facts)
|
||||
|
||||
if local_facts.clauses and satisfiable(enc_cnf) is False:
|
||||
if local_facts and satisfiable(And(local_facts, known_facts_cnf)) is False:
|
||||
raise ValueError("inconsistent assumptions %s" % assumptions)
|
||||
|
||||
if local_facts.clauses:
|
||||
local_facts_ = CNF.CNF_to_cnf(local_facts)
|
||||
|
||||
# See if there's a straight-forward conclusion we can make for the inference
|
||||
if local_facts_.is_Atom:
|
||||
if key in known_facts_dict[local_facts_]:
|
||||
return True
|
||||
if Not(key) in known_facts_dict[local_facts_]:
|
||||
return False
|
||||
elif (isinstance(local_facts_, And) and
|
||||
all(k in known_facts_dict for k in local_facts_.args)):
|
||||
for assum in local_facts_.args:
|
||||
if assum.is_Atom:
|
||||
if key in known_facts_dict[assum]:
|
||||
return True
|
||||
if Not(key) in known_facts_dict[assum]:
|
||||
return False
|
||||
elif isinstance(assum, Not) and assum.args[0].is_Atom:
|
||||
if key in known_facts_dict[assum]:
|
||||
return False
|
||||
if Not(key) in known_facts_dict[assum]:
|
||||
return True
|
||||
elif (isinstance(key, Predicate) and
|
||||
isinstance(local_facts_, Not) and local_facts_.args[0].is_Atom):
|
||||
if local_facts_.args[0] in known_facts_dict[key]:
|
||||
return False
|
||||
|
||||
# direct resolution method, no logic
|
||||
res = key(expr)._eval_ask(assumptions)
|
||||
if res is not None:
|
||||
return bool(res)
|
||||
# using satask (still costly)
|
||||
res = satask(proposition, assumptions=assumptions, context=context)
|
||||
|
||||
if local_facts is None:
|
||||
return satask(proposition, assumptions=assumptions, context=context)
|
||||
|
||||
|
||||
# See if there's a straight-forward conclusion we can make for the inference
|
||||
if local_facts.is_Atom:
|
||||
if key in known_facts_dict[local_facts]:
|
||||
return True
|
||||
if Not(key) in known_facts_dict[local_facts]:
|
||||
return False
|
||||
elif (isinstance(local_facts, And) and
|
||||
all(k in known_facts_dict for k in local_facts.args)):
|
||||
for assum in local_facts.args:
|
||||
if assum.is_Atom:
|
||||
if key in known_facts_dict[assum]:
|
||||
return True
|
||||
if Not(key) in known_facts_dict[assum]:
|
||||
return False
|
||||
elif isinstance(assum, Not) and assum.args[0].is_Atom:
|
||||
if key in known_facts_dict[assum]:
|
||||
return False
|
||||
if Not(key) in known_facts_dict[assum]:
|
||||
return True
|
||||
elif (isinstance(key, Predicate) and
|
||||
isinstance(local_facts, Not) and local_facts.args[0].is_Atom):
|
||||
if local_facts.args[0] in known_facts_dict[key]:
|
||||
return False
|
||||
|
||||
# Failing all else, we do a full logical inference
|
||||
res = ask_full_inference(key, local_facts, known_facts_cnf)
|
||||
if res is None:
|
||||
return satask(proposition, assumptions=assumptions, context=context)
|
||||
return res
|
||||
|
||||
|
||||
@@ -1411,16 +1387,8 @@ def compute_known_facts(known_facts, known_facts_keys):
|
||||
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.assumptions.cnf import Literal
|
||||
from sympy.assumptions.ask import Q
|
||||
|
||||
# -{ Known facts as a set }-
|
||||
@cacheit
|
||||
def get_all_known_facts():
|
||||
return {
|
||||
%s
|
||||
}
|
||||
|
||||
# -{ Known facts in Conjunctive Normal Form }-
|
||||
@cacheit
|
||||
def get_known_facts_cnf():
|
||||
@@ -1439,10 +1407,7 @@ def compute_known_facts(known_facts, known_facts_keys):
|
||||
LINE = ",\n "
|
||||
HANG = ' '*8
|
||||
cnf = to_cnf(known_facts)
|
||||
cnf_ = CNF.to_CNF(known_facts)
|
||||
c = LINE.join([str(a) for a in cnf.args])
|
||||
|
||||
p = LINE.join(sorted(['frozenset((' + ', '.join(str(lit) for lit in sorted(clause, key=str)) +'))' for clause in cnf_.clauses]))
|
||||
mapping = single_fact_lookup(known_facts_keys, cnf)
|
||||
items = sorted(mapping.items(), key=str)
|
||||
keys = [str(i[0]) for i in items]
|
||||
@@ -1452,7 +1417,7 @@ def compute_known_facts(known_facts, known_facts_keys):
|
||||
subsequent_indent=HANG,
|
||||
break_long_words=False))
|
||||
for k, v in zip(keys, values)]) + ','
|
||||
return fact_string % (p, c, m)
|
||||
return fact_string % (c, m)
|
||||
|
||||
# handlers tells us what ask handler we should use
|
||||
# for a particular key
|
||||
@@ -1516,9 +1481,10 @@ def get_known_facts():
|
||||
Equivalent(Q.extended_real, Q.real | Q.infinite),
|
||||
Equivalent(Q.even | Q.odd, Q.integer),
|
||||
Implies(Q.even, ~Q.odd),
|
||||
Implies(Q.prime, Q.integer & Q.positive & ~Q.composite),
|
||||
Equivalent(Q.prime, Q.integer & Q.positive & ~Q.composite),
|
||||
Implies(Q.integer, Q.rational),
|
||||
Implies(Q.rational, Q.algebraic),
|
||||
Implies(Q.irrational, Q.finite),
|
||||
Implies(Q.algebraic, Q.complex),
|
||||
Implies(Q.algebraic, Q.finite),
|
||||
Equivalent(Q.transcendental | Q.algebraic, Q.complex & Q.finite),
|
||||
@@ -1564,4 +1530,4 @@ def get_known_facts():
|
||||
)
|
||||
|
||||
from sympy.assumptions.ask_generated import (
|
||||
get_known_facts_dict, get_known_facts_cnf, get_all_known_facts)
|
||||
get_known_facts_dict, get_known_facts_cnf)
|
||||
|
||||
@@ -8,87 +8,8 @@ Instead, run ./bin/ask_update.py.
|
||||
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.assumptions.cnf import Literal
|
||||
from sympy.assumptions.ask import Q
|
||||
|
||||
# -{ Known facts as a set }-
|
||||
@cacheit
|
||||
def get_all_known_facts():
|
||||
return {
|
||||
frozenset((Literal(Q.algebraic, False), Literal(Q.complex, True), Literal(Q.finite, True), Literal(Q.transcendental, False))),
|
||||
frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
|
||||
frozenset((Literal(Q.algebraic, True), Literal(Q.complex, False))),
|
||||
frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
|
||||
frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
|
||||
frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
|
||||
frozenset((Literal(Q.antihermitian, True), Literal(Q.hermitian, True))),
|
||||
frozenset((Literal(Q.complex, False), Literal(Q.imaginary, True))),
|
||||
frozenset((Literal(Q.complex, False), Literal(Q.real, True))),
|
||||
frozenset((Literal(Q.complex, False), Literal(Q.transcendental, True))),
|
||||
frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
|
||||
frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
|
||||
frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
|
||||
frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
|
||||
frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
|
||||
frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
|
||||
frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
|
||||
frozenset((Literal(Q.even, False), Literal(Q.integer, True), Literal(Q.odd, False))),
|
||||
frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.even, True), Literal(Q.integer, False))),
|
||||
frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
|
||||
frozenset((Literal(Q.extended_real, False), Literal(Q.infinite, True))),
|
||||
frozenset((Literal(Q.extended_real, False), Literal(Q.real, True))),
|
||||
frozenset((Literal(Q.extended_real, True), Literal(Q.infinite, False), Literal(Q.real, False))),
|
||||
frozenset((Literal(Q.finite, False), Literal(Q.irrational, True))),
|
||||
frozenset((Literal(Q.finite, False), Literal(Q.rational, True))),
|
||||
frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
|
||||
frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
|
||||
frozenset((Literal(Q.finite, True), Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.real, True))),
|
||||
frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
|
||||
frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
|
||||
frozenset((Literal(Q.hermitian, False), Literal(Q.real, True))),
|
||||
frozenset((Literal(Q.imaginary, True), Literal(Q.real, True))),
|
||||
frozenset((Literal(Q.integer, False), Literal(Q.odd, True))),
|
||||
frozenset((Literal(Q.integer, False), Literal(Q.prime, True))),
|
||||
frozenset((Literal(Q.integer, True), Literal(Q.rational, False))),
|
||||
frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
|
||||
frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
|
||||
frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
|
||||
frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
|
||||
frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
|
||||
frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
|
||||
frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
|
||||
frozenset((Literal(Q.irrational, True), Literal(Q.real, False))),
|
||||
frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
|
||||
frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
|
||||
frozenset((Literal(Q.negative, False), Literal(Q.nonpositive, True), Literal(Q.zero, False))),
|
||||
frozenset((Literal(Q.negative, False), Literal(Q.nonzero, True), Literal(Q.positive, False))),
|
||||
frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.real, True), Literal(Q.zero, False))),
|
||||
frozenset((Literal(Q.negative, True), Literal(Q.nonpositive, False))),
|
||||
frozenset((Literal(Q.negative, True), Literal(Q.nonzero, False))),
|
||||
frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
|
||||
frozenset((Literal(Q.negative, True), Literal(Q.real, False))),
|
||||
frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.nonnegative, False), Literal(Q.positive, True))),
|
||||
frozenset((Literal(Q.nonnegative, False), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.nonnegative, True), Literal(Q.positive, False), Literal(Q.zero, False))),
|
||||
frozenset((Literal(Q.nonpositive, False), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.nonzero, False), Literal(Q.positive, True))),
|
||||
frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
|
||||
frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
|
||||
frozenset((Literal(Q.orthogonal, False), Literal(Q.real, True), Literal(Q.unitary, True))),
|
||||
frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
|
||||
frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
|
||||
frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
|
||||
frozenset((Literal(Q.positive, True), Literal(Q.real, False))),
|
||||
frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.rational, True), Literal(Q.real, False))),
|
||||
frozenset((Literal(Q.real, False), Literal(Q.zero, True))),
|
||||
frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
|
||||
frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
|
||||
frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
|
||||
}
|
||||
|
||||
# -{ Known facts in Conjunctive Normal Form }-
|
||||
@cacheit
|
||||
def get_known_facts_cnf():
|
||||
@@ -164,6 +85,7 @@ def get_known_facts_cnf():
|
||||
Q.orthogonal | ~Q.real | ~Q.unitary,
|
||||
Q.negative | Q.positive | Q.zero | ~Q.real,
|
||||
Q.algebraic | Q.transcendental | ~Q.complex | ~Q.finite,
|
||||
Q.composite | Q.prime | ~Q.integer | ~Q.positive,
|
||||
Q.irrational | Q.rational | ~Q.finite | ~Q.real
|
||||
)
|
||||
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
"""
|
||||
The classes used here are for the internal use of assumptions system
|
||||
only and should not be used anywhere else as these don't possess the
|
||||
signatures common to SymPy objects. For general use of logic constructs
|
||||
please refer to sympy.logic classes And, Or, Not, etc.
|
||||
"""
|
||||
from itertools import combinations, product
|
||||
from sympy import S, Nor, Nand, Xor, Implies, Equivalent, ITE
|
||||
from sympy.logic.boolalg import Or, And, Not, Xnor
|
||||
from sympy.core.compatibility import zip_longest
|
||||
|
||||
|
||||
class Literal(object):
|
||||
"""
|
||||
The smallest element of a CNF object
|
||||
"""
|
||||
|
||||
def __new__(cls, lit, is_Not=False):
|
||||
if isinstance(lit, Not):
|
||||
lit = lit.args[0]
|
||||
is_Not = True
|
||||
elif isinstance(lit, (AND, OR, Literal)):
|
||||
return ~lit if is_Not else lit
|
||||
obj = super(Literal, cls).__new__(cls)
|
||||
obj.lit = lit
|
||||
obj.is_Not = is_Not
|
||||
return obj
|
||||
|
||||
@property
|
||||
def arg(self):
|
||||
return self.lit
|
||||
|
||||
def rcall(self, expr):
|
||||
if callable(self.lit):
|
||||
lit = self.lit(expr)
|
||||
else:
|
||||
try:
|
||||
lit = self.lit.apply(expr)
|
||||
except AttributeError:
|
||||
lit = self.lit.rcall(expr)
|
||||
return type(self)(lit, self.is_Not)
|
||||
|
||||
def __invert__(self):
|
||||
is_Not = not self.is_Not
|
||||
return Literal(self.lit, is_Not)
|
||||
|
||||
def __str__(self):
|
||||
return '%s(%s, %s)' % (type(self).__name__, self.lit, self.is_Not)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.arg == other.arg and self.is_Not == other.is_Not
|
||||
|
||||
def __hash__(self):
|
||||
h = hash((type(self).__name__, self.arg, self.is_Not))
|
||||
return h
|
||||
|
||||
|
||||
class OR(object):
|
||||
"""
|
||||
A low-level implementation for Or
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
self._args = args
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return sorted(self._args, key=str)
|
||||
|
||||
def rcall(self, expr):
|
||||
return type(self)(*[arg.rcall(expr)
|
||||
for arg in self._args
|
||||
])
|
||||
|
||||
def __invert__(self):
|
||||
return AND(*[~arg for arg in self._args])
|
||||
|
||||
def __hash__(self):
|
||||
return hash((type(self).__name__,) + tuple(self.args))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.args == other.args
|
||||
|
||||
def __str__(self):
|
||||
s = '(' + ' | '.join([str(arg) for arg in self.args]) + ')'
|
||||
return s
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class AND(object):
|
||||
"""
|
||||
A low-level implementation for And
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
self._args = args
|
||||
|
||||
def __invert__(self):
|
||||
return OR(*[~arg for arg in self._args])
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return sorted(self._args, key=str)
|
||||
|
||||
def rcall(self, expr):
|
||||
return type(self)(*[arg.rcall(expr)
|
||||
for arg in self._args
|
||||
])
|
||||
|
||||
def __hash__(self):
|
||||
return hash((type(self).__name__,) + tuple(self.args))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.args == other.args
|
||||
|
||||
def __str__(self):
|
||||
s = '('+' & '.join([str(arg) for arg in self.args])+')'
|
||||
return s
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
def to_NNF(expr):
|
||||
"""
|
||||
Generates the Negation Normal Form of any boolean expression in terms
|
||||
of AND, OR, and Literal objects.
|
||||
"""
|
||||
|
||||
if isinstance(expr, Not):
|
||||
arg = expr.args[0]
|
||||
tmp = to_NNF(arg) # Strategy: negate the NNF of expr
|
||||
return ~tmp
|
||||
|
||||
if isinstance(expr, Or):
|
||||
return OR(*[to_NNF(x) for x in Or.make_args(expr)])
|
||||
|
||||
if isinstance(expr, And):
|
||||
return AND(*[to_NNF(x) for x in And.make_args(expr)])
|
||||
|
||||
if isinstance(expr, Nand):
|
||||
tmp = AND(*[to_NNF(x) for x in expr.args])
|
||||
return ~tmp
|
||||
|
||||
if isinstance(expr, Nor):
|
||||
tmp = OR(*[to_NNF(x) for x in expr.args])
|
||||
return ~tmp
|
||||
|
||||
if isinstance(expr, Xor):
|
||||
cnfs = []
|
||||
for i in range(0, len(expr.args) + 1, 2):
|
||||
for neg in combinations(expr.args, i):
|
||||
clause = [~to_NNF(s) if s in neg else to_NNF(s)
|
||||
for s in expr.args]
|
||||
cnfs.append(OR(*clause))
|
||||
return AND(*cnfs)
|
||||
|
||||
if isinstance(expr, Xnor):
|
||||
cnfs = []
|
||||
for i in range(0, len(expr.args) + 1, 2):
|
||||
for neg in combinations(expr.args, i):
|
||||
clause = [~to_NNF(s) if s in neg else to_NNF(s)
|
||||
for s in expr.args]
|
||||
cnfs.append(OR(*clause))
|
||||
return ~AND(*cnfs)
|
||||
|
||||
if isinstance(expr, Implies):
|
||||
L, R = to_NNF(expr.args[0]), to_NNF(expr.args[1])
|
||||
return OR(~L, R)
|
||||
|
||||
if isinstance(expr, Equivalent):
|
||||
cnfs = []
|
||||
for a, b in zip_longest(expr.args, expr.args[1:], fillvalue=expr.args[0]):
|
||||
a = to_NNF(a)
|
||||
b = to_NNF(b)
|
||||
cnfs.append(OR(~a, b))
|
||||
return AND(*cnfs)
|
||||
|
||||
if isinstance(expr, ITE):
|
||||
L = to_NNF(expr.args[0])
|
||||
M = to_NNF(expr.args[1])
|
||||
R = to_NNF(expr.args[2])
|
||||
return AND(OR(~L, M), OR(L, R))
|
||||
|
||||
else:
|
||||
return Literal(expr)
|
||||
|
||||
|
||||
def distribute_AND_over_OR(expr):
|
||||
"""
|
||||
Distributes AND over OR in the NNF expression.
|
||||
Returns the result( Conjunctive Normal Form of expression)
|
||||
as a CNF object.
|
||||
"""
|
||||
if not isinstance(expr, (AND, OR)):
|
||||
tmp = set()
|
||||
tmp.add(frozenset((expr,)))
|
||||
return CNF(tmp)
|
||||
|
||||
if isinstance(expr, OR):
|
||||
return CNF.all_or(*[distribute_AND_over_OR(arg)
|
||||
for arg in expr._args])
|
||||
|
||||
if isinstance(expr, AND):
|
||||
return CNF.all_and(*[distribute_AND_over_OR(arg)
|
||||
for arg in expr._args])
|
||||
|
||||
|
||||
class CNF(object):
|
||||
"""
|
||||
Class to represent CNF of a Boolean expression.
|
||||
Consists of set of clauses, which themselves are stored as
|
||||
frozenset of Literal objects.
|
||||
"""
|
||||
def __init__(self, clauses=None):
|
||||
if not clauses:
|
||||
clauses = set()
|
||||
self.clauses = clauses
|
||||
|
||||
def add(self, prop):
|
||||
clauses = CNF.to_CNF(prop).clauses
|
||||
self.add_clauses(clauses)
|
||||
|
||||
def __str__(self):
|
||||
s = ' & '.join(
|
||||
['(' + ' | '.join([str(lit) for lit in clause]) +')'
|
||||
for clause in self.clauses]
|
||||
)
|
||||
return s
|
||||
|
||||
def extend(self, props):
|
||||
for p in props:
|
||||
self.add(p)
|
||||
return self
|
||||
|
||||
def copy(self):
|
||||
return CNF(set(self.clauses))
|
||||
|
||||
def add_clauses(self, clauses):
|
||||
self.clauses |= clauses
|
||||
|
||||
@classmethod
|
||||
def from_prop(cls, prop):
|
||||
res = cls()
|
||||
res.add(prop)
|
||||
return res
|
||||
|
||||
def __iand__(self, other):
|
||||
self.add_clauses(other.clauses)
|
||||
return self
|
||||
|
||||
def all_predicates(self):
|
||||
predicates = set()
|
||||
for c in self.clauses:
|
||||
predicates |= {arg.lit for arg in c}
|
||||
return predicates
|
||||
|
||||
def _or(self, cnf):
|
||||
clauses = set()
|
||||
for a, b in product(self.clauses, cnf.clauses):
|
||||
tmp = set(a)
|
||||
for t in b:
|
||||
tmp.add(t)
|
||||
clauses.add(frozenset(tmp))
|
||||
return CNF(clauses)
|
||||
|
||||
def _and(self, cnf):
|
||||
clauses = self.clauses.union(cnf.clauses)
|
||||
return CNF(clauses)
|
||||
|
||||
def _not(self):
|
||||
clss = list(self.clauses)
|
||||
ll = set()
|
||||
for x in clss[-1]:
|
||||
ll.add(frozenset((~x,)))
|
||||
ll = CNF(ll)
|
||||
|
||||
for rest in clss[:-1]:
|
||||
p = set()
|
||||
for x in rest:
|
||||
p.add(frozenset((~x,)))
|
||||
ll = ll._or(CNF(p))
|
||||
return ll
|
||||
|
||||
def rcall(self, expr):
|
||||
clause_list = list()
|
||||
for clause in self.clauses:
|
||||
lits = [arg.rcall(expr) for arg in clause]
|
||||
clause_list.append(OR(*lits))
|
||||
expr = AND(*clause_list)
|
||||
return distribute_AND_over_OR(expr)
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def all_or(cls, *cnfs):
|
||||
b = cnfs[0].copy()
|
||||
for rest in cnfs[1:]:
|
||||
b = b._or(rest)
|
||||
return b
|
||||
|
||||
@classmethod
|
||||
def all_and(cls, *cnfs):
|
||||
b = cnfs[0].copy()
|
||||
for rest in cnfs[1:]:
|
||||
b = b._and(rest)
|
||||
return b
|
||||
|
||||
@classmethod
|
||||
def to_CNF(cls, expr):
|
||||
expr = to_NNF(expr)
|
||||
expr = distribute_AND_over_OR(expr)
|
||||
return expr
|
||||
|
||||
@classmethod
|
||||
def CNF_to_cnf(cls, cnf):
|
||||
"""
|
||||
Converts CNF object to SymPy's boolean expression
|
||||
retaining the form of expression.
|
||||
"""
|
||||
def remove_literal(arg):
|
||||
return Not(arg.lit) if arg.is_Not else arg.lit
|
||||
|
||||
return And(*(Or(*(remove_literal(arg) for arg in clause)) for clause in cnf.clauses))
|
||||
|
||||
|
||||
class EncodedCNF(object):
|
||||
"""
|
||||
Class for encoding the CNF expression.
|
||||
"""
|
||||
def __init__(self, data=None, encoding=None):
|
||||
if not data and not encoding:
|
||||
data = list()
|
||||
encoding = dict()
|
||||
self.data = data
|
||||
self.encoding = encoding
|
||||
self._symbols = list(encoding.keys())
|
||||
|
||||
def from_cnf(self, cnf):
|
||||
self._symbols = list(cnf.all_predicates())
|
||||
n = len(self._symbols)
|
||||
self.encoding = dict(list(zip(self._symbols, list(range(1, n + 1)))))
|
||||
self.data = [self.encode(clause) for clause in cnf.clauses]
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return self._symbols
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
return range(1, len(self._symbols) + 1)
|
||||
|
||||
def copy(self):
|
||||
new_data = [set(clause) for clause in self.data]
|
||||
return EncodedCNF(new_data, dict(self.encoding))
|
||||
|
||||
def add_prop(self, prop):
|
||||
cnf = CNF.from_prop(prop)
|
||||
self.add_from_cnf(cnf)
|
||||
|
||||
def add_from_cnf(self, cnf):
|
||||
clauses = [self.encode(clause) for clause in cnf.clauses]
|
||||
self.data += clauses
|
||||
|
||||
def encode_arg(self, arg):
|
||||
literal = arg.lit
|
||||
value = self.encoding.get(literal, None)
|
||||
if value is None:
|
||||
n = len(self._symbols)
|
||||
self._symbols.append(literal)
|
||||
value = self.encoding[literal] = n + 1
|
||||
if arg.is_Not:
|
||||
return -value
|
||||
else:
|
||||
return value
|
||||
|
||||
def encode(self, clause):
|
||||
return {self.encode_arg(arg) if not arg.lit == S.false else 0 for arg in clause}
|
||||
@@ -6,9 +6,8 @@ from __future__ import print_function, division
|
||||
from sympy.assumptions import Q, ask
|
||||
from sympy.assumptions.handlers import CommonHandler, test_closed_group
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.logic import fuzzy_bool
|
||||
from sympy.functions.elementary.exponential import exp, log
|
||||
from sympy import I, Eq, conjugate, MatrixBase
|
||||
from sympy import I
|
||||
|
||||
|
||||
class AskIntegerHandler(CommonHandler):
|
||||
@@ -337,12 +336,6 @@ class AskHermitianHandler(AskRealHandler):
|
||||
Test that an expression belongs to the field of Hermitian operators
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Expr(expr, assumptions):
|
||||
if isinstance(expr, MatrixBase):
|
||||
return None
|
||||
return AskRealHandler.Expr(expr, assumptions)
|
||||
|
||||
@staticmethod
|
||||
def Add(expr, assumptions):
|
||||
"""
|
||||
@@ -395,18 +388,6 @@ class AskHermitianHandler(AskRealHandler):
|
||||
|
||||
cos, exp = [sin]*2
|
||||
|
||||
@staticmethod
|
||||
def MatrixBase(mat, assumptions):
|
||||
rows, cols = mat.shape
|
||||
ret_val = True
|
||||
for i in range(rows):
|
||||
for j in range(i, cols):
|
||||
cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i])))
|
||||
if cond == None:
|
||||
ret_val = None
|
||||
if cond == False:
|
||||
return False
|
||||
return ret_val
|
||||
|
||||
class AskComplexHandler(CommonHandler):
|
||||
"""
|
||||
|
||||
@@ -240,57 +240,6 @@ def refine_Relational(expr, assumptions):
|
||||
return ask(Q.is_true(expr), assumptions)
|
||||
|
||||
|
||||
def refine_re(expr, assumptions):
|
||||
"""
|
||||
Handler for real part.
|
||||
|
||||
>>> from sympy.assumptions.refine import refine_re
|
||||
>>> from sympy import Q, re
|
||||
>>> from sympy.abc import x
|
||||
>>> refine_re(re(x), Q.real(x))
|
||||
x
|
||||
>>> refine_re(re(x), Q.imaginary(x))
|
||||
0
|
||||
"""
|
||||
arg = expr.args[0]
|
||||
if ask(Q.real(arg), assumptions):
|
||||
return arg
|
||||
if ask(Q.imaginary(arg), assumptions):
|
||||
return 0
|
||||
return _refine_reim(expr, assumptions)
|
||||
|
||||
|
||||
def refine_im(expr, assumptions):
|
||||
"""
|
||||
Handler for imaginary part.
|
||||
|
||||
>>> from sympy.assumptions.refine import refine_im
|
||||
>>> from sympy import Q, im
|
||||
>>> from sympy.abc import x
|
||||
>>> refine_im(im(x), Q.real(x))
|
||||
0
|
||||
>>> refine_im(im(x), Q.imaginary(x))
|
||||
-I*x
|
||||
"""
|
||||
arg = expr.args[0]
|
||||
if ask(Q.real(arg), assumptions):
|
||||
return 0
|
||||
if ask(Q.imaginary(arg), assumptions):
|
||||
return - S.ImaginaryUnit * arg
|
||||
return _refine_reim(expr, assumptions)
|
||||
|
||||
|
||||
def _refine_reim(expr, assumptions):
|
||||
# Helper function for refine_re & refine_im
|
||||
expanded = expr.expand(complex = True)
|
||||
if expanded != expr:
|
||||
refined = refine(expanded, assumptions)
|
||||
if refined != expanded:
|
||||
return refined
|
||||
# Best to leave the expression as is
|
||||
return None
|
||||
|
||||
|
||||
handlers_dict = {
|
||||
'Abs': refine_abs,
|
||||
'Pow': refine_Pow,
|
||||
@@ -300,7 +249,5 @@ handlers_dict = {
|
||||
'GreaterThan': refine_Relational,
|
||||
'LessThan': refine_Relational,
|
||||
'StrictGreaterThan': refine_Relational,
|
||||
'StrictLessThan': refine_Relational,
|
||||
're': refine_re,
|
||||
'im': refine_im
|
||||
'StrictLessThan': refine_Relational
|
||||
}
|
||||
|
||||
+33
-105
@@ -1,39 +1,22 @@
|
||||
from __future__ import print_function, division
|
||||
|
||||
from sympy import Symbol, S
|
||||
from sympy.assumptions.ask_generated import get_all_known_facts
|
||||
from sympy.assumptions.ask_generated import get_known_facts_cnf
|
||||
from sympy.assumptions.assume import global_assumptions, AppliedPredicate
|
||||
from sympy.assumptions.sathandlers import fact_registry
|
||||
from sympy.core import oo
|
||||
from sympy.core import oo, Tuple
|
||||
from sympy.logic.inference import satisfiable
|
||||
from sympy.assumptions.cnf import CNF, EncodedCNF
|
||||
from sympy.logic.boolalg import And
|
||||
|
||||
|
||||
def satask(proposition, assumptions=True, context=global_assumptions,
|
||||
use_known_facts=True, iterations=oo):
|
||||
props = CNF.from_prop(proposition)
|
||||
_props = CNF.from_prop(~proposition)
|
||||
if context:
|
||||
tmp = CNF()
|
||||
context = tmp.extend(context)
|
||||
assumptions = CNF.from_prop(assumptions)
|
||||
|
||||
sat = get_all_relevant_facts(props, assumptions, context,
|
||||
use_known_facts=True, iterations=oo):
|
||||
relevant_facts = get_all_relevant_facts(proposition, assumptions, context,
|
||||
use_known_facts=use_known_facts, iterations=iterations)
|
||||
if context:
|
||||
sat.add_from_cnf(context)
|
||||
sat.add_from_cnf(assumptions)
|
||||
|
||||
return check_satisfiability(props, _props, sat)
|
||||
|
||||
|
||||
def check_satisfiability(prop, _prop, factbase):
|
||||
sat_true = factbase.copy()
|
||||
sat_false = factbase.copy()
|
||||
sat_true.add_from_cnf(prop)
|
||||
sat_false.add_from_cnf(_prop)
|
||||
can_be_true = satisfiable(sat_true)
|
||||
can_be_false = satisfiable(sat_false)
|
||||
can_be_true = satisfiable(And(proposition, assumptions,
|
||||
relevant_facts, *context))
|
||||
can_be_false = satisfiable(And(~proposition, assumptions,
|
||||
relevant_facts, *context))
|
||||
|
||||
if can_be_true and can_be_false:
|
||||
return None
|
||||
@@ -51,61 +34,35 @@ def check_satisfiability(prop, _prop, factbase):
|
||||
raise ValueError("Inconsistent assumptions")
|
||||
|
||||
|
||||
def get_relevant_facts(proposition, assumptions=None,
|
||||
context=None, exprs=None,
|
||||
def get_relevant_facts(proposition, assumptions=(True,),
|
||||
context=global_assumptions, use_known_facts=True, exprs=None,
|
||||
relevant_facts=None):
|
||||
|
||||
newexprs = set()
|
||||
if not exprs:
|
||||
keys = proposition.atoms(AppliedPredicate)
|
||||
# XXX: We need this since True/False are not Basic
|
||||
keys |= Tuple(*assumptions).atoms(AppliedPredicate)
|
||||
if context:
|
||||
keys |= And(*context).atoms(AppliedPredicate)
|
||||
|
||||
if not assumptions:
|
||||
assumptions = CNF({S.true})
|
||||
exprs = {key.args[0] for key in keys}
|
||||
|
||||
if not relevant_facts:
|
||||
relevant_facts = set()
|
||||
relevant_facts = set([])
|
||||
|
||||
def find_symbols(pred):
|
||||
if isinstance(pred, CNF):
|
||||
symbols = set()
|
||||
for a in pred.all_predicates():
|
||||
symbols |= find_symbols(a)
|
||||
return symbols
|
||||
if isinstance(pred.args, AppliedPredicate):
|
||||
return {pred.args[0]}
|
||||
return pred.atoms(Symbol)
|
||||
|
||||
if not exprs:
|
||||
req_keys = find_symbols(proposition)
|
||||
keys = proposition.all_predicates()
|
||||
# XXX: We need this since True/False are not Basic
|
||||
lkeys = set()
|
||||
lkeys |= assumptions.all_predicates()
|
||||
if context:
|
||||
lkeys |= context.all_predicates()
|
||||
|
||||
lkeys = lkeys - {S.true, S.false}
|
||||
tmp_keys = None
|
||||
while tmp_keys != set():
|
||||
tmp = set()
|
||||
for l in lkeys:
|
||||
syms = find_symbols(l)
|
||||
if (syms & req_keys) != set():
|
||||
tmp |= syms
|
||||
tmp_keys = tmp - req_keys
|
||||
req_keys |= tmp_keys
|
||||
keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}
|
||||
|
||||
exprs = {key.args[0] if isinstance(key, AppliedPredicate) else key for key in keys}
|
||||
return exprs, relevant_facts
|
||||
if use_known_facts:
|
||||
for expr in exprs:
|
||||
relevant_facts.add(get_known_facts_cnf().rcall(expr))
|
||||
|
||||
for expr in exprs:
|
||||
for fact in fact_registry[expr.func]:
|
||||
cnf_fact = CNF.to_CNF(fact)
|
||||
newfact = cnf_fact.rcall(expr)
|
||||
relevant_facts = relevant_facts._and(newfact)
|
||||
newexprs |= set([key.args[0] for key in newfact.all_predicates()
|
||||
if isinstance(key, AppliedPredicate)])
|
||||
newfact = fact.rcall(expr)
|
||||
relevant_facts.add(newfact)
|
||||
newexprs |= set([key.args[0] for key in
|
||||
newfact.atoms(AppliedPredicate)])
|
||||
|
||||
return newexprs - exprs, relevant_facts
|
||||
return relevant_facts, newexprs - exprs
|
||||
|
||||
|
||||
def get_all_relevant_facts(proposition, assumptions=True,
|
||||
@@ -115,44 +72,15 @@ def get_all_relevant_facts(proposition, assumptions=True,
|
||||
# we stop getting new things. Hopefully this strategy won't lead to an
|
||||
# infinite loop in the future.
|
||||
i = 0
|
||||
relevant_facts = CNF()
|
||||
relevant_facts = set()
|
||||
exprs = None
|
||||
all_exprs = set()
|
||||
while exprs != set():
|
||||
exprs, relevant_facts = get_relevant_facts(proposition,
|
||||
assumptions, context, exprs=exprs,
|
||||
(relevant_facts, exprs) = get_relevant_facts(proposition,
|
||||
And.make_args(assumptions), context,
|
||||
use_known_facts=use_known_facts, exprs=exprs,
|
||||
relevant_facts=relevant_facts)
|
||||
all_exprs |= exprs
|
||||
i += 1
|
||||
if i >= iterations:
|
||||
break
|
||||
return And(*relevant_facts)
|
||||
|
||||
if use_known_facts:
|
||||
known_facts_CNF = CNF()
|
||||
known_facts_CNF.add_clauses(get_all_known_facts())
|
||||
kf_encoded = EncodedCNF()
|
||||
kf_encoded.from_cnf(known_facts_CNF)
|
||||
|
||||
def translate_literal(lit, delta):
|
||||
if lit > 0:
|
||||
return lit + delta
|
||||
else:
|
||||
return lit - delta
|
||||
|
||||
def translate_data(data, delta):
|
||||
return [{translate_literal(i, delta) for i in clause} for clause in data]
|
||||
data = []
|
||||
symbols = []
|
||||
n_lit = len(kf_encoded.symbols)
|
||||
for i, expr in enumerate(all_exprs):
|
||||
symbols += [pred(expr) for pred in kf_encoded.symbols]
|
||||
data += translate_data(kf_encoded.data, i * n_lit)
|
||||
|
||||
encoding = dict(list(zip(symbols, range(1, len(symbols)+1))))
|
||||
ctx = EncodedCNF(data, encoding)
|
||||
else:
|
||||
ctx = EncodedCNF()
|
||||
|
||||
ctx.add_from_cnf(relevant_facts)
|
||||
|
||||
return ctx
|
||||
return And(*relevant_facts)
|
||||
|
||||
@@ -4,19 +4,20 @@ from collections import defaultdict
|
||||
|
||||
from sympy.assumptions.ask import Q
|
||||
from sympy.assumptions.assume import Predicate, AppliedPredicate
|
||||
from sympy.assumptions.cnf import AND, OR, to_NNF
|
||||
from sympy.core import (Add, Mul, Pow, Integer, Number, NumberSymbol,)
|
||||
from sympy.core.compatibility import MutableMapping
|
||||
from sympy.core.numbers import ImaginaryUnit
|
||||
from sympy.core.logic import fuzzy_or, fuzzy_and
|
||||
from sympy.core.rules import Transform
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.functions.elementary.complexes import Abs
|
||||
from sympy.logic.boolalg import (Equivalent, Implies, BooleanFunction)
|
||||
from sympy.logic.boolalg import (Equivalent, Implies, And, Or,
|
||||
BooleanFunction, Not)
|
||||
from sympy.matrices.expressions import MatMul
|
||||
|
||||
# APIs here may be subject to change
|
||||
|
||||
|
||||
# XXX: Better name?
|
||||
class UnevaluatedOnFree(BooleanFunction):
|
||||
"""
|
||||
Represents a Boolean function that remains unevaluated on free predicates
|
||||
@@ -67,19 +68,13 @@ class UnevaluatedOnFree(BooleanFunction):
|
||||
obj.expr = predicate_args.pop()
|
||||
obj.pred = arg.xreplace(Transform(lambda e: e.func, lambda e:
|
||||
isinstance(e, AppliedPredicate)))
|
||||
applied = obj.apply(obj.expr)
|
||||
applied = obj.apply()
|
||||
if applied is None:
|
||||
return obj
|
||||
return applied
|
||||
|
||||
def apply(self, expr=None):
|
||||
if expr is None:
|
||||
return
|
||||
pred = to_NNF(self.pred)
|
||||
return self._eval_apply(expr, pred)
|
||||
|
||||
def _eval_apply(self, expr, pred):
|
||||
return None
|
||||
def apply(self):
|
||||
return
|
||||
|
||||
|
||||
class AllArgs(UnevaluatedOnFree):
|
||||
@@ -94,6 +89,7 @@ class AllArgs(UnevaluatedOnFree):
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy.assumptions.sathandlers import AllArgs
|
||||
>>> from sympy import symbols, Q
|
||||
>>> x, y = symbols('x y')
|
||||
@@ -101,13 +97,11 @@ class AllArgs(UnevaluatedOnFree):
|
||||
>>> a
|
||||
AllArgs(Q.negative | Q.positive)
|
||||
>>> a.rcall(x*y)
|
||||
((Literal(Q.negative(x), False) | Literal(Q.positive(x), False)) & (Literal(Q.negative(y), False) | \
|
||||
Literal(Q.positive(y), False)))
|
||||
|
||||
(Q.negative(x) | Q.positive(x)) & (Q.negative(y) | Q.positive(y))
|
||||
"""
|
||||
|
||||
def _eval_apply(self, expr, pred):
|
||||
return AND(*[pred.rcall(arg) for arg in expr.args])
|
||||
def apply(self):
|
||||
return And(*[self.pred.rcall(arg) for arg in self.expr.args])
|
||||
|
||||
|
||||
class AnyArgs(UnevaluatedOnFree):
|
||||
@@ -122,6 +116,7 @@ class AnyArgs(UnevaluatedOnFree):
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy.assumptions.sathandlers import AnyArgs
|
||||
>>> from sympy import symbols, Q
|
||||
>>> x, y = symbols('x y')
|
||||
@@ -129,13 +124,11 @@ class AnyArgs(UnevaluatedOnFree):
|
||||
>>> a
|
||||
AnyArgs(Q.negative & Q.positive)
|
||||
>>> a.rcall(x*y)
|
||||
((Literal(Q.negative(x), False) & Literal(Q.positive(x), False)) | (Literal(Q.negative(y), False) & \
|
||||
Literal(Q.positive(y), False)))
|
||||
|
||||
(Q.negative(x) & Q.positive(x)) | (Q.negative(y) & Q.positive(y))
|
||||
"""
|
||||
|
||||
def _eval_apply(self, expr, pred):
|
||||
return OR(*[pred.rcall(arg) for arg in expr.args])
|
||||
def apply(self):
|
||||
return Or(*[self.pred.rcall(arg) for arg in self.expr.args])
|
||||
|
||||
|
||||
class ExactlyOneArg(UnevaluatedOnFree):
|
||||
@@ -151,6 +144,7 @@ class ExactlyOneArg(UnevaluatedOnFree):
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy.assumptions.sathandlers import ExactlyOneArg
|
||||
>>> from sympy import symbols, Q
|
||||
>>> x, y = symbols('x y')
|
||||
@@ -158,19 +152,17 @@ class ExactlyOneArg(UnevaluatedOnFree):
|
||||
>>> a
|
||||
ExactlyOneArg(Q.positive)
|
||||
>>> a.rcall(x*y)
|
||||
((Literal(Q.positive(x), False) & Literal(Q.positive(y), True)) | (Literal(Q.positive(x), True) & \
|
||||
Literal(Q.positive(y), False)))
|
||||
|
||||
(Q.positive(x) & ~Q.positive(y)) | (Q.positive(y) & ~Q.positive(x))
|
||||
"""
|
||||
|
||||
def _eval_apply(self, expr, pred):
|
||||
def apply(self):
|
||||
expr = self.expr
|
||||
pred = self.pred
|
||||
pred_args = [pred.rcall(arg) for arg in expr.args]
|
||||
# Technically this is xor, but if one term in the disjunction is true,
|
||||
# it is not possible for the remainder to be true, so regular or is
|
||||
# fine in this case.
|
||||
res = OR(*[AND(pred_args[i], *[~lit for lit in pred_args[:i] +
|
||||
pred_args[i+1:]]) for i in range(len(pred_args))])
|
||||
return res
|
||||
return Or(*[And(pred_args[i], *map(Not, pred_args[:i] +
|
||||
pred_args[i+1:])) for i in range(len(pred_args))])
|
||||
# Note: this is the equivalent cnf form. The above is more efficient
|
||||
# as the first argument of an implication, since p >> q is the same as
|
||||
# q | ~p, so the the ~ will convert the Or to and, and one just needs
|
||||
@@ -180,6 +172,10 @@ class ExactlyOneArg(UnevaluatedOnFree):
|
||||
|
||||
|
||||
def _old_assump_replacer(obj):
|
||||
# Things to be careful of:
|
||||
# - real means real or infinite in the old assumptions.
|
||||
# - nonzero does not imply real in the old assumptions.
|
||||
# - finite means finite and not zero in the old assumptions.
|
||||
if not isinstance(obj, AppliedPredicate):
|
||||
return obj
|
||||
|
||||
@@ -187,34 +183,33 @@ def _old_assump_replacer(obj):
|
||||
ret = None
|
||||
|
||||
if obj.func == Q.positive:
|
||||
ret = e.is_positive
|
||||
elif obj.func == Q.zero:
|
||||
ret = fuzzy_and([e.is_finite, e.is_positive])
|
||||
if obj.func == Q.zero:
|
||||
ret = e.is_zero
|
||||
elif obj.func == Q.negative:
|
||||
ret = e.is_negative
|
||||
elif obj.func == Q.nonpositive:
|
||||
ret = e.is_nonpositive
|
||||
elif obj.func == Q.nonzero:
|
||||
ret = e.is_nonzero
|
||||
elif obj.func == Q.nonnegative:
|
||||
ret = e.is_nonnegative
|
||||
if obj.func == Q.negative:
|
||||
ret = fuzzy_and([e.is_finite, e.is_negative])
|
||||
if obj.func == Q.nonpositive:
|
||||
ret = fuzzy_and([e.is_finite, e.is_nonpositive])
|
||||
if obj.func == Q.nonzero:
|
||||
ret = fuzzy_and([e.is_nonzero, e.is_finite])
|
||||
if obj.func == Q.nonnegative:
|
||||
ret = fuzzy_and([fuzzy_or([e.is_zero, e.is_finite]),
|
||||
e.is_nonnegative])
|
||||
|
||||
elif obj.func == Q.rational:
|
||||
if obj.func == Q.rational:
|
||||
ret = e.is_rational
|
||||
elif obj.func == Q.irrational:
|
||||
if obj.func == Q.irrational:
|
||||
ret = e.is_irrational
|
||||
|
||||
elif obj.func == Q.even:
|
||||
if obj.func == Q.even:
|
||||
ret = e.is_even
|
||||
elif obj.func == Q.odd:
|
||||
if obj.func == Q.odd:
|
||||
ret = e.is_odd
|
||||
elif obj.func == Q.integer:
|
||||
if obj.func == Q.integer:
|
||||
ret = e.is_integer
|
||||
elif obj.func == Q.composite:
|
||||
ret = e.is_composite
|
||||
elif obj.func == Q.imaginary:
|
||||
if obj.func == Q.imaginary:
|
||||
ret = e.is_imaginary
|
||||
elif obj.func == Q.commutative:
|
||||
if obj.func == Q.commutative:
|
||||
ret = e.is_commutative
|
||||
|
||||
if ret is None:
|
||||
@@ -234,18 +229,15 @@ def evaluate_old_assump(pred):
|
||||
|
||||
|
||||
class CheckOldAssump(UnevaluatedOnFree):
|
||||
def apply(self, expr=None, is_Not=False):
|
||||
arg = self.args[0](expr) if callable(self.args[0]) else self.args[0]
|
||||
res = Equivalent(arg, evaluate_old_assump(arg))
|
||||
return to_NNF(res)
|
||||
def apply(self):
|
||||
return Equivalent(self.args[0], evaluate_old_assump(self.args[0]))
|
||||
|
||||
|
||||
class CheckIsPrime(UnevaluatedOnFree):
|
||||
def apply(self, expr=None, is_Not=False):
|
||||
def apply(self):
|
||||
from sympy import isprime
|
||||
arg = self.args[0](expr) if callable(self.args[0]) else self.args[0]
|
||||
res = Equivalent(arg, isprime(expr))
|
||||
return to_NNF(res)
|
||||
return Equivalent(self.args[0], isprime(self.expr))
|
||||
|
||||
|
||||
class CustomLambda(object):
|
||||
"""
|
||||
@@ -256,8 +248,8 @@ class CustomLambda(object):
|
||||
def __init__(self, lamda):
|
||||
self.lamda = lamda
|
||||
|
||||
def apply(self, *args):
|
||||
return to_NNF(self.lamda(*args))
|
||||
def rcall(self, *args):
|
||||
return self.lamda(*args)
|
||||
|
||||
|
||||
class ClassFactRegistry(MutableMapping):
|
||||
@@ -321,7 +313,6 @@ for klass, fact in [
|
||||
# matching, so that we can just write Equivalent(Q.zero(x**y), Q.zero(x) & Q.positive(y))
|
||||
(Pow, CustomLambda(lambda power: Equivalent(Q.zero(power), Q.zero(power.base) & Q.positive(power.exp)))),
|
||||
(Integer, CheckIsPrime(Q.prime)),
|
||||
(Integer, CheckOldAssump(Q.composite)),
|
||||
# Implicitly assumes Mul has more than one arg
|
||||
# Would be AllArgs(Q.prime | Q.composite) except 1 is composite
|
||||
(Mul, Implies(AllArgs(Q.prime), ~Q.prime)),
|
||||
|
||||
@@ -5,7 +5,9 @@ from sympy.abc import x, y
|
||||
from sympy.assumptions.assume import global_assumptions, Predicate
|
||||
from sympy.assumptions.ask import _extract_facts, Q
|
||||
from sympy.core import symbols
|
||||
from sympy.logic.boolalg import Or
|
||||
from sympy.printing import pretty
|
||||
from sympy.utilities.pytest import XFAIL
|
||||
|
||||
|
||||
def test_equal():
|
||||
|
||||
@@ -16,12 +16,10 @@ from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (
|
||||
acos, acot, asin, atan, cos, cot, sin, tan)
|
||||
from sympy.logic.boolalg import Equivalent, Implies, Xor, And, to_cnf
|
||||
from sympy.matrices import Matrix, SparseMatrix
|
||||
from sympy.utilities.pytest import XFAIL, slow, raises, warns_deprecated_sympy
|
||||
from sympy.assumptions.assume import assuming
|
||||
import math
|
||||
|
||||
|
||||
def test_int_1():
|
||||
z = 1
|
||||
assert ask(Q.commutative(z)) is True
|
||||
@@ -176,8 +174,7 @@ def test_infinity():
|
||||
assert ask(Q.complex(oo)) is False
|
||||
assert ask(Q.irrational(oo)) is False
|
||||
assert ask(Q.imaginary(oo)) is False
|
||||
assert ask(Q.positive(oo)) is False
|
||||
#assert ask(Q.extended_positive(oo)) is True
|
||||
assert ask(Q.positive(oo)) is True
|
||||
assert ask(Q.negative(oo)) is False
|
||||
assert ask(Q.even(oo)) is False
|
||||
assert ask(Q.odd(oo)) is False
|
||||
@@ -200,8 +197,7 @@ def test_neg_infinity():
|
||||
assert ask(Q.irrational(mm)) is False
|
||||
assert ask(Q.imaginary(mm)) is False
|
||||
assert ask(Q.positive(mm)) is False
|
||||
assert ask(Q.negative(mm)) is False
|
||||
#assert ask(Q.extended_negative(mm)) is True
|
||||
assert ask(Q.negative(mm)) is True
|
||||
assert ask(Q.even(mm)) is False
|
||||
assert ask(Q.odd(mm)) is False
|
||||
assert ask(Q.finite(mm)) is False
|
||||
@@ -569,8 +565,8 @@ def test_I():
|
||||
assert ask(Q.real(z)) is True
|
||||
|
||||
|
||||
|
||||
def test_bounded():
|
||||
@slow
|
||||
def test_bounded1():
|
||||
x, y, z = symbols('x,y,z')
|
||||
assert ask(Q.finite(x)) is None
|
||||
assert ask(Q.finite(x), Q.finite(x)) is True
|
||||
@@ -656,6 +652,9 @@ def test_bounded():
|
||||
assert ask(Q.finite(a), ~Q.positive(x) & Q.positive(y)) is None
|
||||
assert ask(Q.finite(a), ~Q.positive(x) & ~Q.positive(y)) is None
|
||||
|
||||
|
||||
@slow
|
||||
def test_bounded2a():
|
||||
x, y, z = symbols('x,y,z')
|
||||
a = x + y + z
|
||||
x, y, z = a.args
|
||||
@@ -800,6 +799,13 @@ def test_bounded():
|
||||
Q.finite(a), Q.finite(x) & ~Q.finite(y) & Q.positive(z)) is None
|
||||
assert ask(Q.finite(a), Q.finite(x) & Q.positive(y) &
|
||||
~Q.finite(y) & Q.positive(z) & ~Q.finite(z)) is False
|
||||
|
||||
|
||||
@slow
|
||||
def test_bounded2b():
|
||||
x, y, z = symbols('x,y,z')
|
||||
a = x + y + z
|
||||
x, y, z = a.args
|
||||
assert ask(Q.finite(a), Q.finite(x) &
|
||||
Q.positive(y) & ~Q.finite(y) & Q.negative(z)) is None
|
||||
assert ask(
|
||||
@@ -974,6 +980,9 @@ def test_bounded():
|
||||
assert ask(Q.finite(2*x)) is None
|
||||
assert ask(Q.finite(2*x), Q.finite(x)) is True
|
||||
|
||||
|
||||
@slow
|
||||
def test_bounded3():
|
||||
x, y, z = symbols('x,y,z')
|
||||
a = x*y
|
||||
x, y = a.args
|
||||
@@ -1032,9 +1041,9 @@ def test_bounded():
|
||||
assert ask(Q.finite(2**x)) is None
|
||||
assert ask(Q.finite(2**x), Q.finite(x)) is True
|
||||
assert ask(Q.finite(x**x)) is None
|
||||
assert ask(Q.finite(S.Half ** x)) is None
|
||||
assert ask(Q.finite(S.Half ** x), Q.positive(x)) is True
|
||||
assert ask(Q.finite(S.Half ** x), Q.negative(x)) is None
|
||||
assert ask(Q.finite(Rational(1, 2) ** x)) is None
|
||||
assert ask(Q.finite(Rational(1, 2) ** x), Q.positive(x)) is True
|
||||
assert ask(Q.finite(Rational(1, 2) ** x), Q.negative(x)) is None
|
||||
assert ask(Q.finite(2**x), Q.negative(x)) is True
|
||||
assert ask(Q.finite(sqrt(x))) is None
|
||||
assert ask(Q.finite(2**x), ~Q.finite(x)) is False
|
||||
@@ -1167,6 +1176,7 @@ def test_complex():
|
||||
assert ask(Q.complex(im(x))) is True
|
||||
|
||||
|
||||
@slow
|
||||
def test_even_query():
|
||||
assert ask(Q.even(x)) is None
|
||||
assert ask(Q.even(x), Q.integer(x)) is None
|
||||
@@ -1321,6 +1331,7 @@ def test_rational():
|
||||
assert ask(Q.rational(h(x)), Q.rational(x)) is False
|
||||
|
||||
|
||||
@slow
|
||||
def test_hermitian():
|
||||
assert ask(Q.hermitian(x)) is None
|
||||
assert ask(Q.hermitian(x), Q.antihermitian(x)) is False
|
||||
@@ -1424,6 +1435,7 @@ def test_hermitian():
|
||||
Q.imaginary(x) & Q.imaginary(y) & Q.imaginary(z)) is True
|
||||
|
||||
|
||||
@slow
|
||||
def test_imaginary():
|
||||
assert ask(Q.imaginary(x)) is None
|
||||
assert ask(Q.imaginary(x), Q.real(x)) is False
|
||||
@@ -1500,7 +1512,7 @@ def test_imaginary():
|
||||
assert ask(Q.imaginary(exp(pi*I/2, evaluate=False))) is True
|
||||
|
||||
# issue 7886
|
||||
assert ask(Q.imaginary(Pow(x, Rational(1, 4))), Q.real(x) & Q.negative(x)) is False
|
||||
assert ask(Q.imaginary(Pow(x, S.One/4)), Q.real(x) & Q.negative(x)) is False
|
||||
|
||||
|
||||
def test_integer():
|
||||
@@ -1525,6 +1537,7 @@ def test_integer():
|
||||
assert ask(Q.integer(x/3), Q.even(x)) is None
|
||||
|
||||
|
||||
@slow
|
||||
def test_negative():
|
||||
assert ask(Q.negative(x), Q.negative(x)) is True
|
||||
assert ask(Q.negative(x), Q.positive(x)) is False
|
||||
@@ -1596,6 +1609,7 @@ def test_nonzero():
|
||||
assert ask(Q.nonzero(cos(1)**2 + sin(1)**2 - 1)) is None
|
||||
|
||||
|
||||
@slow
|
||||
def test_zero():
|
||||
assert ask(Q.zero(x)) is None
|
||||
assert ask(Q.zero(x), Q.real(x)) is None
|
||||
@@ -1626,6 +1640,7 @@ def test_zero():
|
||||
assert ask(Q.zero(x) | Q.zero(y), Q.zero(x*y)) is True
|
||||
|
||||
|
||||
@slow
|
||||
def test_odd_query():
|
||||
assert ask(Q.odd(x)) is None
|
||||
assert ask(Q.odd(x), Q.odd(x)) is True
|
||||
@@ -1731,6 +1746,7 @@ def test_prime():
|
||||
assert ask(Q.prime(x**y), Q.integer(x) & Q.integer(y)) is False
|
||||
|
||||
|
||||
@slow
|
||||
def test_positive():
|
||||
assert ask(Q.positive(x), Q.positive(x)) is True
|
||||
assert ask(Q.positive(x), Q.negative(x)) is False
|
||||
@@ -1830,7 +1846,7 @@ def test_real_basic():
|
||||
assert ask(Q.real(I*x), Q.imaginary(x)) is True
|
||||
assert ask(Q.real(I*x), Q.complex(x)) is None
|
||||
|
||||
|
||||
@slow
|
||||
def test_real_pow():
|
||||
assert ask(Q.real(x**2), Q.real(x)) is True
|
||||
assert ask(Q.real(sqrt(x)), Q.negative(x)) is False
|
||||
@@ -1858,7 +1874,6 @@ def test_real_pow():
|
||||
assert ask(Q.real(x**i), Q.imaginary(i)) is None # x could be 0
|
||||
assert ask(Q.real(x**(I*pi/log(x))), Q.real(x)) is True
|
||||
|
||||
|
||||
def test_real_functions():
|
||||
# trigonometric functions
|
||||
assert ask(Q.real(sin(x))) is None
|
||||
@@ -1892,18 +1907,6 @@ def test_real_functions():
|
||||
assert ask(Q.real(im(x))) is True
|
||||
|
||||
|
||||
def test_matrix():
|
||||
|
||||
# hermitian
|
||||
assert ask(Q.hermitian(Matrix([[2, 2 + I, 4], [2 - I, 3, I], [4, -I, 1]]))) == True
|
||||
assert ask(Q.hermitian(Matrix([[2, 2 + I, 4], [2 + I, 3, I], [4, -I, 1]]))) == False
|
||||
z = symbols('z', complex=True)
|
||||
assert ask(Q.hermitian(Matrix([[2, 2 + I, z], [2 - I, 3, I], [4, -I, 1]]))) == None
|
||||
assert ask(Q.hermitian(SparseMatrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))))) == True
|
||||
assert ask(Q.hermitian(SparseMatrix(((25, 15, -5), (15, I, 0), (-5, 0, 11))))) == False
|
||||
assert ask(Q.hermitian(SparseMatrix(((25, 15, -5), (15, z, 0), (-5, 0, 11))))) == None
|
||||
|
||||
|
||||
def test_algebraic():
|
||||
assert ask(Q.algebraic(x)) is None
|
||||
|
||||
@@ -1918,8 +1921,8 @@ def test_algebraic():
|
||||
assert ask(Q.algebraic(I*sqrt(3))) is True
|
||||
assert ask(Q.algebraic(sqrt(1 + I*sqrt(3)))) is True
|
||||
|
||||
assert ask(Q.algebraic((1 + I*sqrt(3)**Rational(17, 31)))) is True
|
||||
assert ask(Q.algebraic((1 + I*sqrt(3)**(17/pi)))) is False
|
||||
assert ask(Q.algebraic((1 + I*sqrt(3)**(S(17)/31)))) is True
|
||||
assert ask(Q.algebraic((1 + I*sqrt(3)**(S(17)/pi)))) is False
|
||||
|
||||
for f in [exp, sin, tan, asin, atan, cos]:
|
||||
assert ask(Q.algebraic(f(7))) is False
|
||||
@@ -2006,6 +2009,7 @@ def test_composite_assumptions():
|
||||
assert ask(Q.positive(x), Q.real(x) >> Q.positive(y)) is None
|
||||
assert ask(Q.real(x), ~(Q.real(x) >> Q.real(y))) is True
|
||||
|
||||
|
||||
def test_incompatible_resolutors():
|
||||
class Prime2AskHandler(AskHandler):
|
||||
@staticmethod
|
||||
@@ -2178,7 +2182,7 @@ def test_check_old_assumption():
|
||||
assert ask(Q.real(x)) is None
|
||||
assert ask(Q.complex(x)) is True
|
||||
|
||||
x = symbols('x', positive=True, finite=True)
|
||||
x = symbols('x', positive=True)
|
||||
assert ask(Q.positive(x)) is True
|
||||
assert ask(Q.negative(x)) is False
|
||||
assert ask(Q.real(x)) is True
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from sympy import (Abs, exp, Expr, I, pi, Q, Rational, refine, S, sqrt,
|
||||
atan, atan2, nan, Symbol, re, im)
|
||||
from sympy.abc import w, x, y, z
|
||||
atan, atan2, nan, Symbol)
|
||||
from sympy.abc import x, y, z
|
||||
from sympy.core.relational import Eq, Ne
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.utilities.pytest import slow
|
||||
@@ -26,14 +26,17 @@ def test_pow1():
|
||||
assert refine(sqrt(x**2), Q.complex(x)) != Abs(x)
|
||||
assert refine(sqrt(x**2), Q.real(x)) == Abs(x)
|
||||
assert refine(sqrt(x**2), Q.positive(x)) == x
|
||||
assert refine((x**3)**Rational(1, 3)) != x
|
||||
assert refine((x**3)**(S(1)/3)) != x
|
||||
|
||||
assert refine((x**3)**Rational(1, 3), Q.real(x)) != x
|
||||
assert refine((x**3)**Rational(1, 3), Q.positive(x)) == x
|
||||
assert refine((x**3)**(S(1)/3), Q.real(x)) != x
|
||||
assert refine((x**3)**(S(1)/3), Q.positive(x)) == x
|
||||
|
||||
assert refine(sqrt(1/x), Q.real(x)) != 1/sqrt(x)
|
||||
assert refine(sqrt(1/x), Q.positive(x)) == 1/sqrt(x)
|
||||
|
||||
|
||||
@slow
|
||||
def test_pow2():
|
||||
# powers of (-1)
|
||||
assert refine((-1)**(x + y), Q.even(x)) == (-1)**y
|
||||
assert refine((-1)**(x + y + z), Q.odd(x) & Q.odd(z)) == (-1)**y
|
||||
@@ -41,6 +44,9 @@ def test_pow1():
|
||||
assert refine((-1)**(x + y + 2), Q.odd(x)) == (-1)**(y + 1)
|
||||
assert refine((-1)**(x + 3)) == (-1)**(x + 1)
|
||||
|
||||
|
||||
@slow
|
||||
def test_pow3():
|
||||
# continuation
|
||||
assert refine((-1)**((-1)**x/2 - S.Half), Q.integer(x)) == (-1)**x
|
||||
assert refine((-1)**((-1)**x/2 + S.Half), Q.integer(x)) == (-1)**(x + 1)
|
||||
@@ -48,7 +54,7 @@ def test_pow1():
|
||||
|
||||
|
||||
@slow
|
||||
def test_pow2():
|
||||
def test_pow4():
|
||||
assert refine((-1)**((-1)**x/2 - 7*S.Half), Q.integer(x)) == (-1)**(x + 1)
|
||||
assert refine((-1)**((-1)**x/2 - 9*S.Half), Q.integer(x)) == (-1)**x
|
||||
|
||||
@@ -61,7 +67,7 @@ def test_pow2():
|
||||
def test_exp():
|
||||
x = Symbol('x', integer=True)
|
||||
assert refine(exp(pi*I*2*x)) == 1
|
||||
assert refine(exp(pi*I*2*(x + S.Half))) == -1
|
||||
assert refine(exp(pi*I*2*(x + Rational(1, 2)))) == -1
|
||||
assert refine(exp(pi*I*2*(x + Rational(1, 4)))) == I
|
||||
assert refine(exp(pi*I*2*(x + Rational(3, 4)))) == -I
|
||||
|
||||
@@ -136,40 +142,7 @@ def test_atan2():
|
||||
assert refine(atan2(y, x), Q.zero(y) & Q.negative(x)) == pi
|
||||
assert refine(atan2(y, x), Q.positive(y) & Q.zero(x)) == pi/2
|
||||
assert refine(atan2(y, x), Q.negative(y) & Q.zero(x)) == -pi/2
|
||||
assert refine(atan2(y, x), Q.zero(y) & Q.zero(x)) is nan
|
||||
|
||||
|
||||
def test_re():
|
||||
assert refine(re(x), Q.real(x)) == x
|
||||
assert refine(re(x), Q.imaginary(x)) == 0
|
||||
assert refine(re(x+y), Q.real(x) & Q.real(y)) == x + y
|
||||
assert refine(re(x+y), Q.real(x) & Q.imaginary(y)) == x
|
||||
assert refine(re(x*y), Q.real(x) & Q.real(y)) == x * y
|
||||
assert refine(re(x*y), Q.real(x) & Q.imaginary(y)) == 0
|
||||
assert refine(re(x*y*z), Q.real(x) & Q.real(y) & Q.real(z)) == x * y * z
|
||||
|
||||
|
||||
def test_im():
|
||||
assert refine(im(x), Q.imaginary(x)) == -I*x
|
||||
assert refine(im(x), Q.real(x)) == 0
|
||||
assert refine(im(x+y), Q.imaginary(x) & Q.imaginary(y)) == -I*x - I*y
|
||||
assert refine(im(x+y), Q.real(x) & Q.imaginary(y)) == -I*y
|
||||
assert refine(im(x*y), Q.imaginary(x) & Q.real(y)) == -I*x*y
|
||||
assert refine(im(x*y), Q.imaginary(x) & Q.imaginary(y)) == 0
|
||||
assert refine(im(1/x), Q.imaginary(x)) == -I/x
|
||||
assert refine(im(x*y*z), Q.imaginary(x) & Q.imaginary(y)
|
||||
& Q.imaginary(z)) == -I*x*y*z
|
||||
|
||||
|
||||
def test_complex():
|
||||
assert refine(re(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
|
||||
x/(x**2 + y**2)
|
||||
assert refine(im(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
|
||||
-y/(x**2 + y**2)
|
||||
assert refine(re((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
|
||||
& Q.real(z)) == w*y - x*z
|
||||
assert refine(im((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
|
||||
& Q.real(z)) == w*z + x*y
|
||||
assert refine(atan2(y, x), Q.zero(y) & Q.zero(x)) == nan
|
||||
|
||||
|
||||
def test_func_args():
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from sympy.assumptions.satask import satask
|
||||
|
||||
from sympy import S, symbols, Q, assuming, Implies, MatrixSymbol, I, pi
|
||||
from sympy import symbols, Q, assuming, Implies, MatrixSymbol, I, pi, Rational
|
||||
|
||||
from sympy.utilities.pytest import raises, XFAIL
|
||||
from sympy.utilities.pytest import raises, XFAIL, slow
|
||||
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
@@ -200,7 +200,7 @@ def test_odd_satask():
|
||||
|
||||
def test_integer():
|
||||
assert satask(Q.integer(1)) is True
|
||||
assert satask(Q.integer(S.Half)) is False
|
||||
assert satask(Q.integer(Rational(1, 2))) is False
|
||||
|
||||
assert satask(Q.integer(x + y), Q.integer(x) & Q.integer(y)) is True
|
||||
assert satask(Q.integer(x + y), Q.integer(x)) is None
|
||||
@@ -264,6 +264,7 @@ def test_pos_neg():
|
||||
assert satask(Q.negative(x + y), Q.positive(x) & Q.positive(y)) is False
|
||||
|
||||
|
||||
@slow
|
||||
def test_pow_pos_neg():
|
||||
assert satask(Q.nonnegative(x**2), Q.positive(x)) is True
|
||||
assert satask(Q.nonpositive(x**2), Q.positive(x)) is False
|
||||
@@ -322,16 +323,3 @@ def test_pow_pos_neg():
|
||||
|
||||
# We could deduce things for negative powers if x is nonzero, but it
|
||||
# isn't implemented yet.
|
||||
|
||||
|
||||
def test_prime_composite():
|
||||
assert satask(Q.prime(x), Q.composite(x)) is False
|
||||
assert satask(Q.composite(x), Q.prime(x)) is False
|
||||
assert satask(Q.composite(x), ~Q.prime(x)) is None
|
||||
assert satask(Q.prime(x), ~Q.composite(x)) is None
|
||||
# since 1 is neither prime nor composite the following should hold
|
||||
assert satask(Q.prime(x), Q.integer(x) & Q.positive(x) & ~Q.composite(x)) is None
|
||||
assert satask(Q.prime(2)) is True
|
||||
assert satask(Q.prime(4)) is False
|
||||
assert satask(Q.prime(1)) is False
|
||||
assert satask(Q.composite(1)) is False
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from sympy import Mul, Basic, Q, Expr, And, symbols, Equivalent, Or
|
||||
from sympy.assumptions.cnf import to_NNF
|
||||
from sympy import Mul, Basic, Q, Expr, And, symbols, Equivalent, Implies, Or
|
||||
|
||||
from sympy.assumptions.sathandlers import (ClassFactRegistry, AllArgs,
|
||||
UnevaluatedOnFree, AnyArgs, CheckOldAssump, ExactlyOneArg)
|
||||
@@ -40,7 +39,7 @@ def test_UnevaluatedOnFree():
|
||||
Q.negative(y)))
|
||||
|
||||
class MyUnevaluatedOnFree(UnevaluatedOnFree):
|
||||
def apply(self, expr=None):
|
||||
def apply(self):
|
||||
return self.args[0]
|
||||
|
||||
a = MyUnevaluatedOnFree(Q.positive)
|
||||
@@ -57,32 +56,32 @@ def test_UnevaluatedOnFree():
|
||||
def test_AllArgs():
|
||||
a = AllArgs(Q.zero)
|
||||
b = AllArgs(Q.positive | Q.negative)
|
||||
assert a.rcall(x*y) == to_NNF(And(Q.zero(x), Q.zero(y)))
|
||||
assert b.rcall(x*y) == to_NNF(And(Q.positive(x) | Q.negative(x), Q.positive(y) | Q.negative(y)))
|
||||
assert a.rcall(x*y) == And(Q.zero(x), Q.zero(y))
|
||||
assert b.rcall(x*y) == And(Q.positive(x) | Q.negative(x), Q.positive(y) | Q.negative(y))
|
||||
|
||||
|
||||
def test_AnyArgs():
|
||||
a = AnyArgs(Q.zero)
|
||||
b = AnyArgs(Q.positive & Q.negative)
|
||||
assert a.rcall(x*y) == to_NNF(Or(Q.zero(x), Q.zero(y)))
|
||||
assert b.rcall(x*y) == to_NNF(Or(Q.positive(x) & Q.negative(x), Q.positive(y) & Q.negative(y)))
|
||||
assert a.rcall(x*y) == Or(Q.zero(x), Q.zero(y))
|
||||
assert b.rcall(x*y) == Or(Q.positive(x) & Q.negative(x), Q.positive(y) & Q.negative(y))
|
||||
|
||||
|
||||
def test_CheckOldAssump():
|
||||
# TODO: Make these tests more complete
|
||||
|
||||
class Test1(Expr):
|
||||
def _eval_is_extended_positive(self):
|
||||
def _eval_is_positive(self):
|
||||
return True
|
||||
def _eval_is_extended_negative(self):
|
||||
def _eval_is_negative(self):
|
||||
return False
|
||||
|
||||
class Test2(Expr):
|
||||
def _eval_is_finite(self):
|
||||
return True
|
||||
def _eval_is_extended_positive(self):
|
||||
def _eval_is_positive(self):
|
||||
return True
|
||||
def _eval_is_extended_negative(self):
|
||||
def _eval_is_negative(self):
|
||||
return False
|
||||
|
||||
t1 = Test1()
|
||||
@@ -91,19 +90,19 @@ def test_CheckOldAssump():
|
||||
# We can't say if it's positive or negative in the old assumptions without
|
||||
# bounded. Remember, True means "no new knowledge", and
|
||||
# Q.positive(t2) means "t2 is positive."
|
||||
assert CheckOldAssump(Q.positive(t1)) == to_NNF(True)
|
||||
assert CheckOldAssump(Q.negative(t1)) == to_NNF(~Q.negative(t1))
|
||||
assert CheckOldAssump(Q.positive(t1)) == True
|
||||
assert CheckOldAssump(Q.negative(t1)) == ~Q.negative(t1)
|
||||
|
||||
assert CheckOldAssump(Q.positive(t2)) == to_NNF(Q.positive(t2))
|
||||
assert CheckOldAssump(Q.negative(t2)) == to_NNF(~Q.negative(t2))
|
||||
assert CheckOldAssump(Q.positive(t2)) == Q.positive(t2)
|
||||
assert CheckOldAssump(Q.negative(t2)) == ~Q.negative(t2)
|
||||
|
||||
|
||||
def test_ExactlyOneArg():
|
||||
a = ExactlyOneArg(Q.zero)
|
||||
b = ExactlyOneArg(Q.positive | Q.negative)
|
||||
assert a.rcall(x*y) == to_NNF(Or(Q.zero(x) & ~Q.zero(y), Q.zero(y) & ~Q.zero(x)))
|
||||
assert a.rcall(x*y*z) == to_NNF(Or(Q.zero(x) & ~Q.zero(y) & ~Q.zero(z), Q.zero(y)
|
||||
& ~Q.zero(x) & ~Q.zero(z), Q.zero(z) & ~Q.zero(x) & ~Q.zero(y)))
|
||||
assert b.rcall(x*y) == to_NNF(Or((Q.positive(x) | Q.negative(x)) &
|
||||
assert a.rcall(x*y) == Or(Q.zero(x) & ~Q.zero(y), Q.zero(y) & ~Q.zero(x))
|
||||
assert a.rcall(x*y*z) == Or(Q.zero(x) & ~Q.zero(y) & ~Q.zero(z), Q.zero(y)
|
||||
& ~Q.zero(x) & ~Q.zero(z), Q.zero(z) & ~Q.zero(x) & ~Q.zero(y))
|
||||
assert b.rcall(x*y) == Or((Q.positive(x) | Q.negative(x)) &
|
||||
~(Q.positive(y) | Q.negative(y)), (Q.positive(y) | Q.negative(y)) &
|
||||
~(Q.positive(x) | Q.negative(x))))
|
||||
~(Q.positive(x) | Q.negative(x)))
|
||||
|
||||
@@ -148,7 +148,7 @@ def finite_diff_weights(order, x_list, x0=S.One):
|
||||
derivative is wanted, weights for 0th, 1st and 2nd order are
|
||||
calculated "for free", so are formulae using subsets of ``x_list``.
|
||||
This is something one can take advantage of to save computational cost.
|
||||
Be aware that one should define ``x_list`` from nearest to furthest from
|
||||
Be aware that one should define ``x_list`` from nearest to farest from
|
||||
``x0``. If not, subsets of ``x_list`` will yield poorer approximations,
|
||||
which might not grand an order of accuracy of ``len(x_list) - order``.
|
||||
|
||||
@@ -167,9 +167,6 @@ def finite_diff_weights(order, x_list, x0=S.One):
|
||||
|
||||
"""
|
||||
# The notation below closely corresponds to the one used in the paper.
|
||||
order = S(order)
|
||||
if not order.is_number:
|
||||
raise ValueError("Cannot handle symbolic order.")
|
||||
if order < 0:
|
||||
raise ValueError("Negative derivative order illegal.")
|
||||
if int(order) != order:
|
||||
@@ -178,10 +175,10 @@ def finite_diff_weights(order, x_list, x0=S.One):
|
||||
N = len(x_list) - 1
|
||||
delta = [[[0 for nu in range(N+1)] for n in range(N+1)] for
|
||||
m in range(M+1)]
|
||||
delta[0][0][0] = S.One
|
||||
c1 = S.One
|
||||
delta[0][0][0] = S(1)
|
||||
c1 = S(1)
|
||||
for n in range(1, N+1):
|
||||
c2 = S.One
|
||||
c2 = S(1)
|
||||
for nu in range(0, n):
|
||||
c3 = x_list[n]-x_list[nu]
|
||||
c2 = c2 * c3
|
||||
@@ -198,7 +195,7 @@ def finite_diff_weights(order, x_list, x0=S.One):
|
||||
return delta
|
||||
|
||||
|
||||
def apply_finite_diff(order, x_list, y_list, x0=S.Zero):
|
||||
def apply_finite_diff(order, x_list, y_list, x0=S(0)):
|
||||
"""
|
||||
Calculates the finite difference approximation of
|
||||
the derivative of requested order at ``x0`` from points
|
||||
@@ -216,7 +213,7 @@ def apply_finite_diff(order, x_list, y_list, x0=S.Zero):
|
||||
variable in x_list.
|
||||
x0: Number or Symbol
|
||||
At what value of the independent variable the derivative should be
|
||||
evaluated. Defaults to 0.
|
||||
evaluated. Defaults to S(0).
|
||||
|
||||
Returns
|
||||
=======
|
||||
@@ -390,8 +387,6 @@ def _as_finite_diff(derivative, points=1, x0=None, wrt=None):
|
||||
x0 = wrt
|
||||
|
||||
if not iterable(points):
|
||||
if getattr(points, 'is_Function', False) and wrt in points.args:
|
||||
points = points.subs(wrt, x0)
|
||||
# points is simply the step-size, let's make it a
|
||||
# equidistant sequence centered around x0
|
||||
if order % 2 == 0:
|
||||
@@ -418,10 +413,6 @@ as_finite_diff = deprecated(
|
||||
useinstead="Derivative.as_finite_difference",
|
||||
deprecated_since_version="1.1", issue=11410)(_as_finite_diff)
|
||||
|
||||
as_finite_diff.__doc__ = """
|
||||
Deprecated function. Use Diff.as_finite_difference instead.
|
||||
"""
|
||||
|
||||
|
||||
def differentiate_finite(expr, *symbols,
|
||||
# points=1, x0=None, wrt=None, evaluate=True, #Py2:
|
||||
@@ -474,7 +465,7 @@ def differentiate_finite(expr, *symbols,
|
||||
x0 = kwargs.pop('x0', None)
|
||||
wrt = kwargs.pop('wrt', None)
|
||||
evaluate = kwargs.pop('evaluate', False)
|
||||
if kwargs:
|
||||
if kwargs != {}:
|
||||
raise ValueError("Unknown kwargs: %s" % kwargs)
|
||||
|
||||
Dexpr = expr.diff(*symbols, evaluate=evaluate)
|
||||
|
||||
@@ -11,7 +11,6 @@ def test_euler_interface():
|
||||
raises(TypeError, lambda: euler(D(x(t), t)*y(t), [x(t), y]))
|
||||
raises(ValueError, lambda: euler(D(x(t), t)*x(y), [x(t), x(y)]))
|
||||
raises(TypeError, lambda: euler(D(x(t), t)**2, x(0)))
|
||||
raises(TypeError, lambda: euler(D(x(t), t)*y(t), [t]))
|
||||
assert euler(D(x(t), t)**2/2, {x(t)}) == [Eq(-D(x(t), t, t), 0)]
|
||||
assert euler(D(x(t), t)**2/2, x(t), {t}) == [Eq(-D(x(t), t, t), 0)]
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from itertools import product
|
||||
|
||||
from sympy import S, symbols, Function, exp, diff, Rational
|
||||
from sympy import S, symbols, Function, exp
|
||||
from sympy.core.compatibility import range
|
||||
from sympy.utilities.pytest import warns_deprecated_sympy
|
||||
from sympy.calculus.finite_diff import (
|
||||
apply_finite_diff, differentiate_finite, finite_diff_weights,
|
||||
as_finite_diff
|
||||
)
|
||||
from sympy.core.compatibility import range
|
||||
from sympy.utilities.pytest import raises, warns_deprecated_sympy
|
||||
|
||||
|
||||
def test_apply_finite_diff():
|
||||
@@ -16,62 +16,61 @@ def test_apply_finite_diff():
|
||||
(f(x+h)-f(x-h))/(2*h)).simplify() == 0
|
||||
|
||||
assert (apply_finite_diff(1, [5, 6, 7], [f(5), f(6), f(7)], 5) -
|
||||
(Rational(-3, 2)*f(5) + 2*f(6) - S.Half*f(7))).simplify() == 0
|
||||
raises(ValueError, lambda: apply_finite_diff(1, [x, h], [f(x)]))
|
||||
(-S(3)/2*f(5) + 2*f(6) - S(1)/2*f(7))).simplify() == 0
|
||||
|
||||
|
||||
def test_finite_diff_weights():
|
||||
|
||||
d = finite_diff_weights(1, [5, 6, 7], 5)
|
||||
assert d[1][2] == [Rational(-3, 2), 2, Rational(-1, 2)]
|
||||
assert d[1][2] == [-S(3)/2, 2, -S(1)/2]
|
||||
|
||||
# Table 1, p. 702 in doi:10.1090/S0025-5718-1988-0935077-0
|
||||
# --------------------------------------------------------
|
||||
xl = [0, 1, -1, 2, -2, 3, -3, 4, -4]
|
||||
|
||||
# d holds all coefficients
|
||||
d = finite_diff_weights(4, xl, S.Zero)
|
||||
d = finite_diff_weights(4, xl, S(0))
|
||||
|
||||
# Zeroeth derivative
|
||||
for i in range(5):
|
||||
assert d[0][i] == [S.One] + [S.Zero]*8
|
||||
assert d[0][i] == [S(1)] + [S(0)]*8
|
||||
|
||||
# First derivative
|
||||
assert d[1][0] == [S.Zero]*9
|
||||
assert d[1][2] == [S.Zero, S.Half, Rational(-1, 2)] + [S.Zero]*6
|
||||
assert d[1][4] == [S.Zero, Rational(2, 3), Rational(-2, 3), Rational(-1, 12), Rational(1, 12)] + [S.Zero]*4
|
||||
assert d[1][6] == [S.Zero, Rational(3, 4), Rational(-3, 4), Rational(-3, 20), Rational(3, 20),
|
||||
Rational(1, 60), Rational(-1, 60)] + [S.Zero]*2
|
||||
assert d[1][8] == [S.Zero, Rational(4, 5), Rational(-4, 5), Rational(-1, 5), Rational(1, 5),
|
||||
Rational(4, 105), Rational(-4, 105), Rational(-1, 280), Rational(1, 280)]
|
||||
assert d[1][0] == [S(0)]*9
|
||||
assert d[1][2] == [S(0), S(1)/2, -S(1)/2] + [S(0)]*6
|
||||
assert d[1][4] == [S(0), S(2)/3, -S(2)/3, -S(1)/12, S(1)/12] + [S(0)]*4
|
||||
assert d[1][6] == [S(0), S(3)/4, -S(3)/4, -S(3)/20, S(3)/20,
|
||||
S(1)/60, -S(1)/60] + [S(0)]*2
|
||||
assert d[1][8] == [S(0), S(4)/5, -S(4)/5, -S(1)/5, S(1)/5,
|
||||
S(4)/105, -S(4)/105, -S(1)/280, S(1)/280]
|
||||
|
||||
# Second derivative
|
||||
for i in range(2):
|
||||
assert d[2][i] == [S.Zero]*9
|
||||
assert d[2][2] == [-S(2), S.One, S.One] + [S.Zero]*6
|
||||
assert d[2][4] == [Rational(-5, 2), Rational(4, 3), Rational(4, 3), Rational(-1, 12), Rational(-1, 12)] + [S.Zero]*4
|
||||
assert d[2][6] == [Rational(-49, 18), Rational(3, 2), Rational(3, 2), Rational(-3, 20), Rational(-3, 20),
|
||||
Rational(1, 90), Rational(1, 90)] + [S.Zero]*2
|
||||
assert d[2][8] == [Rational(-205, 72), Rational(8, 5), Rational(8, 5), Rational(-1, 5), Rational(-1, 5),
|
||||
Rational(8, 315), Rational(8, 315), Rational(-1, 560), Rational(-1, 560)]
|
||||
assert d[2][i] == [S(0)]*9
|
||||
assert d[2][2] == [-S(2), S(1), S(1)] + [S(0)]*6
|
||||
assert d[2][4] == [-S(5)/2, S(4)/3, S(4)/3, -S(1)/12, -S(1)/12] + [S(0)]*4
|
||||
assert d[2][6] == [-S(49)/18, S(3)/2, S(3)/2, -S(3)/20, -S(3)/20,
|
||||
S(1)/90, S(1)/90] + [S(0)]*2
|
||||
assert d[2][8] == [-S(205)/72, S(8)/5, S(8)/5, -S(1)/5, -S(1)/5,
|
||||
S(8)/315, S(8)/315, -S(1)/560, -S(1)/560]
|
||||
|
||||
# Third derivative
|
||||
for i in range(3):
|
||||
assert d[3][i] == [S.Zero]*9
|
||||
assert d[3][4] == [S.Zero, -S.One, S.One, S.Half, Rational(-1, 2)] + [S.Zero]*4
|
||||
assert d[3][6] == [S.Zero, Rational(-13, 8), Rational(13, 8), S.One, -S.One,
|
||||
Rational(-1, 8), Rational(1, 8)] + [S.Zero]*2
|
||||
assert d[3][8] == [S.Zero, Rational(-61, 30), Rational(61, 30), Rational(169, 120), Rational(-169, 120),
|
||||
Rational(-3, 10), Rational(3, 10), Rational(7, 240), Rational(-7, 240)]
|
||||
assert d[3][i] == [S(0)]*9
|
||||
assert d[3][4] == [S(0), -S(1), S(1), S(1)/2, -S(1)/2] + [S(0)]*4
|
||||
assert d[3][6] == [S(0), -S(13)/8, S(13)/8, S(1), -S(1),
|
||||
-S(1)/8, S(1)/8] + [S(0)]*2
|
||||
assert d[3][8] == [S(0), -S(61)/30, S(61)/30, S(169)/120, -S(169)/120,
|
||||
-S(3)/10, S(3)/10, S(7)/240, -S(7)/240]
|
||||
|
||||
# Fourth derivative
|
||||
for i in range(4):
|
||||
assert d[4][i] == [S.Zero]*9
|
||||
assert d[4][4] == [S(6), -S(4), -S(4), S.One, S.One] + [S.Zero]*4
|
||||
assert d[4][6] == [Rational(28, 3), Rational(-13, 2), Rational(-13, 2), S(2), S(2),
|
||||
Rational(-1, 6), Rational(-1, 6)] + [S.Zero]*2
|
||||
assert d[4][8] == [Rational(91, 8), Rational(-122, 15), Rational(-122, 15), Rational(169, 60), Rational(169, 60),
|
||||
Rational(-2, 5), Rational(-2, 5), Rational(7, 240), Rational(7, 240)]
|
||||
assert d[4][i] == [S(0)]*9
|
||||
assert d[4][4] == [S(6), -S(4), -S(4), S(1), S(1)] + [S(0)]*4
|
||||
assert d[4][6] == [S(28)/3, -S(13)/2, -S(13)/2, S(2), S(2),
|
||||
-S(1)/6, -S(1)/6] + [S(0)]*2
|
||||
assert d[4][8] == [S(91)/8, -S(122)/15, -S(122)/15, S(169)/60, S(169)/60,
|
||||
-S(2)/5, -S(2)/5, S(7)/240, S(7)/240]
|
||||
|
||||
# Table 2, p. 703 in doi:10.1090/S0025-5718-1988-0935077-0
|
||||
# --------------------------------------------------------
|
||||
@@ -83,62 +82,47 @@ def test_finite_diff_weights():
|
||||
i in range(4)]
|
||||
|
||||
# Zeroth derivative
|
||||
assert d[0][0][1] == [S.Half, S.Half]
|
||||
assert d[1][0][3] == [Rational(-1, 16), Rational(9, 16), Rational(9, 16), Rational(-1, 16)]
|
||||
assert d[2][0][5] == [Rational(3, 256), Rational(-25, 256), Rational(75, 128), Rational(75, 128),
|
||||
Rational(-25, 256), Rational(3, 256)]
|
||||
assert d[3][0][7] == [Rational(-5, 2048), Rational(49, 2048), Rational(-245, 2048), Rational(1225, 2048),
|
||||
Rational(1225, 2048), Rational(-245, 2048), Rational(49, 2048), Rational(-5, 2048)]
|
||||
assert d[0][0][1] == [S(1)/2, S(1)/2]
|
||||
assert d[1][0][3] == [-S(1)/16, S(9)/16, S(9)/16, -S(1)/16]
|
||||
assert d[2][0][5] == [S(3)/256, -S(25)/256, S(75)/128, S(75)/128,
|
||||
-S(25)/256, S(3)/256]
|
||||
assert d[3][0][7] == [-S(5)/2048, S(49)/2048, -S(245)/2048, S(1225)/2048,
|
||||
S(1225)/2048, -S(245)/2048, S(49)/2048, -S(5)/2048]
|
||||
|
||||
# First derivative
|
||||
assert d[0][1][1] == [-S.One, S.One]
|
||||
assert d[1][1][3] == [Rational(1, 24), Rational(-9, 8), Rational(9, 8), Rational(-1, 24)]
|
||||
assert d[2][1][5] == [Rational(-3, 640), Rational(25, 384), Rational(-75, 64),
|
||||
Rational(75, 64), Rational(-25, 384), Rational(3, 640)]
|
||||
assert d[3][1][7] == [Rational(5, 7168), Rational(-49, 5120),
|
||||
Rational(245, 3072), Rational(-1225, 1024),
|
||||
Rational(1225, 1024), Rational(-245, 3072),
|
||||
Rational(49, 5120), Rational(-5, 7168)]
|
||||
assert d[0][1][1] == [-S(1), S(1)]
|
||||
assert d[1][1][3] == [S(1)/24, -S(9)/8, S(9)/8, -S(1)/24]
|
||||
assert d[2][1][5] == [-S(3)/640, S(25)/384, -S(75)/64, S(75)/64,
|
||||
-S(25)/384, S(3)/640]
|
||||
assert d[3][1][7] == [S(5)/7168, -S(49)/5120, S(245)/3072, S(-1225)/1024,
|
||||
S(1225)/1024, -S(245)/3072, S(49)/5120, -S(5)/7168]
|
||||
|
||||
# Reasonably the rest of the table is also correct... (testing of that
|
||||
# deemed excessive at the moment)
|
||||
raises(ValueError, lambda: finite_diff_weights(-1, [1, 2]))
|
||||
raises(ValueError, lambda: finite_diff_weights(1.2, [1, 2]))
|
||||
x = symbols('x')
|
||||
raises(ValueError, lambda: finite_diff_weights(x, [1, 2]))
|
||||
|
||||
|
||||
def test_as_finite_diff():
|
||||
x = symbols('x')
|
||||
f = Function('f')
|
||||
dx = Function('dx')
|
||||
|
||||
with warns_deprecated_sympy():
|
||||
as_finite_diff(f(x).diff(x), [x-2, x-1, x, x+1, x+2])
|
||||
|
||||
# Use of undefined functions in ``points``
|
||||
df_true = -f(x+dx(x)/2-dx(x+dx(x)/2)/2) / dx(x+dx(x)/2) \
|
||||
+ f(x+dx(x)/2+dx(x+dx(x)/2)/2) / dx(x+dx(x)/2)
|
||||
df_test = diff(f(x), x).as_finite_difference(points=dx(x), x0=x+dx(x)/2)
|
||||
assert (df_test - df_true).simplify() == 0
|
||||
|
||||
|
||||
def test_differentiate_finite():
|
||||
x, y = symbols('x y')
|
||||
f = Function('f')
|
||||
res0 = differentiate_finite(f(x, y) + exp(42), x, y, evaluate=True)
|
||||
xm, xp, ym, yp = [v + sign*S.Half for v, sign in product([x, y], [-1, 1])]
|
||||
xm, xp, ym, yp = [v + sign*S(1)/2 for v, sign in product([x, y], [-1, 1])]
|
||||
ref0 = f(xm, ym) + f(xp, yp) - f(xm, yp) - f(xp, ym)
|
||||
assert (res0 - ref0).simplify() == 0
|
||||
|
||||
g = Function('g')
|
||||
res1 = differentiate_finite(f(x)*g(x) + 42, x, evaluate=True)
|
||||
ref1 = (-f(x - S.Half) + f(x + S.Half))*g(x) + \
|
||||
(-g(x - S.Half) + g(x + S.Half))*f(x)
|
||||
ref1 = (-f(x - S(1)/2) + f(x + S(1)/2))*g(x) + \
|
||||
(-g(x - S(1)/2) + g(x + S(1)/2))*f(x)
|
||||
assert (res1 - ref1).simplify() == 0
|
||||
|
||||
res2 = differentiate_finite(f(x) + x**3 + 42, x, points=[x-1, x+1])
|
||||
ref2 = (f(x + 1) + (x + 1)**3 - f(x - 1) - (x - 1)**3)/2
|
||||
assert (res2 - ref2).simplify() == 0
|
||||
raises(ValueError, lambda: differentiate_finite(f(x)*g(x), x,
|
||||
pints=[x-1, x+1]))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sympy import Symbol, exp, log, oo, S, I, sqrt, Rational
|
||||
from sympy import Symbol, exp, log, oo, S, I, sqrt
|
||||
from sympy.calculus.singularities import (
|
||||
singularities,
|
||||
is_increasing,
|
||||
@@ -8,7 +8,7 @@ from sympy.calculus.singularities import (
|
||||
is_monotonic
|
||||
)
|
||||
from sympy.sets import Interval, FiniteSet
|
||||
from sympy.utilities.pytest import XFAIL, raises
|
||||
from sympy.utilities.pytest import XFAIL
|
||||
from sympy.abc import x, y
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ def test_is_decreasing():
|
||||
|
||||
assert is_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3))
|
||||
assert is_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
|
||||
assert not is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, Rational(3, 2)))
|
||||
assert not is_decreasing(1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2))
|
||||
assert not is_decreasing(-x**2, Interval(-oo, 0))
|
||||
assert not is_decreasing(-x**2*b, Interval(-oo, 0), x)
|
||||
|
||||
@@ -74,7 +74,7 @@ def test_is_strictly_decreasing():
|
||||
"""Test whether is_strictly_decreasing returns correct value."""
|
||||
assert is_strictly_decreasing(1/(x**2 - 3*x), Interval.Lopen(3, oo))
|
||||
assert not is_strictly_decreasing(
|
||||
1/(x**2 - 3*x), Interval.Ropen(-oo, Rational(3, 2)))
|
||||
1/(x**2 - 3*x), Interval.Ropen(-oo, S(3)/2))
|
||||
assert not is_strictly_decreasing(-x**2, Interval(-oo, 0))
|
||||
assert not is_strictly_decreasing(1)
|
||||
assert is_strictly_decreasing(1/(x**2 - 3*x), Interval.open(1.5, 3))
|
||||
@@ -87,4 +87,3 @@ def test_is_monotonic():
|
||||
assert is_monotonic(x**3 - 3*x**2 + 4*x, S.Reals)
|
||||
assert not is_monotonic(-x**2, S.Reals)
|
||||
assert is_monotonic(x**2 + y + 1, Interval(1, 2), x)
|
||||
raises(NotImplementedError, lambda: is_monotonic(x**2 + y + 1))
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from sympy import (Symbol, S, exp, log, sqrt, oo, E, zoo, pi, tan, sin, cos,
|
||||
cot, sec, csc, Abs, symbols, I, re, simplify,
|
||||
expint, Rational)
|
||||
cot, sec, csc, Abs, symbols, I, re, Lambda, simplify,
|
||||
ImageSet)
|
||||
from sympy.calculus.util import (function_range, continuous_domain, not_empty_in,
|
||||
periodicity, lcim, AccumBounds, is_convex,
|
||||
stationary_points, minimum, maximum)
|
||||
from sympy.core import Add, Mul, Pow
|
||||
from sympy.sets.sets import (Interval, FiniteSet, EmptySet, Complement,
|
||||
Union)
|
||||
Union, Intersection)
|
||||
from sympy.utilities.pytest import raises
|
||||
from sympy.abc import x
|
||||
|
||||
@@ -24,7 +24,7 @@ def test_function_range():
|
||||
assert function_range(tan(x), x, Interval(pi/2, pi)
|
||||
) == Interval(-oo, 0)
|
||||
assert function_range((x + 3)/(x - 2), x, Interval(-5, 5)
|
||||
) == Union(Interval(-oo, Rational(2, 7)), Interval(Rational(8, 3), oo))
|
||||
) == Union(Interval(-oo, S(2)/7), Interval(S(8)/3, oo))
|
||||
assert function_range(1/(x**2), x, Interval(-1, 1)
|
||||
) == Interval(1, oo)
|
||||
assert function_range(exp(x), x, Interval(-1, 1)
|
||||
@@ -41,11 +41,8 @@ def test_function_range():
|
||||
) == Union(Interval(-sin(3), 1), FiniteSet(sin(4)))
|
||||
assert function_range(cos(x), x, Interval(-oo, -4)
|
||||
) == Interval(-1, 1)
|
||||
assert function_range(cos(x), x, S.EmptySet) == S.EmptySet
|
||||
raises(NotImplementedError, lambda : function_range(
|
||||
exp(x)*(sin(x) - cos(x))/2 - x, x, S.Reals))
|
||||
raises(NotImplementedError, lambda : function_range(
|
||||
sin(x) + x, x, S.Reals)) # issue 13273
|
||||
raises(NotImplementedError, lambda : function_range(
|
||||
log(x), x, S.Integers))
|
||||
raises(NotImplementedError, lambda : function_range(
|
||||
@@ -56,12 +53,12 @@ def test_continuous_domain():
|
||||
x = Symbol('x')
|
||||
assert continuous_domain(sin(x), x, Interval(0, 2*pi)) == Interval(0, 2*pi)
|
||||
assert continuous_domain(tan(x), x, Interval(0, 2*pi)) == \
|
||||
Union(Interval(0, pi/2, False, True), Interval(pi/2, pi*Rational(3, 2), True, True),
|
||||
Interval(pi*Rational(3, 2), 2*pi, True, False))
|
||||
Union(Interval(0, pi/2, False, True), Interval(pi/2, 3*pi/2, True, True),
|
||||
Interval(3*pi/2, 2*pi, True, False))
|
||||
assert continuous_domain((x - 1)/((x - 1)**2), x, S.Reals) == \
|
||||
Union(Interval(-oo, 1, True, True), Interval(1, oo, True, True))
|
||||
assert continuous_domain(log(x) + log(4*x - 1), x, S.Reals) == \
|
||||
Interval(Rational(1, 4), oo, True, True)
|
||||
Interval(S(1)/4, oo, True, True)
|
||||
assert continuous_domain(1/sqrt(x - 3), x, S.Reals) == Interval(3, oo, True, True)
|
||||
assert continuous_domain(1/x - 2, x, S.Reals) == \
|
||||
Union(Interval.open(-oo, 0), Interval.open(0, oo))
|
||||
@@ -71,12 +68,12 @@ def test_continuous_domain():
|
||||
|
||||
def test_not_empty_in():
|
||||
assert not_empty_in(FiniteSet(x, 2*x).intersect(Interval(1, 2, True, False)), x) == \
|
||||
Interval(S.Half, 2, True, False)
|
||||
Interval(S(1)/2, 2, True, False)
|
||||
assert not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x) == \
|
||||
Union(Interval(-sqrt(2), -1), Interval(1, 2))
|
||||
assert not_empty_in(FiniteSet(x**2 + x, x).intersect(Interval(2, 4)), x) == \
|
||||
Union(Interval(-sqrt(17)/2 - S.Half, -2),
|
||||
Interval(1, Rational(-1, 2) + sqrt(17)/2), Interval(2, 4))
|
||||
Union(Interval(-sqrt(17)/2 - S(1)/2, -2),
|
||||
Interval(1, -S(1)/2 + sqrt(17)/2), Interval(2, 4))
|
||||
assert not_empty_in(FiniteSet(x/(x - 1)).intersect(S.Reals), x) == \
|
||||
Complement(S.Reals, FiniteSet(1))
|
||||
assert not_empty_in(FiniteSet(a/(a - 1)).intersect(S.Reals), a) == \
|
||||
@@ -84,9 +81,7 @@ def test_not_empty_in():
|
||||
assert not_empty_in(FiniteSet((x**2 - 3*x + 2)/(x - 1)).intersect(S.Reals), x) == \
|
||||
Complement(S.Reals, FiniteSet(1))
|
||||
assert not_empty_in(FiniteSet(3, 4, x/(x - 1)).intersect(Interval(2, 3)), x) == \
|
||||
Interval(-oo, oo)
|
||||
assert not_empty_in(FiniteSet(4, x/(x - 1)).intersect(Interval(2, 3)), x) == \
|
||||
Interval(S(3)/2, 2)
|
||||
Union(Interval(S(3)/2, 2), FiniteSet(3))
|
||||
assert not_empty_in(FiniteSet(x/(x**2 - 1)).intersect(S.Reals), x) == \
|
||||
Complement(S.Reals, FiniteSet(-1, 1))
|
||||
assert not_empty_in(FiniteSet(x, x**2).intersect(Union(Interval(1, 3, True, True),
|
||||
@@ -96,10 +91,6 @@ def test_not_empty_in():
|
||||
assert not_empty_in(FiniteSet(1).intersect(Interval(3, 4)), x) == S.EmptySet
|
||||
assert not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x) == \
|
||||
Union(Interval(-2, -1, True, False), Interval(2, oo))
|
||||
raises(ValueError, lambda: not_empty_in(x))
|
||||
raises(ValueError, lambda: not_empty_in(Interval(0, 1), x))
|
||||
raises(NotImplementedError,
|
||||
lambda: not_empty_in(FiniteSet(x).intersect(S.Reals), x, a))
|
||||
|
||||
|
||||
def test_periodicity():
|
||||
@@ -127,12 +118,12 @@ def test_periodicity():
|
||||
assert periodicity(2*tan(x)**2, x) == pi
|
||||
assert periodicity(sin(x%4), x) == 4
|
||||
assert periodicity(sin(x)%4, x) == 2*pi
|
||||
assert periodicity(tan((3*x-2)%4), x) == Rational(4, 3)
|
||||
assert periodicity(tan((3*x-2)%4), x) == S(4)/3
|
||||
assert periodicity((sqrt(2)*(x+1)+x) % 3, x) == 3 / (sqrt(2)+1)
|
||||
assert periodicity((x**2+1) % x, x) is None
|
||||
assert periodicity((x**2+1) % x, x) == None
|
||||
assert periodicity(sin(re(x)), x) == 2*pi
|
||||
assert periodicity(sin(x)**2 + cos(x)**2, x) is S.Zero
|
||||
assert periodicity(tan(x), y) is S.Zero
|
||||
assert periodicity(sin(x)**2 + cos(x)**2, x) == S.Zero
|
||||
assert periodicity(tan(x), y) == S.Zero
|
||||
assert periodicity(sin(x) + I*cos(x), x) == 2*pi
|
||||
assert periodicity(x - sin(2*y), y) == pi
|
||||
|
||||
@@ -165,9 +156,6 @@ def test_periodicity():
|
||||
assert periodicity((x**2 + 4)%2, x) is None
|
||||
assert periodicity((E**x)%3, x) is None
|
||||
|
||||
assert periodicity(sin(expint(1, x))/expint(1, x), x) is None
|
||||
|
||||
|
||||
def test_periodicity_check():
|
||||
x = Symbol('x')
|
||||
y = Symbol('y')
|
||||
@@ -182,20 +170,18 @@ def test_periodicity_check():
|
||||
def test_lcim():
|
||||
from sympy import pi
|
||||
|
||||
assert lcim([S.Half, S(2), S(3)]) == 6
|
||||
assert lcim([S(1)/2, S(2), S(3)]) == 6
|
||||
assert lcim([pi/2, pi/4, pi]) == pi
|
||||
assert lcim([2*pi, pi/2]) == 2*pi
|
||||
assert lcim([S.One, 2*pi]) is None
|
||||
assert lcim([S(2) + 2*E, E/3 + Rational(1, 3), S.One + E]) == S(2) + 2*E
|
||||
|
||||
assert lcim([S(1), 2*pi]) is None
|
||||
assert lcim([S(2) + 2*E, E/3 + S(1)/3, S(1) + E]) == S(2) + 2*E
|
||||
|
||||
def test_is_convex():
|
||||
|
||||
assert is_convex(1/x, x, domain=Interval(0, oo)) == True
|
||||
assert is_convex(1/x, x, domain=Interval(-oo, 0)) == False
|
||||
assert is_convex(x**2, x, domain=Interval(0, oo)) == True
|
||||
assert is_convex(log(x), x) == False
|
||||
raises(NotImplementedError, lambda: is_convex(log(x), x, a))
|
||||
|
||||
|
||||
def test_stationary_points():
|
||||
x, y = symbols('x y')
|
||||
@@ -207,11 +193,11 @@ def test_stationary_points():
|
||||
assert stationary_points(tan(x), x,
|
||||
) == EmptySet()
|
||||
assert stationary_points(sin(x)*cos(x), x, Interval(0, pi)
|
||||
) == {pi/4, pi*Rational(3, 4)}
|
||||
) == {pi/4, 3*pi/4}
|
||||
assert stationary_points(sec(x), x, Interval(0, pi)
|
||||
) == {0, pi}
|
||||
assert stationary_points((x+3)*(x-2), x
|
||||
) == FiniteSet(Rational(-1, 2))
|
||||
) == FiniteSet(-S.Half)
|
||||
assert stationary_points((x + 3)/(x - 2), x, Interval(-5, 5)
|
||||
) == EmptySet()
|
||||
assert stationary_points((x**2+3)/(x-2), x
|
||||
@@ -219,7 +205,7 @@ def test_stationary_points():
|
||||
assert stationary_points((x**2+3)/(x-2), x, Interval(0, 5)
|
||||
) == {2 + sqrt(7)}
|
||||
assert stationary_points(x**4 + x**3 - 5*x**2, x, S.Reals
|
||||
) == FiniteSet(-2, 0, Rational(5, 4))
|
||||
) == FiniteSet(-2, 0, S(5)/4)
|
||||
assert stationary_points(exp(x), x
|
||||
) == EmptySet()
|
||||
assert stationary_points(log(x) - x, x, S.Reals
|
||||
@@ -228,27 +214,25 @@ def test_stationary_points():
|
||||
) == {0, -pi, pi}
|
||||
assert stationary_points(y, x, S.Reals
|
||||
) == S.Reals
|
||||
assert stationary_points(y, x, S.EmptySet) == S.EmptySet
|
||||
|
||||
|
||||
def test_maximum():
|
||||
x, y = symbols('x y')
|
||||
assert maximum(sin(x), x) is S.One
|
||||
assert maximum(sin(x), x) == S.One
|
||||
assert maximum(sin(x), x, Interval(0, 1)) == sin(1)
|
||||
assert maximum(tan(x), x) is oo
|
||||
assert maximum(tan(x), x, Interval(-pi/4, pi/4)) is S.One
|
||||
assert maximum(tan(x), x) == oo
|
||||
assert maximum(tan(x), x, Interval(-pi/4, pi/4)) == S.One
|
||||
assert maximum(sin(x)*cos(x), x, S.Reals) == S.Half
|
||||
assert simplify(maximum(sin(x)*cos(x), x, Interval(pi*Rational(3, 8), pi*Rational(5, 8)))
|
||||
assert simplify(maximum(sin(x)*cos(x), x, Interval(3*pi/8, 5*pi/8))
|
||||
) == sqrt(2)/4
|
||||
assert maximum((x+3)*(x-2), x) is oo
|
||||
assert maximum((x+3)*(x-2), x) == oo
|
||||
assert maximum((x+3)*(x-2), x, Interval(-5, 0)) == S(14)
|
||||
assert maximum((x+3)/(x-2), x, Interval(-5, 0)) == Rational(2, 7)
|
||||
assert maximum((x+3)/(x-2), x, Interval(-5, 0)) == S(2)/7
|
||||
assert simplify(maximum(-x**4-x**3+x**2+10, x)
|
||||
) == 41*sqrt(41)/512 + Rational(5419, 512)
|
||||
) == 41*sqrt(41)/512 + S(5419)/512
|
||||
assert maximum(exp(x), x, Interval(-oo, 2)) == exp(2)
|
||||
assert maximum(log(x) - x, x, S.Reals) is S.NegativeOne
|
||||
assert maximum(log(x) - x, x, S.Reals) == -S.One
|
||||
assert maximum(cos(x), x, Union(Interval(0, 5), Interval(-6, -3))
|
||||
) is S.One
|
||||
) == S.One
|
||||
assert maximum(cos(x)-sin(x), x, S.Reals) == sqrt(2)
|
||||
assert maximum(y, x, S.Reals) == y
|
||||
|
||||
@@ -257,26 +241,25 @@ def test_maximum():
|
||||
raises(ValueError, lambda : maximum(1/(x**2 + y**2 + 1), x, S.EmptySet))
|
||||
raises(ValueError, lambda : maximum(sin(x), sin(x)))
|
||||
raises(ValueError, lambda : maximum(sin(x), x*y, S.EmptySet))
|
||||
raises(ValueError, lambda : maximum(sin(x), S.One))
|
||||
|
||||
raises(ValueError, lambda : maximum(sin(x), S(1)))
|
||||
|
||||
def test_minimum():
|
||||
x, y = symbols('x y')
|
||||
|
||||
assert minimum(sin(x), x) is S.NegativeOne
|
||||
assert minimum(sin(x), x) == -S.One
|
||||
assert minimum(sin(x), x, Interval(1, 4)) == sin(4)
|
||||
assert minimum(tan(x), x) is -oo
|
||||
assert minimum(tan(x), x, Interval(-pi/4, pi/4)) is S.NegativeOne
|
||||
assert minimum(sin(x)*cos(x), x, S.Reals) == Rational(-1, 2)
|
||||
assert simplify(minimum(sin(x)*cos(x), x, Interval(pi*Rational(3, 8), pi*Rational(5, 8)))
|
||||
assert minimum(tan(x), x) == -oo
|
||||
assert minimum(tan(x), x, Interval(-pi/4, pi/4)) == -S.One
|
||||
assert minimum(sin(x)*cos(x), x, S.Reals) == -S.Half
|
||||
assert simplify(minimum(sin(x)*cos(x), x, Interval(3*pi/8, 5*pi/8))
|
||||
) == -sqrt(2)/4
|
||||
assert minimum((x+3)*(x-2), x) == Rational(-25, 4)
|
||||
assert minimum((x+3)/(x-2), x, Interval(-5, 0)) == Rational(-3, 2)
|
||||
assert minimum((x+3)*(x-2), x) == -S(25)/4
|
||||
assert minimum((x+3)/(x-2), x, Interval(-5, 0)) == -S(3)/2
|
||||
assert minimum(x**4-x**3+x**2+10, x) == S(10)
|
||||
assert minimum(exp(x), x, Interval(-2, oo)) == exp(-2)
|
||||
assert minimum(log(x) - x, x, S.Reals) is -oo
|
||||
assert minimum(log(x) - x, x, S.Reals) == -oo
|
||||
assert minimum(cos(x), x, Union(Interval(0, 5), Interval(-6, -3))
|
||||
) is S.NegativeOne
|
||||
) == -S.One
|
||||
assert minimum(cos(x)-sin(x), x, S.Reals) == -sqrt(2)
|
||||
assert minimum(y, x, S.Reals) == y
|
||||
|
||||
@@ -285,16 +268,15 @@ def test_minimum():
|
||||
raises(ValueError, lambda : minimum(1/(x**2 + y**2 + 1), x, S.EmptySet))
|
||||
raises(ValueError, lambda : minimum(sin(x), sin(x)))
|
||||
raises(ValueError, lambda : minimum(sin(x), x*y, S.EmptySet))
|
||||
raises(ValueError, lambda : minimum(sin(x), S.One))
|
||||
|
||||
raises(ValueError, lambda : minimum(sin(x), S(1)))
|
||||
|
||||
def test_AccumBounds():
|
||||
assert AccumBounds(1, 2).args == (1, 2)
|
||||
assert AccumBounds(1, 2).delta is S.One
|
||||
assert AccumBounds(1, 2).mid == Rational(3, 2)
|
||||
assert AccumBounds(1, 2).delta == S(1)
|
||||
assert AccumBounds(1, 2).mid == S(3)/2
|
||||
assert AccumBounds(1, 3).is_real == True
|
||||
|
||||
assert AccumBounds(1, 1) is S.One
|
||||
assert AccumBounds(1, 1) == S(1)
|
||||
|
||||
assert AccumBounds(1, 2) + 1 == AccumBounds(2, 3)
|
||||
assert 1 + AccumBounds(1, 2) == AccumBounds(2, 3)
|
||||
@@ -311,27 +293,24 @@ def test_AccumBounds():
|
||||
assert AccumBounds(1, 2) - x == Add(AccumBounds(1, 2), -x)
|
||||
|
||||
assert AccumBounds(-oo, 1) + oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(1, oo) + oo is oo
|
||||
assert AccumBounds(1, oo) + oo == oo
|
||||
assert AccumBounds(1, oo) - oo == AccumBounds(-oo, oo)
|
||||
assert (-oo - AccumBounds(-1, oo)) is -oo
|
||||
assert AccumBounds(-oo, 1) - oo is -oo
|
||||
assert (-oo - AccumBounds(-1, oo)) == -oo
|
||||
assert AccumBounds(-oo, 1) - oo == -oo
|
||||
|
||||
assert AccumBounds(1, oo) - oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-oo, 1) - (-oo) == AccumBounds(-oo, oo)
|
||||
assert (oo - AccumBounds(1, oo)) == AccumBounds(-oo, oo)
|
||||
assert (-oo - AccumBounds(1, oo)) is -oo
|
||||
assert (-oo - AccumBounds(1, oo)) == -oo
|
||||
|
||||
assert AccumBounds(1, 2)/2 == AccumBounds(S.Half, 1)
|
||||
assert 2/AccumBounds(2, 3) == AccumBounds(Rational(2, 3), 1)
|
||||
assert AccumBounds(1, 2)/2 == AccumBounds(S(1)/2, 1)
|
||||
assert 2/AccumBounds(2, 3) == AccumBounds(S(2)/3, 1)
|
||||
assert 1/AccumBounds(-1, 1) == AccumBounds(-oo, oo)
|
||||
|
||||
assert abs(AccumBounds(1, 2)) == AccumBounds(1, 2)
|
||||
assert abs(AccumBounds(-2, -1)) == AccumBounds(1, 2)
|
||||
assert abs(AccumBounds(-2, 1)) == AccumBounds(0, 2)
|
||||
assert abs(AccumBounds(-1, 2)) == AccumBounds(0, 2)
|
||||
c = Symbol('c')
|
||||
raises(ValueError, lambda: AccumBounds(0, c))
|
||||
raises(ValueError, lambda: AccumBounds(1, -1))
|
||||
|
||||
|
||||
def test_AccumBounds_mul():
|
||||
@@ -356,26 +335,26 @@ def test_AccumBounds_mul():
|
||||
|
||||
|
||||
def test_AccumBounds_div():
|
||||
assert AccumBounds(-1, 3)/AccumBounds(3, 4) == AccumBounds(Rational(-1, 3), 1)
|
||||
assert AccumBounds(-1, 3)/AccumBounds(3, 4) == AccumBounds(-S(1)/3, 1)
|
||||
assert AccumBounds(-2, 4)/AccumBounds(-3, 4) == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-3, -2)/AccumBounds(-4, 0) == AccumBounds(S.Half, oo)
|
||||
assert AccumBounds(-3, -2)/AccumBounds(-4, 0) == AccumBounds(S(1)/2, oo)
|
||||
|
||||
# these two tests can have a better answer
|
||||
# after Union of AccumBounds is improved
|
||||
assert AccumBounds(-3, -2)/AccumBounds(-2, 1) == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(2, 3)/AccumBounds(-2, 2) == AccumBounds(-oo, oo)
|
||||
|
||||
assert AccumBounds(-3, -2)/AccumBounds(0, 4) == AccumBounds(-oo, Rational(-1, 2))
|
||||
assert AccumBounds(2, 4)/AccumBounds(-3, 0) == AccumBounds(-oo, Rational(-2, 3))
|
||||
assert AccumBounds(2, 4)/AccumBounds(0, 3) == AccumBounds(Rational(2, 3), oo)
|
||||
assert AccumBounds(-3, -2)/AccumBounds(0, 4) == AccumBounds(-oo, -S(1)/2)
|
||||
assert AccumBounds(2, 4)/AccumBounds(-3, 0) == AccumBounds(-oo, -S(2)/3)
|
||||
assert AccumBounds(2, 4)/AccumBounds(0, 3) == AccumBounds(S(2)/3, oo)
|
||||
|
||||
assert AccumBounds(0, 1)/AccumBounds(0, 1) == AccumBounds(0, oo)
|
||||
assert AccumBounds(-1, 0)/AccumBounds(0, 1) == AccumBounds(-oo, 0)
|
||||
assert AccumBounds(-1, 2)/AccumBounds(-2, 2) == AccumBounds(-oo, oo)
|
||||
|
||||
assert 1/AccumBounds(-1, 2) == AccumBounds(-oo, oo)
|
||||
assert 1/AccumBounds(0, 2) == AccumBounds(S.Half, oo)
|
||||
assert (-1)/AccumBounds(0, 2) == AccumBounds(-oo, Rational(-1, 2))
|
||||
assert 1/AccumBounds(0, 2) == AccumBounds(S(1)/2, oo)
|
||||
assert (-1)/AccumBounds(0, 2) == AccumBounds(-oo, -S(1)/2)
|
||||
assert 1/AccumBounds(-oo, 0) == AccumBounds(-oo, 0)
|
||||
assert 1/AccumBounds(-1, 0) == AccumBounds(-oo, -1)
|
||||
assert (-2)/AccumBounds(-oo, 0) == AccumBounds(0, oo)
|
||||
@@ -410,51 +389,51 @@ def test_AccumBounds_pow():
|
||||
assert AccumBounds(-1, 2)**3 == AccumBounds(-1, 8)
|
||||
assert AccumBounds(-1, 1)**0 == 1
|
||||
|
||||
assert AccumBounds(1, 2)**Rational(5, 2) == AccumBounds(1, 4*sqrt(2))
|
||||
assert AccumBounds(-1, 2)**Rational(1, 3) == AccumBounds(-1, 2**Rational(1, 3))
|
||||
assert AccumBounds(0, 2)**S.Half == AccumBounds(0, sqrt(2))
|
||||
assert AccumBounds(1, 2)**(S(5)/2) == AccumBounds(1, 4*sqrt(2))
|
||||
assert AccumBounds(-1, 2)**(S(1)/3) == AccumBounds(-1, 2**(S(1)/3))
|
||||
assert AccumBounds(0, 2)**(S(1)/2) == AccumBounds(0, sqrt(2))
|
||||
|
||||
assert AccumBounds(-4, 2)**Rational(2, 3) == AccumBounds(0, 2*2**Rational(1, 3))
|
||||
assert AccumBounds(-4, 2)**(S(2)/3) == AccumBounds(0, 2*2**(S(1)/3))
|
||||
|
||||
assert AccumBounds(-1, 5)**S.Half == AccumBounds(0, sqrt(5))
|
||||
assert AccumBounds(-oo, 2)**S.Half == AccumBounds(0, sqrt(2))
|
||||
assert AccumBounds(-2, 3)**Rational(-1, 4) == AccumBounds(0, oo)
|
||||
assert AccumBounds(-1, 5)**(S(1)/2) == AccumBounds(0, sqrt(5))
|
||||
assert AccumBounds(-oo, 2)**(S(1)/2) == AccumBounds(0, sqrt(2))
|
||||
assert AccumBounds(-2, 3)**(S(-1)/4) == AccumBounds(0, oo)
|
||||
|
||||
assert AccumBounds(1, 5)**(-2) == AccumBounds(Rational(1, 25), 1)
|
||||
assert AccumBounds(1, 5)**(-2) == AccumBounds(S(1)/25, 1)
|
||||
assert AccumBounds(-1, 3)**(-2) == AccumBounds(0, oo)
|
||||
assert AccumBounds(0, 2)**(-2) == AccumBounds(Rational(1, 4), oo)
|
||||
assert AccumBounds(0, 2)**(-2) == AccumBounds(S(1)/4, oo)
|
||||
assert AccumBounds(-1, 2)**(-3) == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-3, -2)**(-3) == AccumBounds(Rational(-1, 8), Rational(-1, 27))
|
||||
assert AccumBounds(-3, -2)**(-2) == AccumBounds(Rational(1, 9), Rational(1, 4))
|
||||
assert AccumBounds(0, oo)**S.Half == AccumBounds(0, oo)
|
||||
assert AccumBounds(-oo, -1)**Rational(1, 3) == AccumBounds(-oo, -1)
|
||||
assert AccumBounds(-2, 3)**(Rational(-1, 3)) == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-3, -2)**(-3) == AccumBounds(S(-1)/8, -S(1)/27)
|
||||
assert AccumBounds(-3, -2)**(-2) == AccumBounds(S(1)/9, S(1)/4)
|
||||
assert AccumBounds(0, oo)**(S(1)/2) == AccumBounds(0, oo)
|
||||
assert AccumBounds(-oo, -1)**(S(1)/3) == AccumBounds(-oo, -1)
|
||||
assert AccumBounds(-2, 3)**(-S(1)/3) == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-oo, 0)**(-2) == AccumBounds(0, oo)
|
||||
assert AccumBounds(-2, 0)**(-2) == AccumBounds(Rational(1, 4), oo)
|
||||
assert AccumBounds(-2, 0)**(-2) == AccumBounds(S(1)/4, oo)
|
||||
|
||||
assert AccumBounds(Rational(1, 3), S.Half)**oo is S.Zero
|
||||
assert AccumBounds(0, S.Half)**oo is S.Zero
|
||||
assert AccumBounds(S.Half, 1)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(S(1)/3, S(1)/2)**oo == S(0)
|
||||
assert AccumBounds(0, S(1)/2)**oo == S(0)
|
||||
assert AccumBounds(S(1)/2, 1)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(0, 1)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(2, 3)**oo is oo
|
||||
assert AccumBounds(2, 3)**oo == oo
|
||||
assert AccumBounds(1, 2)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(S.Half, 3)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(Rational(-1, 3), Rational(-1, 4))**oo is S.Zero
|
||||
assert AccumBounds(-1, Rational(-1, 2))**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(S(1)/2, 3)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(-S(1)/3, -S(1)/4)**oo == S(0)
|
||||
assert AccumBounds(-1, -S(1)/2)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-3, -2)**oo == FiniteSet(-oo, oo)
|
||||
assert AccumBounds(-2, -1)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-2, Rational(-1, 2))**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(Rational(-1, 2), S.Half)**oo is S.Zero
|
||||
assert AccumBounds(Rational(-1, 2), 1)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(Rational(-2, 3), 2)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(-2, -S(1)/2)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-S(1)/2, S(1)/2)**oo == S(0)
|
||||
assert AccumBounds(-S(1)/2, 1)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(-S(2)/3, 2)**oo == AccumBounds(0, oo)
|
||||
assert AccumBounds(-1, 1)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-1, S.Half)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-1, S(1)/2)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-1, 2)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-2, S.Half)**oo == AccumBounds(-oo, oo)
|
||||
assert AccumBounds(-2, S(1)/2)**oo == AccumBounds(-oo, oo)
|
||||
|
||||
assert AccumBounds(1, 2)**x == Pow(AccumBounds(1, 2), x, evaluate=False)
|
||||
|
||||
assert AccumBounds(2, 3)**(-oo) is S.Zero
|
||||
assert AccumBounds(2, 3)**(-oo) == S(0)
|
||||
assert AccumBounds(0, 2)**(-oo) == AccumBounds(0, oo)
|
||||
assert AccumBounds(-1, 2)**(-oo) == AccumBounds(-oo, oo)
|
||||
|
||||
@@ -477,25 +456,9 @@ def test_comparison_AccumBounds():
|
||||
assert (AccumBounds(1, 3) < AccumBounds(2, 4)).rel_op == '<'
|
||||
assert (AccumBounds(1, 3) < AccumBounds(-2, 0)) == S.false
|
||||
|
||||
assert (AccumBounds(1, 3) <= AccumBounds(4, 6)) == S.true
|
||||
assert (AccumBounds(1, 3) <= AccumBounds(-2, 0)) == S.false
|
||||
|
||||
assert (AccumBounds(1, 3) > AccumBounds(4, 6)) == S.false
|
||||
assert (AccumBounds(1, 3) > AccumBounds(-2, 0)) == S.true
|
||||
|
||||
assert (AccumBounds(1, 3) >= AccumBounds(4, 6)) == S.false
|
||||
assert (AccumBounds(1, 3) >= AccumBounds(-2, 0)) == S.true
|
||||
|
||||
# issue 13499
|
||||
assert (cos(x) > 0).subs(x, oo) == (AccumBounds(-1, 1) > 0)
|
||||
|
||||
c = Symbol('c')
|
||||
raises(TypeError, lambda: (AccumBounds(0, 1) < c))
|
||||
raises(TypeError, lambda: (AccumBounds(0, 1) <= c))
|
||||
raises(TypeError, lambda: (AccumBounds(0, 1) > c))
|
||||
raises(TypeError, lambda: (AccumBounds(0, 1) >= c))
|
||||
|
||||
|
||||
def test_contains_AccumBounds():
|
||||
assert (1 in AccumBounds(1, 2)) == S.true
|
||||
raises(TypeError, lambda: a in AccumBounds(1, 2))
|
||||
@@ -510,26 +473,3 @@ def test_contains_AccumBounds():
|
||||
import itertools
|
||||
for perm in itertools.permutations([0, AccumBounds(-1, 1), x]):
|
||||
assert Mul(*perm) == 0
|
||||
|
||||
|
||||
def test_intersection_AccumBounds():
|
||||
assert AccumBounds(0, 3).intersection(AccumBounds(1, 2)) == AccumBounds(1, 2)
|
||||
assert AccumBounds(0, 3).intersection(AccumBounds(1, 4)) == AccumBounds(1, 3)
|
||||
assert AccumBounds(0, 3).intersection(AccumBounds(-1, 2)) == AccumBounds(0, 2)
|
||||
assert AccumBounds(0, 3).intersection(AccumBounds(-1, 4)) == AccumBounds(0, 3)
|
||||
assert AccumBounds(0, 1).intersection(AccumBounds(2, 3)) == S.EmptySet
|
||||
raises(TypeError, lambda: AccumBounds(0, 3).intersection(1))
|
||||
|
||||
|
||||
def test_union_AccumBounds():
|
||||
assert AccumBounds(0, 3).union(AccumBounds(1, 2)) == AccumBounds(0, 3)
|
||||
assert AccumBounds(0, 3).union(AccumBounds(1, 4)) == AccumBounds(0, 4)
|
||||
assert AccumBounds(0, 3).union(AccumBounds(-1, 2)) == AccumBounds(-1, 3)
|
||||
assert AccumBounds(0, 3).union(AccumBounds(-1, 4)) == AccumBounds(-1, 4)
|
||||
raises(TypeError, lambda: AccumBounds(0, 3).union(1))
|
||||
|
||||
|
||||
def test_issue_16469():
|
||||
x = Symbol("x", real=True)
|
||||
f = abs(x)
|
||||
assert function_range(f, x, S.Reals) == Interval(0, oo, False, True)
|
||||
|
||||
+55
-58
@@ -10,7 +10,6 @@ from sympy.logic.boolalg import And
|
||||
from sympy.polys.rationaltools import together
|
||||
from sympy.sets.sets import (Interval, Intersection, FiniteSet, Union,
|
||||
Complement, EmptySet)
|
||||
from sympy.sets.fancysets import ImageSet
|
||||
from sympy.simplify.radsimp import denom
|
||||
from sympy.solvers.inequalities import solve_univariate_inequality
|
||||
from sympy.utilities import filldedent
|
||||
@@ -162,7 +161,7 @@ def function_range(f, symbol, domain):
|
||||
return S.EmptySet
|
||||
|
||||
period = periodicity(f, symbol)
|
||||
if period == S.Zero:
|
||||
if period is S.Zero:
|
||||
# the expression is constant wrt symbol
|
||||
return FiniteSet(f.expand())
|
||||
|
||||
@@ -212,11 +211,7 @@ def function_range(f, symbol, domain):
|
||||
solution = solveset(f.diff(symbol), symbol, interval)
|
||||
|
||||
if not iterable(solution):
|
||||
raise NotImplementedError(
|
||||
'Unable to find critical points for {}'.format(f))
|
||||
if isinstance(solution, ImageSet):
|
||||
raise NotImplementedError(
|
||||
'Infinite number of critical points for {}'.format(f))
|
||||
raise NotImplementedError('Unable to find critical points for {}'.format(f))
|
||||
|
||||
critical_points += solution
|
||||
|
||||
@@ -274,7 +269,7 @@ def not_empty_in(finset_intersection, *syms):
|
||||
>>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)
|
||||
Interval(0, 2)
|
||||
>>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)
|
||||
Union(Interval(1, 2), Interval(-sqrt(2), -1))
|
||||
Union(Interval(-sqrt(2), -1), Interval(1, 2))
|
||||
>>> not_empty_in(FiniteSet(x**2/(x + 2)).intersect(Interval(1, oo)), x)
|
||||
Union(Interval.Lopen(-2, -1), Interval(2, oo))
|
||||
"""
|
||||
@@ -285,8 +280,8 @@ def not_empty_in(finset_intersection, *syms):
|
||||
if len(syms) == 0:
|
||||
raise ValueError("One or more symbols must be given in syms.")
|
||||
|
||||
if finset_intersection is S.EmptySet:
|
||||
return S.EmptySet
|
||||
if finset_intersection.is_EmptySet:
|
||||
return EmptySet()
|
||||
|
||||
if isinstance(finset_intersection, Union):
|
||||
elm_in_sets = finset_intersection.args[0]
|
||||
@@ -411,6 +406,7 @@ def periodicity(f, symbol, check=False):
|
||||
pi
|
||||
>>> periodicity(exp(x), x)
|
||||
"""
|
||||
from sympy.core.function import diff
|
||||
from sympy.core.mod import Mod
|
||||
from sympy.core.relational import Relational
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
@@ -419,7 +415,7 @@ def periodicity(f, symbol, check=False):
|
||||
TrigonometricFunction, sin, cos, csc, sec)
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.solvers.decompogen import decompogen
|
||||
from sympy.polys.polytools import degree
|
||||
from sympy.polys.polytools import degree, lcm_list
|
||||
|
||||
temp = Dummy('x', real=True)
|
||||
f = f.subs(symbol, temp)
|
||||
@@ -586,8 +582,7 @@ def _periodicity(args, symbol):
|
||||
if len(periods) > 1:
|
||||
return lcim(periods)
|
||||
|
||||
if periods:
|
||||
return periods[0]
|
||||
return periods[0]
|
||||
|
||||
|
||||
def lcim(numbers):
|
||||
@@ -1000,15 +995,17 @@ class AccumulationBounds(AtomicExpr):
|
||||
calculations, use ``mpmath.iv`` instead.
|
||||
"""
|
||||
|
||||
is_extended_real = True
|
||||
is_real = True
|
||||
|
||||
def __new__(cls, min, max):
|
||||
|
||||
min = _sympify(min)
|
||||
max = _sympify(max)
|
||||
|
||||
# Only allow real intervals (use symbols with 'is_extended_real=True').
|
||||
if not min.is_extended_real or not max.is_extended_real:
|
||||
inftys = [S.Infinity, S.NegativeInfinity]
|
||||
# Only allow real intervals (use symbols with 'is_real=True').
|
||||
if not (min.is_real or min in inftys) \
|
||||
or not (max.is_real or max in inftys):
|
||||
raise ValueError("Only real AccumulationBounds are supported")
|
||||
|
||||
# Make sure that the created AccumBounds object will be valid.
|
||||
@@ -1025,10 +1022,6 @@ class AccumulationBounds(AtomicExpr):
|
||||
# setting the operation priority
|
||||
_op_priority = 11.0
|
||||
|
||||
def _eval_is_real(self):
|
||||
if self.min.is_real and self.max.is_real:
|
||||
return True
|
||||
|
||||
@property
|
||||
def min(self):
|
||||
"""
|
||||
@@ -1109,7 +1102,7 @@ class AccumulationBounds(AtomicExpr):
|
||||
if other is S.Infinity and self.min is S.NegativeInfinity or \
|
||||
other is S.NegativeInfinity and self.max is S.Infinity:
|
||||
return AccumBounds(-oo, oo)
|
||||
elif other.is_extended_real:
|
||||
elif other.is_real:
|
||||
return AccumBounds(Add(self.min, other), Add(self.max, other))
|
||||
return Add(self, other, evaluate=False)
|
||||
return NotImplemented
|
||||
@@ -1129,7 +1122,7 @@ class AccumulationBounds(AtomicExpr):
|
||||
if other is S.NegativeInfinity and self.min is S.NegativeInfinity or \
|
||||
other is S.Infinity and self.max is S.Infinity:
|
||||
return AccumBounds(-oo, oo)
|
||||
elif other.is_extended_real:
|
||||
elif other.is_real:
|
||||
return AccumBounds(
|
||||
Add(self.min, -other),
|
||||
Add(self.max, -other))
|
||||
@@ -1162,7 +1155,7 @@ class AccumulationBounds(AtomicExpr):
|
||||
return AccumBounds(-oo, 0)
|
||||
if self.max.is_zero:
|
||||
return AccumBounds(0, oo)
|
||||
if other.is_extended_real:
|
||||
if other.is_real:
|
||||
if other.is_zero:
|
||||
if self == AccumBounds(-oo, oo):
|
||||
return AccumBounds(-oo, oo)
|
||||
@@ -1171,11 +1164,11 @@ class AccumulationBounds(AtomicExpr):
|
||||
if self.min is S.NegativeInfinity:
|
||||
return AccumBounds(-oo, 0)
|
||||
return S.Zero
|
||||
if other.is_extended_positive:
|
||||
if other.is_positive:
|
||||
return AccumBounds(
|
||||
Mul(self.min, other),
|
||||
Mul(self.max, other))
|
||||
elif other.is_extended_negative:
|
||||
elif other.is_negative:
|
||||
return AccumBounds(
|
||||
Mul(self.max, other),
|
||||
Mul(self.min, other))
|
||||
@@ -1200,33 +1193,33 @@ class AccumulationBounds(AtomicExpr):
|
||||
return AccumBounds(-oo, 0)
|
||||
return AccumBounds(-oo, oo)
|
||||
|
||||
if self.max.is_extended_negative:
|
||||
if other.min.is_extended_negative:
|
||||
if self.max.is_negative:
|
||||
if other.min.is_negative:
|
||||
if other.max.is_zero:
|
||||
return AccumBounds(self.max / other.min, oo)
|
||||
if other.max.is_extended_positive:
|
||||
if other.max.is_positive:
|
||||
# the actual answer is a Union of AccumBounds,
|
||||
# Union(AccumBounds(-oo, self.max/other.max),
|
||||
# AccumBounds(self.max/other.min, oo))
|
||||
return AccumBounds(-oo, oo)
|
||||
|
||||
if other.min.is_zero and other.max.is_extended_positive:
|
||||
if other.min.is_zero and other.max.is_positive:
|
||||
return AccumBounds(-oo, self.max / other.max)
|
||||
|
||||
if self.min.is_extended_positive:
|
||||
if other.min.is_extended_negative:
|
||||
if self.min.is_positive:
|
||||
if other.min.is_negative:
|
||||
if other.max.is_zero:
|
||||
return AccumBounds(-oo, self.min / other.min)
|
||||
if other.max.is_extended_positive:
|
||||
if other.max.is_positive:
|
||||
# the actual answer is a Union of AccumBounds,
|
||||
# Union(AccumBounds(-oo, self.min/other.min),
|
||||
# AccumBounds(self.min/other.max, oo))
|
||||
return AccumBounds(-oo, oo)
|
||||
|
||||
if other.min.is_zero and other.max.is_extended_positive:
|
||||
if other.min.is_zero and other.max.is_positive:
|
||||
return AccumBounds(self.min / other.max, oo)
|
||||
|
||||
elif other.is_extended_real:
|
||||
elif other.is_real:
|
||||
if other is S.Infinity or other is S.NegativeInfinity:
|
||||
if self == AccumBounds(-oo, oo):
|
||||
return AccumBounds(-oo, oo)
|
||||
@@ -1234,9 +1227,9 @@ class AccumulationBounds(AtomicExpr):
|
||||
return AccumBounds(Min(0, other), Max(0, other))
|
||||
if self.min is S.NegativeInfinity:
|
||||
return AccumBounds(Min(0, -other), Max(0, -other))
|
||||
if other.is_extended_positive:
|
||||
if other.is_positive:
|
||||
return AccumBounds(self.min / other, self.max / other)
|
||||
elif other.is_extended_negative:
|
||||
elif other.is_negative:
|
||||
return AccumBounds(self.max / other, self.min / other)
|
||||
return Mul(self, 1 / other, evaluate=False)
|
||||
|
||||
@@ -1247,19 +1240,19 @@ class AccumulationBounds(AtomicExpr):
|
||||
@_sympifyit('other', NotImplemented)
|
||||
def __rdiv__(self, other):
|
||||
if isinstance(other, Expr):
|
||||
if other.is_extended_real:
|
||||
if other.is_real:
|
||||
if other.is_zero:
|
||||
return S.Zero
|
||||
if S.Zero in self:
|
||||
if self.min.is_zero:
|
||||
if other.is_extended_positive:
|
||||
if self.min == S.Zero:
|
||||
if other.is_positive:
|
||||
return AccumBounds(Mul(other, 1 / self.max), oo)
|
||||
if other.is_extended_negative:
|
||||
if other.is_negative:
|
||||
return AccumBounds(-oo, Mul(other, 1 / self.max))
|
||||
if self.max.is_zero:
|
||||
if other.is_extended_positive:
|
||||
if self.max == S.Zero:
|
||||
if other.is_positive:
|
||||
return AccumBounds(-oo, Mul(other, 1 / self.min))
|
||||
if other.is_extended_negative:
|
||||
if other.is_negative:
|
||||
return AccumBounds(Mul(other, 1 / self.min), oo)
|
||||
return AccumBounds(-oo, oo)
|
||||
else:
|
||||
@@ -1276,13 +1269,13 @@ class AccumulationBounds(AtomicExpr):
|
||||
from sympy.functions.elementary.miscellaneous import real_root
|
||||
if isinstance(other, Expr):
|
||||
if other is S.Infinity:
|
||||
if self.min.is_extended_nonnegative:
|
||||
if self.min.is_nonnegative:
|
||||
if self.max < 1:
|
||||
return S.Zero
|
||||
if self.min > 1:
|
||||
return S.Infinity
|
||||
return AccumBounds(0, oo)
|
||||
elif self.max.is_extended_negative:
|
||||
elif self.max.is_negative:
|
||||
if self.min > -1:
|
||||
return S.Zero
|
||||
if self.max < -1:
|
||||
@@ -1298,22 +1291,22 @@ class AccumulationBounds(AtomicExpr):
|
||||
if other is S.NegativeInfinity:
|
||||
return (1 / self)**oo
|
||||
|
||||
if other.is_extended_real and other.is_number:
|
||||
if other.is_real and other.is_number:
|
||||
if other.is_zero:
|
||||
return S.One
|
||||
|
||||
if other.is_Integer:
|
||||
if self.min.is_extended_positive:
|
||||
if self.min.is_positive:
|
||||
return AccumBounds(
|
||||
Min(self.min ** other, self.max ** other),
|
||||
Max(self.min ** other, self.max ** other))
|
||||
elif self.max.is_extended_negative:
|
||||
elif self.max.is_negative:
|
||||
return AccumBounds(
|
||||
Min(self.max ** other, self.min ** other),
|
||||
Max(self.max ** other, self.min ** other))
|
||||
|
||||
if other % 2 == 0:
|
||||
if other.is_extended_negative:
|
||||
if other.is_negative:
|
||||
if self.min.is_zero:
|
||||
return AccumBounds(self.max**other, oo)
|
||||
if self.max.is_zero:
|
||||
@@ -1322,7 +1315,7 @@ class AccumulationBounds(AtomicExpr):
|
||||
return AccumBounds(
|
||||
S.Zero, Max(self.min**other, self.max**other))
|
||||
else:
|
||||
if other.is_extended_negative:
|
||||
if other.is_negative:
|
||||
if self.min.is_zero:
|
||||
return AccumBounds(self.max**other, oo)
|
||||
if self.max.is_zero:
|
||||
@@ -1331,10 +1324,10 @@ class AccumulationBounds(AtomicExpr):
|
||||
return AccumBounds(self.min**other, self.max**other)
|
||||
|
||||
num, den = other.as_numer_denom()
|
||||
if num == S.One:
|
||||
if num == S(1):
|
||||
if den % 2 == 0:
|
||||
if S.Zero in self:
|
||||
if self.min.is_extended_negative:
|
||||
if self.min.is_negative:
|
||||
return AccumBounds(0, real_root(self.max, den))
|
||||
return AccumBounds(real_root(self.min, den),
|
||||
real_root(self.max, den))
|
||||
@@ -1345,9 +1338,9 @@ class AccumulationBounds(AtomicExpr):
|
||||
return NotImplemented
|
||||
|
||||
def __abs__(self):
|
||||
if self.max.is_extended_negative:
|
||||
if self.max.is_negative:
|
||||
return self.__neg__()
|
||||
elif self.min.is_extended_negative:
|
||||
elif self.min.is_negative:
|
||||
return AccumBounds(S.Zero, Max(abs(self.min), self.max))
|
||||
else:
|
||||
return self
|
||||
@@ -1378,7 +1371,8 @@ class AccumulationBounds(AtomicExpr):
|
||||
return True
|
||||
if self.min >= other.max:
|
||||
return False
|
||||
elif not other.is_extended_real:
|
||||
elif not(other.is_real or other is S.Infinity or
|
||||
other is S.NegativeInfinity):
|
||||
raise TypeError(
|
||||
"Invalid comparison of %s %s" %
|
||||
(type(other), other))
|
||||
@@ -1415,7 +1409,8 @@ class AccumulationBounds(AtomicExpr):
|
||||
return True
|
||||
if self.min > other.max:
|
||||
return False
|
||||
elif not other.is_extended_real:
|
||||
elif not(other.is_real or other is S.Infinity or
|
||||
other is S.NegativeInfinity):
|
||||
raise TypeError(
|
||||
"Invalid comparison of %s %s" %
|
||||
(type(other), other))
|
||||
@@ -1452,7 +1447,8 @@ class AccumulationBounds(AtomicExpr):
|
||||
return True
|
||||
if self.max <= other.min:
|
||||
return False
|
||||
elif not other.is_extended_real:
|
||||
elif not(other.is_real or other is S.Infinity or
|
||||
other is S.NegativeInfinity):
|
||||
raise TypeError(
|
||||
"Invalid comparison of %s %s" %
|
||||
(type(other), other))
|
||||
@@ -1489,7 +1485,8 @@ class AccumulationBounds(AtomicExpr):
|
||||
return True
|
||||
if self.max < other.min:
|
||||
return False
|
||||
elif not other.is_extended_real:
|
||||
elif not(other.is_real or other is S.Infinity or
|
||||
other is S.NegativeInfinity):
|
||||
raise TypeError(
|
||||
"Invalid comparison of %s %s" %
|
||||
(type(other), other))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user