You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MatrixFunction.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.ss.formula.functions;
  16. import org.apache.poi.ss.formula.CacheAreaEval;
  17. import org.apache.poi.ss.formula.eval.AreaEval;
  18. import org.apache.poi.ss.formula.eval.ErrorEval;
  19. import org.apache.poi.ss.formula.eval.EvaluationException;
  20. import org.apache.poi.ss.formula.eval.NumberEval;
  21. import org.apache.poi.ss.formula.eval.OperandResolver;
  22. import org.apache.poi.ss.formula.eval.ValueEval;
  23. import org.apache.commons.math3.exception.DimensionMismatchException;
  24. import org.apache.commons.math3.linear.Array2DRowRealMatrix;
  25. import org.apache.commons.math3.linear.LUDecomposition;
  26. import org.apache.commons.math3.linear.MatrixUtils;
  27. /**
  28. * @author Robert Hulbert
  29. */
  30. public abstract class MatrixFunction implements Function{
  31. public static final void checkValues(double[] results) throws EvaluationException {
  32. for (int idx = 0; idx < results.length; idx++) {
  33. if (Double.isNaN(results[idx]) || Double.isInfinite(results[idx])) {
  34. throw new EvaluationException(ErrorEval.NUM_ERROR);
  35. }
  36. }
  37. }
  38. protected final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
  39. ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
  40. return OperandResolver.coerceValueToDouble(ve);
  41. }
  42. /* converts 1D array to 2D array for calculations */
  43. private static double[][] fillDoubleArray(double[] vector, int rows, int cols) throws EvaluationException {
  44. int i = 0, j = 0;
  45. if (rows < 1 || cols < 1 || vector.length < 1) {
  46. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  47. }
  48. double[][] matrix = new double[rows][cols];
  49. for (int idx = 0; idx < vector.length; idx++) {
  50. if (j < matrix.length) {
  51. if (i == matrix[0].length) {
  52. i = 0;
  53. j++;
  54. }
  55. matrix[j][i++] = vector[idx];
  56. }
  57. }
  58. return matrix;
  59. }
  60. /* retrieves 1D array from 2D array after calculations */
  61. private static double[] extractDoubleArray(double[][] matrix) throws EvaluationException {
  62. int idx = 0;
  63. if (matrix == null || matrix.length < 1 || matrix[0].length < 1) {
  64. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  65. }
  66. double[] vector = new double[matrix.length * matrix[0].length];
  67. for (int j = 0; j < matrix.length; j++) {
  68. for (int i = 0; i < matrix[0].length; i++) {
  69. vector[idx++] = matrix[j][i];
  70. }
  71. }
  72. return vector;
  73. }
  74. public static abstract class OneArrayArg extends Fixed1ArgFunction {
  75. protected OneArrayArg() {
  76. //no fields to initialize
  77. }
  78. @Override
  79. public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
  80. if (arg0 instanceof AreaEval) {
  81. double result[] = null, resultArray[][];
  82. int width = 1, height = 1;
  83. try {
  84. double values[] = collectValues(arg0);
  85. double array[][] = fillDoubleArray(values,((AreaEval) arg0).getHeight(),((AreaEval) arg0).getWidth());
  86. resultArray = evaluate(array);
  87. width = resultArray[0].length;
  88. height = resultArray.length;
  89. result = extractDoubleArray(resultArray);
  90. checkValues(result);
  91. }
  92. catch(EvaluationException e){
  93. return e.getErrorEval();
  94. }
  95. ValueEval vals[] = new ValueEval[result.length];
  96. for (int idx = 0; idx < result.length; idx++) {
  97. vals[idx] = new NumberEval(result[idx]);
  98. }
  99. if (result.length == 1) {
  100. return vals[0];
  101. }
  102. else {
  103. /* find a better solution */
  104. return new CacheAreaEval(((AreaEval) arg0).getFirstRow(), ((AreaEval) arg0).getFirstColumn(),
  105. ((AreaEval) arg0).getFirstRow() + height - 1,
  106. ((AreaEval) arg0).getFirstColumn() + width - 1, vals);
  107. }
  108. }
  109. else {
  110. double result[][] = null;
  111. try {
  112. double value = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
  113. double temp[][] = {{value}};
  114. result = evaluate(temp);
  115. NumericFunction.checkValue(result[0][0]);
  116. }
  117. catch (EvaluationException e) {
  118. return e.getErrorEval();
  119. }
  120. return new NumberEval(result[0][0]);
  121. }
  122. }
  123. protected abstract double[][] evaluate(double[][] d1) throws EvaluationException;
  124. protected abstract double[] collectValues(ValueEval arg) throws EvaluationException;
  125. }
  126. public static abstract class TwoArrayArg extends Fixed2ArgFunction {
  127. protected TwoArrayArg() {
  128. //no fields to initialize
  129. }
  130. @Override
  131. public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
  132. double result[];
  133. int width = 1, height = 1;
  134. try {
  135. double array0[][], array1[][], resultArray[][];
  136. if (arg0 instanceof AreaEval) {
  137. try {
  138. double values[] = collectValues(arg0);
  139. array0 = fillDoubleArray(values, ((AreaEval) arg0).getHeight(), ((AreaEval) arg0).getWidth());
  140. }
  141. catch(EvaluationException e) {
  142. return e.getErrorEval();
  143. }
  144. }
  145. else {
  146. try {
  147. double value = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
  148. array0 = new double[][] {{value}};
  149. }
  150. catch (EvaluationException e) {
  151. return e.getErrorEval();
  152. }
  153. }
  154. if (arg1 instanceof AreaEval) {
  155. try {
  156. double values[] = collectValues(arg1);
  157. array1 = fillDoubleArray(values, ((AreaEval) arg1).getHeight(),((AreaEval) arg1).getWidth());
  158. }
  159. catch (EvaluationException e) {
  160. return e.getErrorEval();
  161. }
  162. }
  163. else {
  164. try {
  165. double value = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
  166. array1 = new double[][] {{value}};
  167. }
  168. catch (EvaluationException e) {
  169. return e.getErrorEval();
  170. }
  171. }
  172. resultArray = evaluate(array0, array1);
  173. width = resultArray[0].length;
  174. height = resultArray.length;
  175. result = extractDoubleArray(resultArray);
  176. checkValues(result);
  177. }
  178. catch (EvaluationException e) {
  179. return e.getErrorEval();
  180. }
  181. catch (IllegalArgumentException e) {
  182. return ErrorEval.VALUE_INVALID;
  183. }
  184. ValueEval vals[] = new ValueEval[result.length];
  185. for (int idx = 0; idx < result.length; idx++) {
  186. vals[idx] = new NumberEval(result[idx]);
  187. }
  188. if (result.length == 1)
  189. return vals[0];
  190. else {
  191. return new CacheAreaEval(((AreaEval) arg0).getFirstRow(), ((AreaEval) arg0).getFirstColumn(),
  192. ((AreaEval) arg0).getFirstRow() + height - 1,
  193. ((AreaEval) arg0).getFirstColumn() + width - 1, vals);
  194. }
  195. }
  196. protected abstract double[][] evaluate(double[][] d1, double[][] d2) throws EvaluationException;
  197. protected abstract double[] collectValues(ValueEval arg) throws EvaluationException;
  198. }
  199. public static final class MutableValueCollector extends MultiOperandNumericFunction {
  200. public MutableValueCollector(boolean isReferenceBoolCounted, boolean isBlankCounted) {
  201. super(isReferenceBoolCounted, isBlankCounted);
  202. }
  203. public double[] collectValues(ValueEval...operands) throws EvaluationException {
  204. return getNumberArray(operands);
  205. }
  206. protected double evaluate(double[] values) {
  207. throw new IllegalStateException("should not be called");
  208. }
  209. }
  210. public static final Function MINVERSE = new OneArrayArg() {
  211. private final MutableValueCollector instance = new MutableValueCollector(false, false);
  212. protected double[] collectValues(ValueEval arg) throws EvaluationException {
  213. double[] values = instance.collectValues(arg);
  214. /* handle case where MDETERM is operating on an array that that is not completely filled*/
  215. if (arg instanceof AreaEval && values.length == 1)
  216. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  217. return values;
  218. }
  219. protected double[][] evaluate(double[][] d1) throws EvaluationException {
  220. if (d1.length != d1[0].length) {
  221. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  222. }
  223. Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
  224. return MatrixUtils.inverse(temp).getData();
  225. }
  226. };
  227. public static final Function TRANSPOSE = new OneArrayArg() {
  228. private final MutableValueCollector instance = new MutableValueCollector(false, true);
  229. protected double[] collectValues(ValueEval arg) throws EvaluationException {
  230. return instance.collectValues(arg);
  231. }
  232. protected double[][] evaluate(double[][] d1) throws EvaluationException {
  233. Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
  234. return temp.transpose().getData();
  235. }
  236. };
  237. public static final Function MDETERM = new OneArrayArg() {
  238. private final MutableValueCollector instance = new MutableValueCollector(false, false);
  239. protected double[] collectValues(ValueEval arg) throws EvaluationException {
  240. double[] values = instance.collectValues(arg);
  241. /* handle case where MDETERM is operating on an array that that is not completely filled*/
  242. if (arg instanceof AreaEval && values.length == 1)
  243. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  244. return instance.collectValues(arg);
  245. }
  246. protected double[][] evaluate(double[][] d1) throws EvaluationException {
  247. if (d1.length != d1[0].length) {
  248. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  249. }
  250. double result[][] = new double[1][1];
  251. Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
  252. result[0][0] = (new LUDecomposition(temp)).getDeterminant();
  253. return result;
  254. }
  255. };
  256. public static final Function MMULT = new TwoArrayArg() {
  257. private final MutableValueCollector instance = new MutableValueCollector(false, false);
  258. protected double[] collectValues(ValueEval arg) throws EvaluationException {
  259. double values[] = instance.collectValues(arg);
  260. /* handle case where MMULT is operating on an array that is not completely filled*/
  261. if (arg instanceof AreaEval && values.length == 1)
  262. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  263. return values;
  264. }
  265. protected double[][] evaluate(double[][] d1, double[][] d2) throws EvaluationException{
  266. Array2DRowRealMatrix first = new Array2DRowRealMatrix(d1);
  267. Array2DRowRealMatrix second = new Array2DRowRealMatrix(d2);
  268. try {
  269. MatrixUtils.checkMultiplicationCompatible(first, second);
  270. }
  271. catch (DimensionMismatchException e) {
  272. throw new EvaluationException(ErrorEval.VALUE_INVALID);
  273. }
  274. return first.multiply(second).getData();
  275. }
  276. };
  277. }