adds support for Python 3.5 infix matrix multiplication

Python 3.5 will have a new commercial-at infix operator with the magic
methods __matmul__, __rmatmul__, and __imatmul__, unused as yet in the
standard library, but intended to represent matrix multiplication in
numerical code; see PEP 465 (https://www.python.org/dev/peps/pep-0465/)
for details. This commit (developed against Python 3.5 alpha 3) brings
support for this operator to Hy when running under Python 3.5 (or,
hypothetically as yet, greater). For Hy under Python <= 3.4, attempting
to use `@` in function-call position currently results in a NameError;
this commit does not change that behavior.

This is intended to resolve #668.
This commit is contained in:
Zack M. Davis 2015-04-12 20:35:08 -07:00
parent 4938163ec4
commit 0dbf2126cf
3 changed files with 67 additions and 1 deletions

View File

@ -42,6 +42,7 @@ PY27 = sys.version_info >= (2, 7)
PY3 = sys.version_info[0] >= 3 PY3 = sys.version_info[0] >= 3
PY33 = sys.version_info >= (3, 3) PY33 = sys.version_info >= (3, 3)
PY34 = sys.version_info >= (3, 4) PY34 = sys.version_info >= (3, 4)
PY35 = sys.version_info >= (3, 5)
if PY3: if PY3:
str_type = str str_type = str

View File

@ -38,7 +38,8 @@ from hy.models.cons import HyCons
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
import hy.macros import hy.macros
from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34, raise_empty from hy._compat import (
str_type, long_type, PY27, PY33, PY3, PY34, PY35, raise_empty)
from hy.macros import require, macroexpand, reader_macroexpand from hy.macros import require, macroexpand, reader_macroexpand
import hy.importer import hy.importer
@ -121,6 +122,13 @@ def builds(_type):
return _dec return _dec
def builds_if(_type, condition):
if condition:
return builds(_type)
else:
return lambda fn: fn
class Result(object): class Result(object):
""" """
Smart representation of the result of a hy->AST compilation Smart representation of the result of a hy->AST compilation
@ -1638,6 +1646,7 @@ class HyASTCompiler(object):
@builds("|") @builds("|")
@builds("^") @builds("^")
@builds("&") @builds("&")
@builds_if("@", PY35)
@checkargs(min=2) @checkargs(min=2)
def compile_maths_expression(self, expression): def compile_maths_expression(self, expression):
ops = {"+": ast.Add, ops = {"+": ast.Add,
@ -1652,6 +1661,8 @@ class HyASTCompiler(object):
"|": ast.BitOr, "|": ast.BitOr,
"^": ast.BitXor, "^": ast.BitXor,
"&": ast.BitAnd} "&": ast.BitAnd}
if PY35:
ops.update({"@": ast.MatMult})
inv = expression.pop(0) inv = expression.pop(0)
op = ops[inv] op = ops[inv]
@ -1711,6 +1722,7 @@ class HyASTCompiler(object):
@builds("|=") @builds("|=")
@builds("^=") @builds("^=")
@builds("&=") @builds("&=")
@builds_if("@=", PY35)
@checkargs(2) @checkargs(2)
def compile_augassign_expression(self, expression): def compile_augassign_expression(self, expression):
ops = {"+=": ast.Add, ops = {"+=": ast.Add,
@ -1725,6 +1737,8 @@ class HyASTCompiler(object):
"|=": ast.BitOr, "|=": ast.BitOr,
"^=": ast.BitXor, "^=": ast.BitXor,
"&=": ast.BitAnd} "&=": ast.BitAnd}
if PY35:
ops.update({"@=": ast.MatMult})
op = ops[expression[0]] op = ops[expression[0]]

View File

@ -1,3 +1,5 @@
(import [hy._compat [PY35]])
(setv square (fn [x] (setv square (fn [x]
(* x x))) (* x x)))
@ -140,3 +142,52 @@
(defn overflow-int-to-long [] (defn overflow-int-to-long []
"NATIVE: test if int does not raise an overflow exception" "NATIVE: test if int does not raise an overflow exception"
(assert (integer? (+ 1 1000000000000000000000000)))) (assert (integer? (+ 1 1000000000000000000000000))))
(defclass HyTestMatrix [list]
[[--matmul--
(fn [self other]
(let [[n (len self)]
[m (len (. other [0]))]
[result []]]
(for [i (range m)]
(let [[result-row []]]
(for [j (range n)]
(let [[dot-product 0]]
(for [k (range (len (. self [0])))]
(+= dot-product (* (. self [i] [k])
(. other [k] [j]))))
(.append result-row dot-product)))
(.append result result-row)))
result))]])
(def first-test-matrix (HyTestMatrix [[1 2 3]
[4 5 6]
[7 8 9]]))
(def second-test-matrix (HyTestMatrix [[2 0 0]
[0 2 0]
[0 0 2]]))
(def product-of-test-matrices (HyTestMatrix [[ 2 4 6]
[ 8 10 12]
[14 16 18]]))
(defn test-matmul []
"NATIVE: test matrix multiplication"
(if PY35
(assert (= (@ first-test-matrix second-test-matrix)
product-of-test-matrices))
;; Python <= 3.4
(let [[matmul-attempt (try (@ first-test-matrix second-test-matrix)
(catch [e [Exception]] e))]]
(assert (isinstance matmul-attempt NameError)))))
(defn test-augassign-matmul []
"NATIVE: test augmented-assignment matrix multiplication"
(let [[matrix first-test-matrix]
[matmul-attempt (try (@= matrix second-test-matrix)
(catch [e [Exception]] e))]]
(if PY35
(assert (= product-of-test-matrices matrix))
(assert (isinstance matmul-attempt NameError)))))