Compare commits

..
4 Commits
629 changed files with 49215 additions and 81676 deletions
-3
View File
@@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: ['https://numfocus.salsalabs.org/donate-to-sympy/index.html']
-5
View File
@@ -7,8 +7,6 @@
# 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
@@ -23,9 +21,6 @@ __pycache__/
# VSCode settings file
.vscode/
# PyCharm settings file
/.idea/
# Generated files from Jython
*$py.class
+7 -7
View File
@@ -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
@@ -48,7 +48,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.8-dev
dist: xenial
sudo: true
@@ -63,7 +63,7 @@ matrix:
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 +78,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 +166,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:
@@ -284,7 +284,7 @@ before_install:
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:
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
-26
View File
@@ -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
----------
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)``.
+3 -3
View File
@@ -1,6 +1,6 @@
========
Algebras
========
===============
Algebras Module
===============
Introduction
------------
+3 -3
View File
@@ -1,8 +1,8 @@
.. _assumptions_module:
===========
Assumptions
===========
==================
Assumptions Module
==================
.. automodule:: sympy.assumptions
-2
View File
@@ -10,8 +10,6 @@ Calculus
.. automodule:: sympy.calculus.singularities
:members:
.. _finite_diff:
.. automodule:: sympy.calculus.finite_diff
:members:
+2 -3
View File
@@ -1,6 +1,5 @@
===============
Category Theory
===============
Category Theory Module
======================
Introduction
------------
+5 -5
View File
@@ -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::
+3 -4
View File
@@ -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
-307
View File
@@ -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>`_.
+2 -3
View File
@@ -1,6 +1,5 @@
========
Concrete
========
Concrete Mathematics
====================
Hypergeometric terms
--------------------
+2 -3
View File
@@ -1,6 +1,5 @@
====
Core
====
SymPy Core
==========
sympify
-------
+2 -7
View File
@@ -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
+2 -3
View File
@@ -1,6 +1,5 @@
=====================
Differential Geometry
=====================
Differential Geometry Module
============================
.. module:: sympy.diffgeom
+2 -3
View File
@@ -1,6 +1,5 @@
========
Discrete
========
Discrete Module
===============
The ``discrete`` module in SymPy implements methods to compute discrete
transforms and convolutions of finite sequences.
+1 -2
View File
@@ -1,7 +1,6 @@
.. _evalf-label:
====================
Numerical Evaluation
Numerical evaluation
====================
Basics
+2 -3
View File
@@ -1,8 +1,7 @@
.. _functions:
=========
Functions
=========
Functions Module
****************
.. module:: sympy.functions
+3 -3
View File
@@ -1,6 +1,6 @@
========
Geometry
========
===============
Geometry Module
===============
Introduction
+1 -1
View File
@@ -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::
+3 -3
View File
@@ -1,8 +1,8 @@
.. _holonomic-docs:
=========
Holonomic
=========
===================
Holonomic Functions
===================
.. automodule:: sympy.holonomic
+23 -23
View File
@@ -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
+2 -3
View File
@@ -1,6 +1,5 @@
=========
Integrals
=========
Symbolic Integrals
==================
.. module:: sympy.integrals
+3 -3
View File
@@ -1,6 +1,6 @@
===========
Lie Algebra
===========
==================
Lie Algebra Module
==================
.. automodule:: sympy.liealgebras
+2 -3
View File
@@ -1,6 +1,5 @@
=====
Logic
=====
Logic Module
============
.. module:: sympy.logic
-4
View File
@@ -34,10 +34,6 @@ Matrix Expressions Core Reference
:members:
.. autoclass:: MatPow
:members:
.. autoclass:: HadamardProduct
:members:
.. autoclass:: HadamardPower
:members:
.. autoclass:: Inverse
:members:
.. autoclass:: Transpose
+1 -2
View File
@@ -1,6 +1,5 @@
=============
Number Theory
=============
====================
.. module:: sympy.ntheory.generate
+2 -24
View File
@@ -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
--------------------
+3 -3
View File
@@ -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
+51 -55
View File
@@ -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.
+2 -2
View File
@@ -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.
+4 -5
View File
@@ -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
+3 -3
View File
@@ -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
+2 -17
View File
@@ -1,6 +1,5 @@
========
Printing
========
Printing System
===============
See the :ref:`tutorial-printing` section in tutorial for introduction into
printing.
@@ -301,20 +300,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 -2
View File
@@ -1,5 +1,4 @@
==============
Term Rewriting
Term rewriting
==============
Term rewriting is a very general class of functionalities which are used to
+3 -3
View File
@@ -1,8 +1,8 @@
.. _series-docs:
======
Series
======
=============
Series Module
=============
The series module implements series expansions as a function and many related
functions.
+1 -26
View File
@@ -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))``.
+3 -4
View File
@@ -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
View File
@@ -1,6 +1,5 @@
========
Simplify
========
********
.. currentmodule:: sympy.simplify.simplify
@@ -16,10 +15,6 @@ nthroot
-------
.. autofunction:: nthroot
kroneckersimp
-------------
.. autofunction:: kroneckersimp
besselsimp
----------
.. autofunction:: besselsimp
-12
View File
@@ -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
+1 -1
View File
@@ -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
+4 -22
View File
@@ -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
+3 -3
View File
@@ -1,8 +1,8 @@
.. _tensor_module:
======
Tensor
======
=============
Tensor Module
=============
.. automodule:: sympy.tensor
+15 -11
View File
@@ -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:
+3 -3
View File
@@ -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>`
+5 -7
View File
@@ -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
==================
+2 -2
View File
@@ -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`` ,
+1 -1
View File
@@ -230,7 +230,7 @@ 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
Here is an example of the raw ouput of the ``dotprint()`` function
>>> from sympy.printing.dot import dotprint
>>> from sympy.abc import x
+4 -4
View File
@@ -142,9 +142,9 @@ 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.
@@ -179,7 +179,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:
+1 -1
View File
@@ -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:")
+1 -1
View File
@@ -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):
+1 -1
View File
@@ -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."
]
},
+1 -1
View File
@@ -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. "
]
},
{
+2
View File
@@ -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
+5 -44
View File
@@ -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
View File
@@ -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
View File
@@ -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
+39 -37
View File
@@ -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))
+18 -31
View File
@@ -1,4 +1,4 @@
from sympy import symbols, re, im, I, Abs, Symbol, \
from sympy import symbols, re, im, Add, Mul, I, Abs, Symbol, \
cos, sin, sqrt, conjugate, log, acos, E, pi, \
Matrix, diff, integrate, trigsimp, S, Rational
from sympy.algebras.quaternion import Quaternion
@@ -10,10 +10,9 @@ 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))
@@ -34,9 +33,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)
@@ -69,10 +68,9 @@ def test_quaternion_functions():
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**(-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 +94,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 +105,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))
@@ -154,7 +148,7 @@ def test_quaternion_rotation_iss1593():
[0, sin(x), cos(x)]]))
def test_quaternion_multiplication():
def test_quaternion_nultiplication():
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)
@@ -164,10 +158,3 @@ def test_quaternion_multiplication():
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
+44 -79
View File
@@ -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,7 +1481,7 @@ 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.algebraic, Q.complex),
@@ -1564,4 +1529,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)
+1 -79
View File
@@ -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
)
-378
View File
@@ -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}
+1 -20
View File
@@ -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):
"""
+33 -105
View File
@@ -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)
+51 -60
View File
@@ -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():
+30 -24
View File
@@ -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
@@ -569,8 +567,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 +654,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 +801,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 +982,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 +1043,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 +1178,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 +1333,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 +1437,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 +1514,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 +1539,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 +1611,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 +1642,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 +1748,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 +1848,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 +1876,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 +1909,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 +1923,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 +2011,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
+12 -6
View File
@@ -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,7 +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
assert refine(atan2(y, x), Q.zero(y) & Q.zero(x)) == nan
def test_re():
+4 -16
View File
@@ -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
+15 -16
View File
@@ -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,15 +56,15 @@ 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():
@@ -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)))
+6 -15
View File
@@ -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:
+46 -57
View File
@@ -1,6 +1,6 @@
from itertools import product
from sympy import S, symbols, Function, exp, diff, Rational
from sympy import S, symbols, Function, exp
from sympy.calculus.finite_diff import (
apply_finite_diff, differentiate_finite, finite_diff_weights,
as_finite_diff
@@ -16,62 +16,62 @@ 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
(-S(3)/2*f(5) + 2*f(6) - S(1)/2*f(7))).simplify() == 0
raises(ValueError, lambda: apply_finite_diff(1, [x, h], [f(x)]))
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,58 +83,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])
+3 -3
View File
@@ -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,
@@ -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))
+85 -97
View File
@@ -1,6 +1,6 @@
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)
@@ -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)
@@ -44,8 +44,6 @@ def test_function_range():
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 +54,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 +69,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 +82,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),
@@ -127,12 +123,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,8 +161,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')
@@ -182,11 +176,11 @@ 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():
@@ -207,11 +201,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 +213,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
@@ -233,22 +227,22 @@ def test_stationary_points():
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 +251,26 @@ 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 +279,16 @@ 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,18 +305,18 @@ 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)
@@ -356,26 +350,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 +404,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)
@@ -527,9 +521,3 @@ def test_union_AccumBounds():
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)
+9 -14
View File
@@ -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))
"""
@@ -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):
@@ -1251,12 +1246,12 @@ class AccumulationBounds(AtomicExpr):
if other.is_zero:
return S.Zero
if S.Zero in self:
if self.min.is_zero:
if self.min == S.Zero:
if other.is_extended_positive:
return AccumBounds(Mul(other, 1 / self.max), oo)
if other.is_extended_negative:
return AccumBounds(-oo, Mul(other, 1 / self.max))
if self.max.is_zero:
if self.max == S.Zero:
if other.is_extended_positive:
return AccumBounds(-oo, Mul(other, 1 / self.min))
if other.is_extended_negative:
@@ -1331,7 +1326,7 @@ 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:
+4 -3
View File
@@ -231,7 +231,7 @@ class CodegenArrayContraction(_CodegenArrayAbstract):
def _get_index_shifts(expr):
"""
Get the mapping of indices at the positions before the contraction
occurs.
occures.
Examples
========
@@ -541,7 +541,7 @@ class CodegenArrayPermuteDims(_CodegenArrayAbstract):
from sympy.combinatorics import Permutation
expr = _sympify(expr)
permutation = Permutation(permutation)
plist = permutation.array_form
plist = permutation.args[0]
if plist == sorted(plist):
return expr
obj = Basic.__new__(cls, expr, permutation)
@@ -1336,7 +1336,7 @@ def _recognize_matrix_expression(expr):
elif isinstance(expr, (MatrixSymbol, IndexedBase)):
return expr
elif isinstance(expr, CodegenArrayPermuteDims):
if expr.permutation.array_form == [1, 0]:
if expr.permutation.args[0] == [1, 0]:
return _RecognizeMatOp(Transpose, [_recognize_matrix_expression(expr.expr)])
elif isinstance(expr.expr, CodegenArrayTensorProduct):
ranks = expr.expr.subranks
@@ -1389,6 +1389,7 @@ def _suppress_trivial_dims_in_tensor_product(mat_list):
# That is, add contractions over trivial dimensions:
mat_11 = []
mat_k1 = []
last_dim = mat_list[0].shape[0]
for mat in mat_list:
if mat.shape == (1, 1):
mat_11.append(mat)
+3 -3
View File
@@ -266,7 +266,7 @@ class Token(Basic):
if isinstance(arg, Token):
return printer._print(arg, *args, joiner=self._joiner(k, il), **kwargs)
else:
return printer._print(arg, *args, **kwargs)
return printer._print(v, *args, **kwargs)
if isinstance(v, Tuple):
joined = self._joiner(k, il).join([_print(arg) for arg in v.args])
@@ -1511,7 +1511,7 @@ class Pointer(Variable):
>>> i = Symbol('i', integer=True)
>>> p = Pointer('x')
>>> p[i+1]
Element(x, indices=(i + 1,))
Element(x, indices=((i + 1,),))
"""
@@ -1582,7 +1582,7 @@ class While(Token):
Parameters
==========
condition : expression convertible to Boolean
condition : expression convertable to Boolean
body : CodeBlock or iterable
When passed an iterable it is used to instantiate a CodeBlock.
+1 -1
View File
@@ -398,7 +398,7 @@ class Sqrt(Function): # 'sqrt' already defined in sympy.functions.elementary.mi
Returns the first derivative of this function.
"""
if argindex == 1:
return Pow(self.args[0], Rational(-1, 2))/_Two
return Pow(self.args[0], -S.Half)/_Two
else:
raise ArgumentIndexError(self, argindex)
-10
View File
@@ -619,22 +619,12 @@ def test_FunctionCall():
fc = FunctionCall('power', (x, 3))
assert fc.function_args[0] == x
assert fc.function_args[1] == 3
assert len(fc.function_args) == 2
assert isinstance(fc.function_args[1], Integer)
assert fc == FunctionCall('power', (x, 3))
assert fc != FunctionCall('power', (3, x))
assert fc != FunctionCall('Power', (x, 3))
assert fc.func(*fc.args) == fc
fc2 = FunctionCall('fma', [2, 3, 4])
assert len(fc2.function_args) == 3
assert fc2.function_args[0] == 2
assert fc2.function_args[1] == 3
assert fc2.function_args[2] == 4
assert str(fc2) in ( # not sure if QuotedString is a better default...
'FunctionCall(fma, function_args=(2, 3, 4))',
'FunctionCall("fma", function_args=(2, 3, 4))',
)
def test_ast_replace():
x = Variable('x', real)
+4 -4
View File
@@ -47,7 +47,7 @@ def test_log1p():
assert abs(expand_log(log1p(1e-99)).evalf() - 1e-99) < 1e-100
# Properties
assert log1p(-2**Rational(-1, 2)).is_real
assert log1p(-2**(-S(1)/2)).is_real
assert not log1p(-1).is_finite
assert log1p(pi).is_finite
@@ -141,10 +141,10 @@ def test_Sqrt():
x = Symbol('x')
# Expand
assert Sqrt(x).expand(func=True) - x**S.Half == 0
assert Sqrt(x).expand(func=True) - x**Rational(1, 2) == 0
# Diff
assert Sqrt(42*x).diff(x) - 42*(42*x)**(S.Half - 1)/2 == 0
assert Sqrt(42*x).diff(x) - 42*(42*x)**(Rational(1, 2) - 1)/2 == 0
assert Sqrt(42*x).diff(x) - Sqrt(42*x).expand(func=True).diff(x) == 0
@@ -152,7 +152,7 @@ def test_hypot():
x, y = symbols('x y')
# Expand
assert hypot(x, y).expand(func=True) - (x**2 + y**2)**S.Half == 0
assert hypot(x, y).expand(func=True) - (x**2 + y**2)**Rational(1, 2) == 0
# Diff
assert hypot(17*x, 42*y).diff(x).expand(func=True) - hypot(17*x, 42*y).expand(func=True).diff(x) == 0
-1
View File
@@ -11,4 +11,3 @@ from sympy.combinatorics.group_constructs import DirectProduct
from sympy.combinatorics.graycode import GrayCode
from sympy.combinatorics.named_groups import (SymmetricGroup, DihedralGroup,
CyclicGroup, AlternatingGroup, AbelianGroup, RubikGroup)
from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector
+1 -1
View File
@@ -1166,7 +1166,7 @@ def modified_coset_enumeration_r(fp_grp, Y, max_cosets=None, draft=None,
r"""
Introduce a new set of symbols y \in Y that correspond to the
generators of the subgroup. Store the elements of Y as a
word P[\alpha, x] and compute the coset table similar to that of
word P[\alpha, x] and compute the coset table simlar to that of
the regular coset enumeration methods.
Examples
+1 -1
View File
@@ -383,7 +383,7 @@ class FpGroup(DefaultPrinting):
'''
from sympy.combinatorics import Permutation, PermutationGroup
from sympy.combinatorics.homomorphisms import homomorphism
if self.order() is S.Infinity:
if self.order() == S.Infinity:
raise NotImplementedError("Permutation presentation of infinite "
"groups is not implemented")
if self._perm_isomorphism:
+4 -4
View File
@@ -116,7 +116,7 @@ class GroupHomomorphism(object):
from sympy import S
G = self.domain
G_order = G.order()
if G_order is S.Infinity:
if G_order == S.Infinity:
raise NotImplementedError(
"Kernel computation is not implemented for infinite groups")
gens = []
@@ -199,7 +199,7 @@ class GroupHomomorphism(object):
from sympy import S
im = self.image().order()
oth = self.codomain.order()
if im is S.Infinity and oth is S.Infinity:
if im == S.Infinity and oth == S.Infinity:
return None
else:
return im == oth
@@ -495,11 +495,11 @@ def group_isomorphism(G, H, isomorphism=True):
g_order = G.order()
h_order = H.order()
if g_order is S.Infinity:
if g_order == S.Infinity:
raise NotImplementedError("Isomorphism methods are not implemented for infinite groups.")
if isinstance(H, FpGroup):
if h_order is S.Infinity:
if h_order == S.Infinity:
raise NotImplementedError("Isomorphism methods are not implemented for infinite groups.")
_H, h_isomorphism = H._to_perm_group()
+1 -1
View File
@@ -6,7 +6,7 @@ from sympy.core.sympify import _sympify
from sympy.functions.combinatorial.numbers import bell
from sympy.matrices import zeros
from sympy.sets.sets import FiniteSet, Union
from sympy.utilities.iterables import flatten, group
from sympy.utilities.iterables import has_dups, flatten, group
from collections import defaultdict
-672
View File
@@ -1,672 +0,0 @@
from sympy import isprime
from sympy.combinatorics.perm_groups import PermutationGroup
from sympy.printing.defaults import DefaultPrinting
from sympy.combinatorics.free_groups import free_group
class PolycyclicGroup(DefaultPrinting):
is_group = True
is_solvable = True
def __init__(self, pc_sequence, pc_series, relative_order, collector=None):
"""
Parameters
==========
pc_sequence : list
A sequence of elements whose classes generate the cyclic factor
groups of pc_series.
pc_series : list
A subnormal sequence of subgroups where each factor group is cyclic.
relative_order : list
The orders of factor groups of pc_series.
collector : Collector
By default, it is None. Collector class provides the
polycyclic presentation with various other functionalities.
"""
self.pcgs = pc_sequence
self.pc_series = pc_series
self.relative_order = relative_order
self.collector = Collector(self.pcgs, pc_series, relative_order) if not collector else collector
def is_prime_order(self):
return all(isprime(order) for order in self.relative_order)
def length(self):
return len(self.pcgs)
class Collector(DefaultPrinting):
"""
References
==========
.. [1] Holt, D., Eick, B., O'Brien, E.
"Handbook of Computational Group Theory"
Section 8.1.3
"""
def __init__(self, pcgs, pc_series, relative_order, free_group_=None, pc_presentation=None):
"""
Most of the parameters for the Collector class are the same as for PolycyclicGroup.
Others are described below.
Parameters
==========
free_group_ : tuple
free_group_ provides the mapping of polycyclic generating
sequence with the free group elements.
pc_presentation : dict
Provides the presentation of polycyclic groups with the
help of power and conjugate relators.
See Also
========
PolycyclicGroup
"""
self.pcgs = pcgs
self.pc_series = pc_series
self.relative_order = relative_order
self.free_group = free_group('x:{0}'.format(len(pcgs)))[0] if not free_group_ else free_group_
self.index = {s: i for i, s in enumerate(self.free_group.symbols)}
self.pc_presentation = self.pc_relators()
def minimal_uncollected_subword(self, word):
r"""
Returns the minimal uncollected subwords.
A word ``v`` defined on generators in ``X`` 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}`
for `a_j` not in `\{1, \ldots, s-1\}`. Where, ``s`` is the power
exponent of the corresponding generator.
Examples
========
>>> 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),)
"""
# To handle the case word = <identity>
if not word:
return None
array = word.array_form
re = self.relative_order
index = self.index
for i in range(len(array)):
s1, e1 = array[i]
if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1):
return ((s1, e1), )
for i in range(len(array)-1):
s1, e1 = array[i]
s2, e2 = array[i+1]
if index[s1] > index[s2]:
e = 1 if e2 > 0 else -1
return ((s1, e1), (s2, e))
return None
def relations(self):
"""
Separates the given relators of pc presentation in power and
conjugate relations.
Returns
=======
(power_rel, conj_rel)
Separates pc presentation into power and conjugate relations.
Examples
========
>>> from sympy.combinatorics.named_groups import SymmetricGroup
>>> G = SymmetricGroup(3)
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> power_rel, conj_rel = collector.relations()
>>> power_rel
{x0**2: (), x1**3: ()}
>>> conj_rel
{x0**-1*x1*x0: x1**2}
See Also
========
pc_relators
"""
power_relators = {}
conjugate_relators = {}
for key, value in self.pc_presentation.items():
if len(key.array_form) == 1:
power_relators[key] = value
else:
conjugate_relators[key] = value
return power_relators, conjugate_relators
def subword_index(self, word, w):
"""
Returns the start and ending index of a given
subword in a word.
Parameters
==========
word : FreeGroupElement
word defined on free group elements for a
polycyclic group.
w : FreeGroupElement
subword of a given word, whose starting and
ending index to be computed.
Returns
=======
(i, j)
A tuple containing starting and ending index of ``w``
in the given word.
Examples
========
>>> 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)
"""
low = -1
high = -1
for i in range(len(word)-len(w)+1):
if word.subword(i, i+len(w)) == w:
low = i
high = i+len(w)
break
if low == high == -1:
return -1, -1
return low, high
def map_relation(self, w):
"""
Return a conjugate relation.
Given a word formed by two free group elements, the
corresponding conjugate relation with those free
group elements is formed and mapped with the collected
word in the polycyclic presentation.
Examples
========
>>> from sympy.combinatorics.named_groups import SymmetricGroup
>>> from sympy.combinatorics.free_groups import free_group
>>> G = SymmetricGroup(3)
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> F, x0, x1 = free_group("x0, x1")
>>> w = x1*x0
>>> collector.map_relation(w)
x1**2
See Also
========
pc_presentation
"""
array = w.array_form
s1 = array[0][0]
s2 = array[1][0]
key = ((s2, -1), (s1, 1), (s2, 1))
key = self.free_group.dtype(key)
return self.pc_presentation[key]
def collected_word(self, word):
r"""
Return the collected form of a 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\}`.
Otherwise w is uncollected.
Parameters
==========
word : FreeGroupElement
An uncollected word.
Returns
=======
word
A collected word of form `w = {x_{i_1}}^{a_1}, \ldots,
{x_{i_r}}^{a_r}` with `i_1, i_2, \ldots, i_r` and `a_j \in
\{1, \ldots, {s_j}-1\}`.
Examples
========
>>> 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
See Also
========
minimal_uncollected_subword
"""
free_group = self.free_group
while True:
w = self.minimal_uncollected_subword(word)
if not w:
break
low, high = self.subword_index(word, free_group.dtype(w))
if low == -1:
continue
s1, e1 = w[0]
if len(w) == 1:
re = self.relative_order[self.index[s1]]
q = e1 // re
r = e1-q*re
key = ((w[0][0], re), )
key = free_group.dtype(key)
if self.pc_presentation[key]:
word_ = ((w[0][0], r), (self.pc_presentation[key], q))
word_ = free_group.dtype(word_)
else:
if r != 0:
word_ = ((w[0][0], r), )
word_ = free_group.dtype(word_)
else:
word_ = None
word = word.eliminate_word(free_group.dtype(w), word_)
if len(w) == 2 and w[1][1] > 0:
s2, e2 = w[1]
s2 = ((s2, 1), )
s2 = free_group.dtype(s2)
word_ = self.map_relation(free_group.dtype(w))
word_ = s2*word_**e1
word_ = free_group.dtype(word_)
word = word.substituted_word(low, high, word_)
elif len(w) == 2 and w[1][1] < 0:
s2, e2 = w[1]
s2 = ((s2, 1), )
s2 = free_group.dtype(s2)
word_ = self.map_relation(free_group.dtype(w))
word_ = s2**-1*word_**e1
word_ = free_group.dtype(word_)
word = word.substituted_word(low, high, word_)
return word
def pc_relators(self):
r"""
Return the polycyclic presentation.
There are two types of relations used in polycyclic
presentation.
* ``Power relations`` : Power relators are of the form `x_i^{re_i}`,
where `i \in \{0, \ldots, \mathrm{len(pcgs)}\}`, ``x`` represents polycyclic
generator and ``re`` is the corresponding relative order.
* ``Conjugate relations`` : Conjugate relators are of the form `x_j^-1x_ix_j`,
where `j < i \in \{0, \ldots, \mathrm{len(pcgs)}\}`.
Returns
=======
A dictionary with power and conjugate relations as key and
their collected form as corresponding values.
Notes
=====
Identity Permutation is mapped with empty ``()``.
Examples
========
>>> from sympy.combinatorics.named_groups import SymmetricGroup
>>> from sympy.combinatorics.permutations import Permutation
>>> S = SymmetricGroup(49).sylow_subgroup(7)
>>> der = S.derived_series()
>>> G = der[len(der)-2]
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> pcgs = PcGroup.pcgs
>>> len(pcgs)
6
>>> 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
"""
free_group = self.free_group
rel_order = self.relative_order
pc_relators = {}
perm_to_free = {}
pcgs = self.pcgs
for gen, s in zip(pcgs, free_group.generators):
perm_to_free[gen**-1] = s**-1
perm_to_free[gen] = s
pcgs = pcgs[::-1]
series = self.pc_series[::-1]
rel_order = rel_order[::-1]
collected_gens = []
for i, gen in enumerate(pcgs):
re = rel_order[i]
relation = perm_to_free[gen]**re
G = series[i]
l = G.generator_product(gen**re, original = True)
l.reverse()
word = free_group.identity
for g in l:
word = word*perm_to_free[g]
word = self.collected_word(word)
pc_relators[relation] = word if word else ()
self.pc_presentation = pc_relators
collected_gens.append(gen)
if len(collected_gens) > 1:
conj = collected_gens[len(collected_gens)-1]
conjugator = perm_to_free[conj]
for j in range(len(collected_gens)-1):
conjugated = perm_to_free[collected_gens[j]]
relation = conjugator**-1*conjugated*conjugator
gens = conj**-1*collected_gens[j]*conj
l = G.generator_product(gens, original = True)
l.reverse()
word = free_group.identity
for g in l:
word = word*perm_to_free[g]
word = self.collected_word(word)
pc_relators[relation] = word if word else ()
self.pc_presentation = pc_relators
return pc_relators
def exponent_vector(self, element):
r"""
Return the exponent vector of length equal to the
length of polycyclic generating sequence.
For a given generator/element ``g`` of the polycyclic group,
it 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.
Parameters
==========
element : Permutation
Generator of a polycyclic group.
Examples
========
>>> 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]
References
==========
.. [1] Holt, D., Eick, B., O'Brien, E.
"Handbook of Computational Group Theory"
Section 8.1.1, Definition 8.4
"""
free_group = self.free_group
G = PermutationGroup()
for g in self.pcgs:
G = PermutationGroup([g] + G.generators)
gens = G.generator_product(element, original = True)
gens.reverse()
perm_to_free = {}
for sym, g in zip(free_group.generators, self.pcgs):
perm_to_free[g**-1] = sym**-1
perm_to_free[g] = sym
w = free_group.identity
for g in gens:
w = w*perm_to_free[g]
word = self.collected_word(w)
index = self.index
exp_vector = [0]*len(free_group)
word = word.array_form
for t in word:
exp_vector[index[t[0]]] = t[1]
return exp_vector
def depth(self, element):
r"""
Return the depth of a given element.
The depth of a given element ``g`` is defined by
`\mathrm{dep}[g] = i` if `e_1 = e_2 = \ldots = e_{i-1} = 0`
and `e_i != 0`, where ``e`` represents the exponent-vector.
Examples
========
>>> 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
References
==========
.. [1] Holt, D., Eick, B., O'Brien, E.
"Handbook of Computational Group Theory"
Section 8.1.1, Definition 8.5
"""
exp_vector = self.exponent_vector(element)
return next((i+1 for i, x in enumerate(exp_vector) if x), len(self.pcgs)+1)
def leading_exponent(self, element):
r"""
Return the leading non-zero exponent.
The leading exponent for a given element `g` is defined
by `\mathrm{leading\_exponent}[g]` `= e_i`, if `\mathrm{depth}[g] = i`.
Examples
========
>>> from sympy.combinatorics.named_groups import SymmetricGroup
>>> G = SymmetricGroup(3)
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> collector.leading_exponent(G[1])
1
"""
exp_vector = self.exponent_vector(element)
depth = self.depth(element)
if depth != len(self.pcgs)+1:
return exp_vector[depth-1]
return None
def _sift(self, z, g):
h = g
d = self.depth(h)
while d < len(self.pcgs) and z[d-1] != 1:
k = z[d-1]
e = self.leading_exponent(h)*self.leading_exponent(k**-1)
e = e % self.relative_order[d-1]
h = k**-e*h
d = self.depth(h)
return h
def induced_pcgs(self, gens):
"""
Parameters
==========
gens : list
A list of generators on which polycyclic subgroup
is to be defined.
Examples
========
>>> from sympy.combinatorics.named_groups import SymmetricGroup
>>> S = SymmetricGroup(8)
>>> G = S.sylow_subgroup(2)
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> gens = [G[0], G[1]]
>>> ipcgs = collector.induced_pcgs(gens)
>>> [gen.order() for gen in ipcgs]
[2, 2, 2]
>>> G = S.sylow_subgroup(3)
>>> PcGroup = G.polycyclic_group()
>>> collector = PcGroup.collector
>>> gens = [G[0], G[1]]
>>> ipcgs = collector.induced_pcgs(gens)
>>> [gen.order() for gen in ipcgs]
[3]
"""
z = [1]*len(self.pcgs)
G = gens
while G:
g = G.pop(0)
h = self._sift(z, g)
d = self.depth(h)
if d < len(self.pcgs):
for gen in z:
if gen != 1:
G.append(h**-1*gen**-1*h*gen)
z[d-1] = h;
z = [gen for gen in z if gen != 1]
return z
def constructive_membership_test(self, ipcgs, g):
"""
Return the exponent vector for induced pcgs.
"""
e = [0]*len(ipcgs)
h = g
d = self.depth(h)
for i, gen in enumerate(ipcgs):
while self.depth(gen) == d:
f = self.leading_exponent(h)*self.leading_exponent(gen)
f = f % self.relative_order[d-1]
h = gen**(-f)*h
e[i] = f
d = self.depth(h)
if h == 1:
return e
return False
+1 -42
View File
@@ -1344,7 +1344,7 @@ class PermutationGroup(Basic):
>>> from sympy.combinatorics import Permutation, PermutationGroup
>>> p = PermutationGroup(Permutation(1, 3), Permutation(1, 2))
>>> p.elements
{(1 2 3), (1 3 2), (1 3), (2 3), (3), (3)(1 2)}
{(3), (2 3), (3)(1 2), (1 2 3), (1 3 2), (1 3)}
"""
return set(self._elements)
@@ -4515,47 +4515,6 @@ class PermutationGroup(Basic):
G._fp_presentation = simplify_presentation(G_p)
return G._fp_presentation
def polycyclic_group(self):
"""
Return the PolycyclicGroup instance with below parameters:
* ``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
the derived series.
* ``relative_order`` : A list, computed by the ratio of adjacent groups in
pc_series.
"""
from sympy.combinatorics.pc_groups import PolycyclicGroup
if not self.is_polycyclic:
raise ValueError("The group must be solvable")
der = self.derived_series()
pc_series = []
pc_sequence = []
relative_order = []
pc_series.append(der[-1])
der.reverse()
for i in range(len(der)-1):
H = der[i]
for g in der[i+1].generators:
if g not in H:
H = PermutationGroup([g] + H.generators)
pc_series.insert(0, H)
pc_sequence.insert(0, g)
G1 = pc_series[0].order()
G2 = pc_series[1].order()
relative_order.insert(0, G1 // G2)
return PolycyclicGroup(pc_sequence, pc_series, relative_order, collector=None)
def _orbit(degree, generators, alpha, action='tuples'):
r"""Compute the orbit of alpha `\{g(\alpha) | g \in G\}` as a set.
+3 -3
View File
@@ -3,7 +3,7 @@ from __future__ import print_function, division
import random
from collections import defaultdict
from sympy.core.basic import Atom
from sympy.core import Basic
from sympy.core.compatibility import is_sequence, reduce, range, as_int
from sympy.matrices import zeros
from sympy.polys.polytools import lcm
@@ -466,7 +466,7 @@ class Cycle(dict):
return Cycle(self)
class Permutation(Atom):
class Permutation(Basic):
"""
A permutation, alternatively known as an 'arrangement number' or 'ordering'
is an arrangement of the elements of an ordered list into a one-to-one
@@ -968,7 +968,7 @@ class Permutation(Atom):
Permutation([2, 1, 3, 0])
"""
p = super(Permutation, cls).__new__(cls)
p = Basic.__new__(cls, perm)
p._array_form = perm
p._size = len(perm)
return p
+1 -1
View File
@@ -398,7 +398,7 @@ class RewritingSystem(object):
else:
self.reduction_automaton.states[state].add_transition(letter, current_state_name)
elif current_state_type == 'a':
# Check if the transition to any new state in possible.
# Check if the transition to any new state in posible.
for letter in automaton_alphabet:
_next = current_state_name*letter
while len(_next) and _next not in self.reduction_automaton.states:
+2 -2
View File
@@ -155,7 +155,7 @@ def test_order():
assert f.order() == 8
f = FpGroup(F, [x*y*x**-1*y**-1, y**2])
assert f.order() is S.Infinity
assert f.order() == S.Infinity
F, a, b, c = free_group("a, b, c")
f = FpGroup(F, [a**250, b**2, c*b*c**-1*b, c**4, c**-1*a**-1*c*a, a**-1*b**-1*a*b])
@@ -163,7 +163,7 @@ def test_order():
F, x = free_group("x")
f = FpGroup(F, [])
assert f.order() is S.Infinity
assert f.order() == S.Infinity
f = FpGroup(free_group('')[0], [])
assert f.order() == 1
@@ -27,7 +27,7 @@ def test_free_group():
assert len(F) == 3
assert str(F) == '<free group on the generators (x, y, z)>'
assert not F == G
assert F.order() is oo
assert F.order() == oo
assert F.is_abelian == False
assert F.center() == set([F.identity])
@@ -90,7 +90,7 @@ def test_FreeGroupElm_type_error():
def test_FreeGroupElm_methods():
assert (x**0).order() == 1
assert (y**2).order() is oo
assert (y**2).order() == oo
assert (x**-1*y).commutator(x) == y**-1*x**-1*y*x
assert len(x**2*y**-1) == 3
assert len(x**-1*y**3*z) == 5

Some files were not shown because too many files have changed in this diff Show More