Skip to content

Commit

Permalink
move SymPyPyCall -> SymPy; breaking change
Browse files Browse the repository at this point in the history
  • Loading branch information
jverzani committed Oct 21, 2023
1 parent d7610a5 commit 1a9131c
Show file tree
Hide file tree
Showing 57 changed files with 163 additions and 16,966 deletions.
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ assignees: ''

---

**Wait**

Most all issues are actually issues with [SymPyCore](https://github.com/jverzani/SymPyCore.jl/issues). If so please report them there.

`SymPy` contains very little code. Basically only that necessary to support the "glue" package `PyCall` to interop `Julia` with `Python`.



**Describe the bug**
A clear and concise description of what the bug is.

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/CompatHelper.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: CompatHelper
on:
schedule:
- cron: '00 00 * * *'
- cron: 0 0 * * *
workflow_dispatch:
jobs:
CompatHelper:
Expand All @@ -12,5 +12,5 @@ jobs:
- name: CompatHelper.main()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} # optional
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
run: julia -e 'using CompatHelper; CompatHelper.main()'
18 changes: 17 additions & 1 deletion .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ on:
types:
- created
workflow_dispatch:
inputs:
lookback:
default: 3
permissions:
actions: read
checks: read
contents: write
deployments: read
issues: read
discussions: read
packages: read
pages: read
pull-requests: read
repository-projects: read
security-events: read
statuses: read
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
Expand All @@ -12,4 +28,4 @@ jobs:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
40 changes: 15 additions & 25 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: CI
on:
pull_request:
branches:
- master
push:
branches:
- master
tags: '*'
- main
tags: ['*']
pull_request:
workflow_dispatch:
concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
Expand All @@ -15,33 +19,19 @@ jobs:
fail-fast: false
matrix:
version:
- '1.6' # lowest supported version
- '1' # last released version
os:
- '1.6'
- '1.9'

os:
- ubuntu-latest
- macos-latest
- windows-latest
arch:
- x64
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
file: lcov.info
28 changes: 0 additions & 28 deletions .github/workflows/documenter.yml

This file was deleted.

26 changes: 10 additions & 16 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
name = "SymPy"
uuid = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
version = "1.2.0"
version = "2.0.0."

[deps]
CommonEq = "3709ef60-1bee-4518-9f2f-acd86f176c50"
CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"

[weakdeps]
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"

[extensions]
SymPySymbolicUtilsExt = "SymbolicUtils"
SymPyCore = "458b697b-88f0-4a86-b56b-78b75cfb3531"

[compat]
CommonEq = "0.2"
CommonSolve = "0.2"
Latexify = "0.15, 0.16"
PyCall = "1.91"
RecipesBase = "0.7, 0.8, 1.0, 1.1"
SpecialFunctions = "0.8, 0.9, 0.10, 1.0, 2"
LinearAlgebra = "1.6"
PyCall = "1.96"
SpecialFunctions = "0.7, 0.8, 0.8, 0.10, 1, 2"
SymbolicUtils = "1"
julia = "1.0"
SymPyCore = "0.1, 1"
julia = "1.6"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"

[targets]
test = ["SymbolicUtils", "Test"]
test = ["Test", "LinearAlgebra", "SpecialFunctions", "SymbolicUtils"]
161 changes: 8 additions & 153 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
[![Build status](https://github.com/JuliaPy/SymPy.jl/workflows/CI/badge.svg)](https://github.com/JuliaPy/SymPy.jl/actions)
 
[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliapy.github.io/SymPy.jl/dev/)
# SymPy

[![Docs](https://img.shields.io/badge/docs-dev-blue.svg)](https://jverzani.github.io/SymPyCore.jl/dev)
[![Build Status](https://github.com/jverzani/SymPy.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/jverzani/SymPy.jl/actions/workflows/CI.yml?query=branch%3Amain)
[![Coverage](https://codecov.io/gh/jverzani/SymPy.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/jverzani/SymPy.jl)

> **Note**
> `SymPy` is on the move... Well, actually, the `SymPy.jl` package is planned to utilize
> the new [SymPyCore](https://github.com/jverzani/SymPyCore.jl) backend, which is used by [SymPyPythonCall](https://github.com/jverzani/SymPyPythonCall.jl) and (temporarily) by the unregistered [SymPyPyCall](https://github.com/jverzani/SymPyPyCall.jl).
>
> The plan is:
>
> * `v"1.2.0"` will include most all deprecations. See `src/deprecated.jl` for details
>
> * `v"2.0.0"` will be what is currently `SymPyPyCall`. It is expected that most all code will continue to work as is, though if a few of the dustier corners are used, there may need to be some modest adjustments.

[SymPyCore](https://github.com/jverzani/SymPyCore.jl) provides a `Julia`n interface to the [SymPy](https://www.sympy.org/) library of Python.

# SymPy Package to bring Python's `SymPy` functionality into `Julia` via `PyCall`

SymPy (`http://sympy.org/`) is a Python library for symbolic mathematics.
`SymPy` utilizes `SymPyCore` and the `PyCall` package (to provide the interop between `Julia` and `Python`) to enable access to Python's SymPy library using the practices and idioms of `Julia`.

With the excellent `PyCall` package of `julia`, one has access to the
many features of the SymPy library from within a `Julia` session.

This `SymPy` package provides a light interface for the
features of the SymPy library that makes working with SymPy objects a bit
easier.

(The [SymPyPythonCall](https://github.com/jverzani/SymPyPythonCall.jl/) package provides an
alternative relying on the [PythonCall](https://github.com/cjdoris/PythonCall.jl)
package for interfacing with `Python`.)

The documentation inludes an introduction document and a version of
the SymPy tutorial translated from the Python syntax into Julia.
The package [SymPyPythonCall](https://github.com/jverzani/SymPyPythonCall.jl) does a similar thing with the `PythonCall` package providing the interop.

### Installation

Expand All @@ -39,7 +19,7 @@ installed on your system. If `PyCall` is installed using `Conda`
(which is the default if no system `python` is found), then the
underlying SymPy library will be installed via `Conda` when the
package is first loaded. Otherwise, installing both Python and the
SymPy library (which also requires mpmath) can be done by other means.
SymPy library can be done by other means.
In this case, the `Anaconda` distribution is suggested, as it provides a single
installation of Python that includes SymPy and many other
scientific libraries that can be profitably accessed within `Julia`
Expand All @@ -56,128 +36,3 @@ Pkg.add("Conda") # if needed
using Conda
Conda.update()
```

## The `PyCall` interface to `SymPy`

The only point to this package is that using `PyCall` to access
SymPy is somewhat cumbersome. The following is how one would define
a symbolic value `x`, take its sine, then evaluate the symboic
expression for `x` equal `pi`, say:

```
using PyCall
sympy = pyimport("sympy") #
x = sympy.Symbol("x") # PyObject x
y = sympy.sin(x) # PyObject sin(x)
z = y.subs(x, sympy.pi) # PyObject 0
convert(Float64, z) # 0.0
```


The `sympy` object imported on the second line provides the access to
much of SymPy's functionality, allowing access to functions
(`sympy.sin`), properties, modules (`sympy`), and classes
(`sympy.Symbol`, `sympy.Pi`). The `Symbol` and `sin` operations are found
within the imported `sympy` module and, as seen, are referenced with
`Python`'s dot call syntax, as implemented in `PyCall` through a
specialized `getproperty` method.

SymPy's functionality is also found through methods bound to
an object of a certain class. The `subs` method of the `y` object is an
example. Such methods are also accessed with Python's dot-call
syntax. The call above substitutes a value of `sympy.pi` for the
symbolic variable `x`. This leaves the object as a `PyObject` storing
a number which can be brought back into `julia` through conversion, in
this case through an explicit `convert` call.


Alternatively, `PyCall` now has a `*` method, so the above could also be done with:

```
x = sympy.Symbol("x")
y = sympy.pi * x
z = sympy.sin(y)
convert(Float64, z.subs(x, 1))
```

With the `SymPy` package this gets replaced by a more `julia`n syntax:

```
using SymPy
x = symbols("x") # or @syms x
y = sin(pi*x)
y(1) # Does y.subs(x, 1). Use y(x=>1) to be specific as to which symbol to substitute
```

The object `x` we create is of type `Sym`, a simple proxy for the
underlying `PyObject`. The package overloads the familiar math functions so
that working with symbolic expressions can use natural `julia`
idioms. The final result here is a symbolic value of `0`, which
prints as `0` and not `PyObject 0`. To convert it into a numeric value
within `Julia`, the `N` function may be used, which acts like the
float conversion, only there is an attempt to preserve the variable type.

(There is a subtlety, the value of `pi` here (an `Irrational` in
`Julia`) is converted to the symbolic `PI`, but in general won't be if
the math constant is coerced to a floating point value before it
encounters a symbolic object. It is better to just use the symbolic
value `PI`, an alias for `sympy.pi` used above.)


----


SymPy has a mix of function calls (as in `sin(x)`) and method calls
(as in `y.subs(x,1)`). The function calls are from objects in the base
`sympy` module. When the `SymPy` package is loaded, in addition to
specialized methods for many generic `Julia` functions, such as `sin`,
a priviledged set of the function calls in `sympy` are imported as
generic functions narrowed on their first argument being a symbolic
object, as constructed by `@syms`, `Sym`, or `symbols`. (Calling
`import_from(sympy)` will import all the function calls.)

The basic usage follows these points:

* generic methods from `Julia` and imported functions in the `sympy`
namespace are called through `fn(object)`

* SymPy methods are called through Python's dot-call syntax:
`object.fn(...)`

* Contructors, like `sympy.Symbol`, and other non-function calls from `sympy` are qualified
with `sympy.Constructor(...)`. Such qualified calls are also useful
when the first argument is not symbolic.


So, these three calls are different,

```
sin(1), sin(Sym(1)), sympy.sin(1)
```

The first involves no symbolic values. The second and third are
related and return a symbolic value for `sin(1)`. The second
dispatches on the symbolic argument `Sym(1)`, the third has no
dispatch, but refers to a SymPy function from the `sympy` object. Its
argument, `1`, is converted by `PyCall` into a Python object for the
function to process.

In the initial example, slightly rewritten, we could have issued:

```
x = symbols("x")
y = sin(pi*x)
y.subs(x, 1)
```

The first line calls a provided alias for `sympy.symbols` which is
defined to allow a string (or a symbol) as an argument. The second,
dispatches to `sympy.sin`, as `pi*x` is symbolic-- `x` is, and
multiplication promotes to a symbolic value. The third line uses the
dot-call syntax of `PyCall` to call the `subs` method of the symbolic
`y` object.


Not illustrated above, but classes and other objects from SymPy are
not brought in by default, and can be accessed using qualification, as
in `sympy.Function` (used, as is `@syms`, to define symbolic functions).
4 changes: 0 additions & 4 deletions REQUIRE

This file was deleted.

5 changes: 0 additions & 5 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"

[compat]
Documenter = "0.24, 1"
Loading

0 comments on commit 1a9131c

Please sign in to comment.