Skip to content

Commit

Permalink
Add optional type argument to array generating functions
Browse files Browse the repository at this point in the history
This commit also resolves issue #1 and issue #3.
  • Loading branch information
Symbolics committed Jul 15, 2023
1 parent 0a8ef6c commit 027a2dc
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 62 deletions.
4 changes: 4 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,12 @@ Contributions are what make the open source community such an amazing place to b
<!-- LICENSE -->
## 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 -->
## Contact
Expand Down
18 changes: 9 additions & 9 deletions array-operations.asd
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@
;;; 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 <[email protected]>"
:maintainer "Steve Nunez"
:version "1.0.0"
:maintainer "Steve Nunez <[email protected]>"
: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
:description "Unit tests for the ARRAY-OPERATIONS library."
:author "Tamas K. Papp <[email protected]>"
: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)
Expand Down
2 changes: 1 addition & 1 deletion description.text
Original file line number Diff line number Diff line change
@@ -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.
93 changes: 50 additions & 43 deletions src/creating.lisp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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."
Expand All @@ -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.
Expand All @@ -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)))
Expand Down Expand Up @@ -146,24 +151,25 @@ 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.
(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))
Expand All @@ -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))
Expand Down
16 changes: 9 additions & 7 deletions src/transforming.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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."))

Expand Down Expand Up @@ -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)))
Expand Down

0 comments on commit 027a2dc

Please sign in to comment.