diff --git a/LICENSE.md b/LICENSE.md index cad91a3..50a7dec 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -29,3 +29,7 @@ A "contributor" is any person that distributes its contribution under this licen (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + + + +Original portions of the code are licensed under the MIT license diff --git a/README.md b/README.md index ac1cc0c..144da34 100644 --- a/README.md +++ b/README.md @@ -158,11 +158,12 @@ Contributions are what make the open source community such an amazing place to b ## License -Distributed under the MS-PL License. See [LICENSE](LICENSE.md) for more information. +The original code from Tamas Papp and Ben Dudson is licensed under the MIT license. Modifications by Symbolics are +distributed under the MS-PL License. See [LICENSE](LICENSE.md) for more information. ## Notes -Expect spurious warnings from SBCL related to 'deleting unreachable code' when running the tests. The tests it's warning about _do_ run and we haven't had the time to debug SBCL's warnings. +Expect spurious warnings from SBCL related to 'deleting unreachable code' when running the tests. The tests it's warning about _do_ run and we haven't had the time to debug SBCL's warnings. See issue #4. ## Contact diff --git a/array-operations.asd b/array-operations.asd index f36a3ad..9ee8a24 100644 --- a/array-operations.asd +++ b/array-operations.asd @@ -2,23 +2,23 @@ ;;; Copyright (c) 2012-2018 by Tamas Papp. All rights reserved. ;;; Copyright (c) 2019-2022 by Ben Dudson. All rights reserved. ;;; Copyright (c) 2021-2023 by Symbolics Pte. Ltd. All rights reserved. +;;; SPDX-License-identifier: MS-PL -(defsystem #:array-operations - :version "1.1.0" +(defsystem "array-operations" + :version "1.2.0" :description "Array operations library for Common Lisp" :long-description #.(uiop:read-file-string (uiop:subpathname *load-pathname* "description.text")) :author "Tamas K. Papp " - :maintainer "Steve Nunez" - :version "1.0.0" + :maintainer "Steve Nunez " :long-name "Array operations for array-like data structures" :homepage "https://lisp-stat.dev/docs/manuals/array-operations" :bug-tracker "https://github.com/Lisp-Stat/array-operations/issues" :license :MS-PL :class :package-inferred-system :pathname "src/" - :depends-on (#:let-plus - :array-operations/all) + :depends-on ("let-plus" + "array-operations/all") :in-order-to ((test-op (test-op :array-operations/tests)))) (defsystem #:array-operations/tests @@ -26,9 +26,9 @@ :author "Tamas K. Papp " :maintainer "Steve Nunez" :license :MS-PL - :depends-on (:array-operations ; loads everything else - :alexandria - :clunit2) + :depends-on ("array-operations" + "alexandria" + "clunit2") :pathname "tests/" :components ((:file "tests")) :perform (test-op (o c) diff --git a/description.text b/description.text index 6538802..b557571 100644 --- a/description.text +++ b/description.text @@ -1,3 +1,3 @@ -The array-operations system is a collection of functions and macros for manipulating Common Lisp arrays and performing numerical calculations with them. +The array-operations system is a collection of functions and macros for manipulating Common Lisp arrays and performing numerical calculations with them. It is extendible to array-like data structures, such as data frames. It is a core system for solving linear systems of equations in common lisp. Array-operations is a 'generic' way of operating on array like data structures using a syntax that is natural for Common Lisp. Several aops functions have been implemented for data-frame. For those that haven't, you can transform arrays to data frames using the df:matrix-df function, and a data-frame to an array using df:as-array. This make it convenient to work with the data sets using either system. diff --git a/src/creating.lisp b/src/creating.lisp index d1b9e7a..4998c71 100644 --- a/src/creating.lisp +++ b/src/creating.lisp @@ -1,7 +1,8 @@ ;;; -*- Mode: LISP; Base: 10; Syntax: ANSI-Common-lisp; Package: ARRAY-OPERATIONS/CREATING -*- ;;; Copyright (c) 2012-2018 by Tamas Papp. All rights reserved. ;;; Copyright (c) 2018-2022 by Ben Dudson. All rights reserved. -;;; Copyright (c) 2021-2022 by Symbolics Pte. Ltd. All rights reserved. +;;; Copyright (c) 2021-2023 by Symbolics Pte. Ltd. All rights reserved. +;;; SPDX-License-identifier: MS-PL (defpackage :array-operations/creating (:use :cl :array-operations/generic @@ -39,10 +40,14 @@ Returns ARRAY." :element-type element-type :initial-element (coerce 0 element-type))) -(defun zeros (dimensions) - "Makes an array of shape DIMENSIONS and type T, filled with zeros" - (make-array (ensure-dimensions dimensions) - :initial-element 0)) +(defun zeros (dimensions &optional element-type) + "Make an array of shape DIMENSIONS filled with zeros. Elements are of type T, unless ELEMENT-TYPE is given." + (assert (subtypep element-type 'number) + (element-type) + "Cannot create a ZEROS array because ~A is not a numeric type." element-type) + (if element-type + (zeros* element-type dimensions) + (make-array (ensure-dimensions dimensions) :initial-element 0))) (defun ones! (array) "Fills the given ARRAY with 1's, coerced to the element type. Returns ARRAY." @@ -55,10 +60,14 @@ Returns ARRAY." :element-type element-type :initial-element (coerce 1 element-type))) -(defun ones (dimensions) - "Makes an array of shape DIMENSIONS and type T, filled with ones" - (make-array (ensure-dimensions dimensions) - :initial-element 1)) +(defun ones (dimensions &optional element-type) + "Makes an array of shape DIMENSIONS filled with ones. Elements are of type T, unless ELEMENT-TYPE is given." + (assert (subtypep element-type 'number) + (element-type) + "Cannot create a ONES array because ~A is not a numeric type." element-type) + (if element-type + (ones* element-type dimensions) + (make-array (ensure-dimensions dimensions) :initial-element 1))) (defun rand! (array) "Fills a given ARRAY with random numbers, uniformly distributed between 0 and 1. Uses the built-in RANDOM function. @@ -77,36 +86,32 @@ Returns ARRAY." (rand 3) -> #(0.39319038 0.69693553 0.5021677) (rand '(2 2)) -> #2A((0.91003513 0.23208928) (0.5577954 0.94657767)) - NOTE: If it's important that these numbers are really random - (e.g. cryptographic applications), then you should probably - not use this function. - " + NOTE: If it's important that these numbers are really random (e.g. cryptographic applications), then you should probably not use this function. " (rand! (make-array (ensure-dimensions dimensions) :element-type element-type))) -(defun rand (dimensions) - "Makes an array of shape DIMENSIONS and type T, filled with random numbers uniformly distributed between 0 and 1. +(defun rand (dimensions &optional element-type) + "Makes an array of shape DIMENSIONS filled with random numbers uniformly distributed between 0 and 1. Elements are of type T, unless ELEMENT-TYPE is given. Uses the built-in RANDOM function. (rand 3) -> #(0.39319038 0.69693553 0.5021677) (rand '(2 2)) -> #2A((0.91003513 0.23208928) (0.5577954 0.94657767)) - NOTE: If it's important that these numbers are really random - (e.g. cryptographic applications), then you should probably - not use this function. - " - (rand* t dimensions)) + NOTE: If it's important that these numbers are really random (e.g. cryptographic applications), then you should probably not use this function." + (assert (subtypep element-type 'number) + (element-type) + "Cannot create a RAND array because ~A is not a numeric type." element-type) + (if element-type + (rand* element-type dimensions) + (rand* t dimensions))) (defun randn! (array) "Fills ARRAY with normally distributed numbers with a mean of zero and standard deviation of 1 Uses the Box-Muller algorithm and built-in random number generator. - NOTE: If it's important that these numbers are really random - (e.g. cryptographic applications), then you should probably - not use this function. - " + NOTE: If it's important that these numbers are really random (e.g. cryptographic applications), then you should probably not use this function." (let ((element-type (array-element-type array)) (size (array-total-size array))) (do ((i 0 (+ 2 i))) @@ -146,7 +151,7 @@ Returns ARRAY." (randn! (make-array (ensure-dimensions dimensions) :element-type element-type))) -(defun randn (dimensions) +(defun randn (dimensions &optional element-type) "Creates an array of shape DIMENSIONS and type T, and fills with normally distributed numbers with a mean of zero and standard deviation of 1 Uses the Box-Muller algorithm and built-in random number generator. @@ -154,16 +159,17 @@ Returns ARRAY." (rand 3) -> #(-0.82067037 -0.60068226 -0.21494178) (randn '(2 2)) -> #2A((1.6905352 -2.5379088) (0.8461403 -1.505984)) - NOTE: If it's important that these numbers are really random - (e.g. cryptographic applications), then you should probably - not use this function. - " - (randn* t dimensions)) + NOTE: If it's important that these numbers are really random (e.g. cryptographic applications), then you should probably not use this function." + (assert (subtypep element-type 'number) + (element-type) + "Cannot create a normal random array because ~A is not a numeric type." element-type) + (if element-type + (randn* element-type dimensions) + (randn* t dimensions))) + (defun linspace! (array start stop) - "Fill an array with evenly spaced numbers over an interval. - The first element is equal to START and last element STOP, - with constant difference between consecutive elements in ROW-MAJOR-INDEX." + "Fill an array with evenly spaced numbers over an interval. The first element is equal to START and last element STOP, with constant difference between consecutive elements in ROW-MAJOR-INDEX." (assert (> stop start) (start stop) "Stop must be greater than start.") (let* ((size (array-total-size array)) (element-type (array-element-type array)) @@ -174,21 +180,22 @@ Returns ARRAY." array)) (defun linspace* (element-type start stop n) - "Make a vector of N elements and type ELEMENT-TYPE, containing evenly spaced numbers over an interval. - The first element is equal to START and last element STOP, - with constant difference between consecutive elements." + "Make a vector of N elements and type ELEMENT-TYPE, containing evenly spaced numbers over an interval. The first element is equal to START and last element STOP, with constant difference between consecutive elements." (linspace! (make-array n :element-type element-type) start stop)) -(defun linspace (start stop n) - "Make a vector of N elements and type T, containing evenly spaced numbers over an interval. - The first element is equal to START and last element STOP, - with constant difference between consecutive elements. +(defun linspace (start stop n &optional element-type) + "Make a vector of N elements containing evenly spaced numbers over an interval. The first element is equal to START and last element STOP, with constant difference between consecutive elements. Elements are of type T, unless ELEMENT-TYPE is given (linspace 0 4 5) -> #(0 1 2 3 4) (linspace 1 3 5) -> #(0 1/2 1 3/2 2) - (linspace 0 4d0 3) -> #(0.0d0 2.0d0 4.0d0) - " - (linspace* t start stop n)) + (linspace 0 4d0 3) -> #(0.0d0 2.0d0 4.0d0)" + (assert (subtypep element-type 'number) + (element-type) + "Cannot create a linear sequence of numbers because ~A is not a numeric type." element-type) + (if element-type + (linspace* element-type start stop n) + (linspace* t start stop n))) + (defun similar-array (array &key (dimensions (dims array)) (adjustable (adjustable-array-p array)) diff --git a/src/transforming.lisp b/src/transforming.lisp index 6209f81..273eb94 100644 --- a/src/transforming.lisp +++ b/src/transforming.lisp @@ -2,6 +2,7 @@ ;;; Copyright (c) 2012-2018 by Tamas Papp. All rights reserved. ;;; Copyright (c) 2018-2022 by Ben Dudson. All rights reserved. ;;; Copyright (c) 2021-2023 by Symbolics Pte. Ltd. All rights reserved. +;;; SPDX-License-identifier: MS-PL (defpackage :array-operations/transforming (:use :cl :array-operations/generic @@ -27,7 +28,8 @@ :permutation-incompatible-rank :permute :recycle - :map-array + :map-array + :array-index-row-major :turn) (:documentation "Functions for transforming arrays in various ways.")) @@ -315,14 +317,14 @@ Array element type is preserved." ;;; turning -;; https://groups.google.com/g/comp.lang.lisp/c/CM3MQkyOTHk/m/Pl4KPUqfobwJ -;; Modified into a destructive, non-consing version. RESULT, if provided, -;; must be a list of length equal to the rank of the array; it is modified to -;; contain the result of the function. This way, the result list can be -;; allocated only once and additionally be DX for zero consing. If RESULT is -;; not provided, then it is consed and then returned. (declaim (inline array-index-row-major)) (defun array-index-row-major (array rmi &optional result) + "https://groups.google.com/g/comp.lang.lisp/c/CM3MQkyOTHk/m/Pl4KPUqfobwJ +Modified into a destructive, non-consing version. RESULT, if provided, +must be a list of length equal to the rank of the array; it is modified to +contain the result of the function. This way, the result list can be +allocated only once and additionally be DX for zero consing. If RESULT is +not provided, then it is consed and then returned." (declare (optimize speed)) (let* ((rank (array-rank array)) (dimensions (make-list rank)))