Initial commit

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@476930 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Phil Steitz 2006-11-19 21:36:15 +00:00
parent c0b59b0810
commit 05195b77ca
255 changed files with 43155 additions and 0 deletions

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

103
src/mantissa/NOTICE.txt Normal file
View File

@ -0,0 +1,103 @@
Apache commons-math
Copyright 2006 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
This product includes software developed by
Luc Maisonobe and licensed to the Apache Software Foundation.
This product includes software translated from the lmder, lmpar
and qrsolv Fortran routines from the Minpack package and
distributed under the following disclaimer:
---------- http://www.netlib.org/minpack/disclaimer ----------
Minpack Copyright Notice (1999) University of Chicago. All rights reserved
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The end-user documentation included with the
redistribution, if any, must include the following
acknowledgment:
"This product includes software developed by the
University of Chicago, as Operator of Argonne National
Laboratory.
Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments
normally appear.
4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
BE CORRECTED.
5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
POSSIBILITY OF SUCH LOSS OR DAMAGES.
---------- http://www.netlib.org/minpack/disclaimer ----------
This product includes software translated from the odex Fortran routine
developed by E. Hairer and G. Wanner and distributed under the following
license:
---------- http://www.unige.ch/~hairer/prog/licence.txt ----------
Copyright (c) 2004, Ernst Hairer
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------- http://www.unige.ch/~hairer/prog/licence.txt ----------

View File

@ -0,0 +1,119 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
/** This class is the base class for all specific exceptions thrown by
* the mantissa classes.
* <p>When the mantissa classes throw exceptions that are specific to
* the package, these exceptions are always subclasses of
* MantissaException. When exceptions that are already covered by the
* standard java API should be thrown, like
* ArrayIndexOutOfBoundsException or IllegalArgumentException, these
* standard exceptions are thrown rather than the mantissa specific
* ones.</p>
* @version $Id: MantissaException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class MantissaException
extends Exception {
private static final long serialVersionUID = 1L;
private static ResourceBundle resources
= ResourceBundle.getBundle("org.spaceroots.mantissa.MessagesResources");
/** Translate a string.
* @param s string to translate
* @return translated string
*/
public static String translate(String s) {
try {
return resources.getString(s);
} catch (MissingResourceException mre) {
return s;
}
}
/** Translate a message.
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
* @return translated message
*/
public static String translate(String specifier, String[] parts) {
return new MessageFormat(translate(specifier)).format(parts);
}
/** Simple constructor.
* Build an exception with an empty message
*/
public MantissaException() {
super();
}
/** Simple constructor.
* Build an exception by translating the specified message
* @param message message to translate
*/
public MantissaException(String message) {
super(translate(message));
}
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public MantissaException(String specifier, String[] parts) {
super(translate(specifier, parts));
}
/** Simple constructor.
* Build an exception from a cause
* @param cause cause of this exception
*/
public MantissaException(Throwable cause) {
super(cause);
}
/** Simple constructor.
* Build an exception from a message and a cause
* @param message message to translate
* @param cause cause of this exception
*/
public MantissaException(String message, Throwable cause) {
super(translate(message), cause);
}
/** Simple constructor.
* Build an exception from a message and a cause
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
* @param cause cause of this exception
*/
public MantissaException(String specifier, String[] parts, Throwable cause) {
super(translate(specifier, parts), cause);
}
}

View File

@ -0,0 +1,113 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa;
import java.util.ListResourceBundle;
/** This class gather the message resources for the mantissa library.
* @version $Id: MessagesResources.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class MessagesResources
extends ListResourceBundle {
/** Simple constructor.
*/
public MessagesResources() {
}
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
// org.spaceroots.mantissa.estimation.GaussNewtonEstimator
{ "unable to converge in {0} iterations",
"unable to converge in {0} iterations" },
// org.spaceroots.mantissa.estimation.LevenbergMarquardtEstimator
{ "cost relative tolerance is too small ({0}), no further reduction in the sum of squares is possible",
"cost relative tolerance is too small ({0}), no further reduction in the sum of squares is possible" },
{ "parameters relative tolerance is too small ({0}), no further improvement in the approximate solution is possible",
"parameters relative tolerance is too small ({0}), no further improvement in the approximate solution is possible" },
{ "orthogonality tolerance is too small ({0}), solution is orthogonal to the jacobian",
"orthogonality tolerance is too small ({0}), solution is orthogonal to the jacobian" },
{ "maximal number of evaluations exceeded ({0})",
"maximal number of evaluations exceeded ({0})" },
// org.spaceroots.mantissa.fitting.HarmonicCoefficientsGuesser
{ "unable to guess a first estimate",
"unable to guess a first estimate" },
// org.spaceroots.mantissa.fitting.HarmonicFitter
{ "sample must contain at least {0} points",
"sample must contain at least {0} points" },
// org.spaceroots.mantissa.functions.ExhaustedSampleException
{ "sample contains only {0} elements",
"sample contains only {0} elements" },
// org.spaceroots.mantissa.geometry.CardanEulerSingularityException
{ "Cardan angles singularity",
"Cardan angles singularity" },
{ "Euler angles singularity",
"Euler angles singularity" },
// org.spaceroots.mantissa.geometry.Rotation
{ "a {0}x{1} matrix cannot be a rotation matrix",
"a {0}x{1} matrix cannot be a rotation matrix" },
{ "the closest orthogonal matrix has a negative determinant {0}",
"the closest orthogonal matrix has a negative determinant {0}" },
{ "unable to orthogonalize matrix in {0} iterations",
"unable to orthogonalize matrix in {0} iterations" },
// org.spaceroots.mantissa.linalg;.SingularMatrixException
{ "singular matrix",
"singular matrix" },
// org.spaceroots.mantissa.ode.AdaptiveStepsizeIntegrator
{ "minimal step size ({0}) reached, integration needs {1}",
"minimal step size ({0}) reached, integration needs {1}" },
// org.spaceroots.mantissa.ode.GraggBulirschStoerIntegrator,
// org.spaceroots.mantissa.ode.RungeKuttaFehlbergIntegrator,
// org.spaceroots.mantissa.ode.RungeKuttaIntegrator
{ "dimensions mismatch: ODE problem has dimension {0},"
+ " state vector has dimension {1}",
"dimensions mismatch: ODE problem has dimension {0},"
+ " state vector has dimension {1}" },
{ "too small integration interval: length = {0}",
"too small integration interval: length = {0}" },
// org.spaceroots.mantissa.optimization.DirectSearchOptimizer
{ "none of the {0} start points lead to convergence",
"none of the {0} start points lead to convergence" },
// org.spaceroots.mantissa.random.CorrelatedRandomVectorGenerator
{ "dimension mismatch {0} != {1}",
"dimension mismatch {0} != {1}" },
// org.spaceroots.mantissa.random.NotPositiveDefiniteMatrixException
{ "not positive definite matrix",
"not positive definite matrix" }
};
}

View File

@ -0,0 +1,112 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa;
import java.util.ListResourceBundle;
/** This class gather the message resources for the mantissa library.
* @version $Id: MessagesResources_fr.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class MessagesResources_fr
extends ListResourceBundle {
/** Simple constructor.
*/
public MessagesResources_fr() {
}
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
// org.spaceroots.mantissa.estimation.GaussNewtonEstimator
{ "unable to converge in {0} iterations",
"pas de convergence apr\u00e8s {0} it\u00e9rations" },
// org.spaceroots.mantissa.estimation.LevenbergMarquardtEstimator
{ "cost relative tolerance is too small ({0}), no further reduction in the sum of squares is possible",
"trop petite tol\u00e9rance relative sur le co\u00fbt ({0}), aucune r\u00e9duction de la somme des carr\u00e9s n''est possible" },
{ "parameters relative tolerance is too small ({0}), no further improvement in the approximate solution is possible",
"trop petite tol\u00e9rance relative sur les param\u00e8tres ({0}), aucune am\u00e9lioration de la solution approximative n''est possible" },
{ "orthogonality tolerance is too small ({0}), solution is orthogonal to the jacobian",
"trop petite tol\u00e9rance sur l''orthogonalit\u00e9 ({0}), la solution est orthogonale \u00e0 la jacobienne" },
{ "maximal number of evaluations exceeded ({0})",
"nombre maximal d''\u00e9valuations d\u00e9pass\u00e9 ({0})" },
// org.spaceroots.mantissa.fitting.HarmonicCoefficientsGuesser
{ "unable to guess a first estimate",
"impossible de trouver une premi\u00e8re estim\u00e9e" },
// org.spaceroots.mantissa.fitting.HarmonicFitter
{ "sample must contain at least {0} points",
"l''\u00e9chantillon doit contenir au moins {0} points" },
// org.spaceroots.mantissa.functions.ExhaustedSampleException
{ "sample contains only {0} elements",
"l''\u00e9chantillon ne contient que {0} points" },
// org.spaceroots.mantissa.geometry.CardanEulerSingularityException
{ "Cardan angles singularity",
"singularit\u00e9 d''angles de Cardan" },
{ "Euler angles singularity",
"singularit\u00e9 d''angles d''Euler" },
// org.spaceroots.mantissa.geometry.Rotation
{ "a {0}x{1} matrix cannot be a rotation matrix",
"une matrice {0}x{1} ne peut pas \u00e9tre une matrice de rotation" },
{ "the closest orthogonal matrix has a negative determinant {0}",
"la matrice orthogonale la plus proche a un d\u00e9terminant n\u00e9gatif {0}" },
{ "unable to orthogonalize matrix in {0} iterations",
"impossible de rendre la matrice orthogonale en {0} it\u00e9rations" },
// org.spaceroots.mantissa.linalg;.SingularMatrixException
{ "singular matrix",
"matrice singuli\u00e8re" },
// org.spaceroots.mantissa.ode.AdaptiveStepsizeIntegrator
{ "minimal step size ({0}) reached, integration needs {1}",
"pas minimal ({0}) atteint, l''int\u00e9gration n\u00e9cessite {1}" },
// org.spaceroots.mantissa.ode.GraggBulirschStoerIntegrator,
// org.spaceroots.mantissa.ode.RungeKuttaFehlbergIntegrator,
// org.spaceroots.mantissa.ode.RungeKuttaIntegrator
{ "dimensions mismatch: ODE problem has dimension {0},"
+ " state vector has dimension {1}",
"incompatibilit\u00e9 de dimensions entre le probl\u00e8me ODE ({0}),"
+ " et le vecteur d'\u00e9tat ({1})" },
{ "too small integration interval: length = {0}",
"intervalle d''int\u00e9gration trop petit : {0}" },
// org.spaceroots.mantissa.optimization.DirectSearchOptimizer
{ "none of the {0} start points lead to convergence",
"aucun des {0} points de d\u00e9part n''aboutit \u00e0 une convergence" },
// org.spaceroots.mantissa.random.CorrelatedRandomVectorGenerator
{ "dimension mismatch {0} != {1}",
"dimensions incompatibles {0} != {1}" },
// org.spaceroots.mantissa.random.NotPositiveDefiniteMatrixException
{ "not positive definite matrix",
"matrice non d\u00e9finie positive" }
};
}

View File

@ -0,0 +1,105 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements Chebyshev polynomials.
* <p>Chebyshev polynomials can be defined by the following recurrence
* relations:
* <pre>
* T0(X) = 1
* T1(X) = X
* Tk+1(X) = 2X Tk(X) - Tk-1(X)
* </pre></p>
* @version $Id: Chebyshev.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class Chebyshev
extends OrthogonalPolynomial {
/** Simple constructor.
* Build a degree 0 Chebyshev polynomial
*/
public Chebyshev() {
super(0, l, maxDegree);
}
/** Simple constructor.
* Build a degree d Chebyshev polynomial
* @param d degree of the polynomial
*/
public Chebyshev(int d) {
super(d, l, maxDegree);
}
/** Initialize the recurrence coefficients.
* The recurrence relation is
* <pre>Tk+1(X) = 2X Tk(X) - Tk-1(X)</pre>
* @param k index of the current step
* @param b2k coefficient to initialize (b2k = a2k / a1k)
* @param b3k coefficient to initialize (b3k = a3k / a1k)
* @param b4k coefficient to initialize (b4k = a4k / a1k)
*/
protected void initRecurrenceCoefficients(int k,
RationalNumber b2k,
RationalNumber b3k,
RationalNumber b4k) {
b2k.reset(0l);
b3k.reset(2l);
b4k.reset(1l);
}
/** Set the maximal degree of already computed polynomials.
* @param d maximal degree of already computed polynomials
*/
protected void setMaxDegree(int d) {
maxDegree = d;
}
private static final long serialVersionUID = 8367010179599693222L;
/** List holding the coefficients of the polynomials computed so far. */
private static List l;
/** Maximal degree of the polynomials computed so far. */
private static int maxDegree;
/** Build the first two polynomials. */
static {
l = new ArrayList ();
// T0(X) = 1
l.add(new RationalNumber(1l));
// T1(X) = X
l.add(new RationalNumber(0l));
l.add(new RationalNumber(1l));
maxDegree = 1;
}
}

View File

@ -0,0 +1,104 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements Hermite polynomials.
* <p>Hermite polynomials can be defined by the following recurrence
* relations:
* <pre>
* H0(X) = 1
* H1(X) = 2X
* Hk+1(X) = 2X Hk(X) - 2k Hk-1(X)
* </pre></p>
* @version $Id: Hermite.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class Hermite
extends OrthogonalPolynomial {
/** Simple constructor.
* Build a degree 0 Hermite polynomial
*/
public Hermite() {
super(0, l, maxDegree);
}
/** Simple constructor.
* Build a degree d Hermite polynomial
* @param d degree of the polynomial
*/
public Hermite(int d) {
super(d, l, maxDegree);
}
/** Initialize the recurrence coefficients.
* The recurrence relation is
* <pre>Hk+1(X) = 2X Hk(X) - 2k Hk-1(X)</pre>
* @param k index of the current step
* @param b2k coefficient to initialize (b2k = a2k / a1k)
* @param b3k coefficient to initialize (b3k = a3k / a1k)
* @param b4k coefficient to initialize (b4k = a4k / a1k)
*/
protected void initRecurrenceCoefficients(int k,
RationalNumber b2k,
RationalNumber b3k,
RationalNumber b4k) {
b2k.reset(0l);
b3k.reset(2l);
b4k.reset(2l * k);
}
/** Set the maximal degree of already computed polynomials.
* @param d maximal degree of already computed polynomials
*/
protected void setMaxDegree(int d) {
maxDegree = d;
}
private static final long serialVersionUID = -4639726453485128770L;
/** Table holding the coefficients of the polynomials computed so far. */
private static List l;
/** Maximal degree of the polynomials computed so far. */
private static int maxDegree;
/** Build the first two polynomials. */
static {
l = new ArrayList ();
// H0(X) = 1
l.add(new RationalNumber(1l));
// H1(X) = 2X
l.add(new RationalNumber(0l));
l.add(new RationalNumber(2l));
maxDegree = 1;
}
}

View File

@ -0,0 +1,105 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements Laguerre polynomials.
* <p>Laguerre polynomials can be defined by the following recurrence
* relations:
* <pre>
* L0(X) = 1
* L1(X) = 1 - X
* (k+1) Lk+1(X) = (2k + 1 - X) Lk(X) - k Lk-1(X)
* </pre></p>
* @version $Id: Laguerre.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class Laguerre
extends OrthogonalPolynomial {
/** Simple constructor.
* Build a degree 0 Laguerre polynomial
*/
public Laguerre() {
super(0, l, maxDegree);
}
/** Simple constructor.
* Build a degree d Laguerre polynomial
* @param d degree of the polynomial
*/
public Laguerre(int d) {
super(d, l, maxDegree);
}
/** Initialize the recurrence coefficients.
* The recurrence relation is
* <pre>(k+1) Lk+1(X) = (2k + 1 - X) Lk(X) - k Lk-1(X)</pre>
* @param k index of the current step
* @param b2k coefficient to initialize (b2k = a2k / a1k)
* @param b3k coefficient to initialize (b3k = a3k / a1k)
* @param b4k coefficient to initialize (b4k = a4k / a1k)
*/
protected void initRecurrenceCoefficients(int k,
RationalNumber b2k,
RationalNumber b3k,
RationalNumber b4k) {
long kP1 = k + 1;
b2k.reset(2 * k + 1, kP1);
b3k.reset(-1l, kP1);
b4k.reset(k, kP1);
}
/** Set the maximal degree of already computed polynomials.
* @param d maximal degree of already computed polynomials
*/
protected void setMaxDegree(int d) {
maxDegree = d;
}
private static final long serialVersionUID = -750526984136835515L;
/** List holding the coefficients of the polynomials computed so far. */
private static List l;
/** Maximal degree of the polynomials computed so far. */
private static int maxDegree;
/** Build the first two polynomials. */
static {
l = new ArrayList ();
// L0(X) = 1
l.add(new RationalNumber(1l));
// L1(X) = 1 - X
l.add(new RationalNumber(1l));
l.add(new RationalNumber(-1l));
maxDegree = 1;
}
}

View File

@ -0,0 +1,106 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements Legendre polynomials.
* <p>Legendre polynomials can be defined by the following recurrence
* relations:
* <pre>
* P0(X) = 1
* P1(X) = X
* (k+1) Pk+1(X) = (2k+1) X Pk(X) - k Pk-1(X)
* </pre></p>
* @version $Id: Legendre.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class Legendre
extends OrthogonalPolynomial {
/** Simple constructor.
* Build a degree 0 Legendre polynomial
*/
public Legendre() {
super(0, l, maxDegree);
}
/** Simple constructor.
* Build a degree d Legendre polynomial
* @param d degree of the polynomial
*/
public Legendre(int d) {
super(d, l, maxDegree);
}
/** Initialize the recurrence coefficients.
* The recurrence relation is
* <pre>(k+1) Pk+1(X) = (2k+1) X Pk(X) - k Ok-1(X)</pre>
* @param k index of the current step
* @param b2k coefficient to initialize (b2k = a2k / a1k)
* @param b3k coefficient to initialize (b3k = a3k / a1k)
* @param b4k coefficient to initialize (b4k = a4k / a1k)
*/
protected void initRecurrenceCoefficients(int k,
RationalNumber b2k,
RationalNumber b3k,
RationalNumber b4k) {
long kP1 = k + 1;
b2k.reset(0l);
b3k.reset(2 * k + 1, kP1);
b4k.reset(k, kP1);
}
/** Set the maximal degree of already computed polynomials.
* @param d maximal degree of already computed polynomials
*/
protected void setMaxDegree(int d) {
maxDegree = d;
}
private static final long serialVersionUID = 428266828791532209L;
/** List holding the coefficients of the polynomials computed so far. */
private static List l;
/** Maximal degree of the polynomials computed so far. */
private static int maxDegree;
/** Build the first two polynomials. */
static {
l = new ArrayList ();
// P0(X) = 1
l.add(new RationalNumber(1l));
// P1(X) = X
l.add(new RationalNumber(0l));
l.add(new RationalNumber(1l));
maxDegree = 1;
}
}

View File

@ -0,0 +1,151 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.util.List;
/**
* This class is the base class for orthogonal polynomials.
* <p>Orthogonal polynomials can be defined by recurrence relations like:
* <pre>
* O0(X) = some 0 degree polynomial
* O1(X) = some first degree polynomial
* a1k Ok+1(X) = (a2k + a3k X) Ok(X) - a4k Ok-1(X)
* </pre>
* where a0k, a1k, a2k and a3k are simple expressions which either are
* constants or depend on k.</p>
* @version $Id: OrthogonalPolynomial.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class OrthogonalPolynomial
extends Polynomial.Rational {
/** Simple constructor.
* Build a degree d orthogonal polynomial
* @param d degree of the polynomial
* @param l list containing all coefficients already computed
* @param maxDegree maximal degree of computed coefficients, this
* coefficient <em>must</em> be greater or equal to 1, i.e. the
* derived class <em>must</em> have initialized the first two
* polynomials of degree 0 and 1 before this constructor can be
* called.
*/
protected OrthogonalPolynomial(int d, List l, int maxDegree) {
if (d > maxDegree) {
computeUpToDegree(d, l, maxDegree);
}
// coefficient for polynomial 0 is l [0]
// coefficient for polynomial 1 are l [1] ... l [2] (degrees 0 ... 1)
// coefficients for polynomial 2 are l [3] ... l [5] (degrees 0 ... 2)
// coefficients for polynomial 3 are l [6] ... l [9] (degrees 0 ... 3)
// coefficients for polynomial 4 are l[10] ... l[14] (degrees 0 ... 4)
// coefficients for polynomial 5 are l[15] ... l[20] (degrees 0 ... 5)
// coefficients for polynomial 6 are l[21] ... l[27] (degrees 0 ... 6)
// ...
int start = d * (d + 1) / 2;
a = new RationalNumber[d+1];
for (int i = 0; i <= d; ++i) {
a[i] = new RationalNumber((RationalNumber) l.get(start + i));
}
unknown = null;
}
/** Initialize the recurrence coefficients.
* The recurrence relation is
* <pre>a1k Ok+1(X) = (a2k + a3k X) Ok(X) - a4k Ok-1(X)</pre>
* @param k index of the current step
* @param b2k coefficient to initialize (b2k = a2k / a1k)
* @param b3k coefficient to initialize (b3k = a3k / a1k)
* @param b4k coefficient to initialize (b4k = a4k / a1k)
*/
protected abstract void initRecurrenceCoefficients(int k,
RationalNumber b2k,
RationalNumber b3k,
RationalNumber b4k);
/** Set the maximal degree of already computed polynomials.
* @param d maximal degree of already computed polynomials
*/
protected abstract void setMaxDegree(int d);
/** Compute all the polynomial coefficients up to a given degree.
* @param d maximal degree
* @param l list containing all coefficients already computed
* @param maxDegree maximal degree of computed coefficients
*/
protected void computeUpToDegree(int d, List l, int maxDegree) {
RationalNumber b2k = new RationalNumber();
RationalNumber b3k = new RationalNumber();
RationalNumber b4k = new RationalNumber();
int startK = (maxDegree - 1) * maxDegree / 2;
for (int k = maxDegree; k < d; ++k) {
// start indices of two previous polynomials Ok(X) and Ok-1(X)
int startKm1 = startK;
startK += k;
// a1k Ok+1(X) = (a2k + a3k X) Ok(X) - a4k Ok-1(X)
// we use bik = aik/a1k
initRecurrenceCoefficients(k, b2k, b3k, b4k);
RationalNumber ckPrev = null;
RationalNumber ck = (RationalNumber)l.get(startK);
RationalNumber ckm1 = (RationalNumber)l.get(startKm1);
// degree 0 coefficient
RationalNumber coeff = RationalNumber.multiply(ck, b2k);
coeff.multiplyAndSubtractFromSelf(ckm1, b4k);
l.add(coeff);
// degree 1 to degree k-1 coefficients
for (int i = 1; i < k; ++i) {
ckPrev = ck;
ck = (RationalNumber)l.get(startK + i);
ckm1 = (RationalNumber)l.get(startKm1 + i);
coeff = RationalNumber.multiply(ck, b2k);
coeff.multiplyAndAddToSelf(ckPrev, b3k);
coeff.multiplyAndSubtractFromSelf(ckm1, b4k);
l.add(coeff);
}
// degree k coefficient
ckPrev = ck;
ck = (RationalNumber)l.get(startK + k);
coeff = RationalNumber.multiply(ck, b2k);
coeff.multiplyAndAddToSelf(ckPrev, b3k);
l.add(coeff);
// degree k+1 coefficient
l.add(RationalNumber.multiply(ck, b3k));
}
setMaxDegree(d);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,439 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.math.BigInteger;
/**
* This class implements fractions of polynomials with one unknown and
* rational coefficients.
* @version $Id: PolynomialFraction.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class PolynomialFraction {
/**
* Simple constructor.
* Build a constant null fraction
*/
public PolynomialFraction() {
this(new Polynomial.Rational(new RationalNumber(0l)),
new Polynomial.Rational(new RationalNumber(1l)));
}
/**
* Simple constructor.
* Build a fraction from a numerator and a denominator.
* @param numerator numerator of the fraction
* @param denominator denominator of the fraction
* @exception ArithmeticException if the denominator is null
*/
public PolynomialFraction(long numerator, long denominator) {
this(new Polynomial.Rational(new RationalNumber(numerator)),
new Polynomial.Rational(new RationalNumber(denominator)));
}
/**
* Simple constructor.
* Build a fraction from a numerator and a denominator.
* @param numerator numerator of the fraction
* @param denominator denominator of the fraction
* @exception ArithmeticException if the denominator is null
*/
public PolynomialFraction(BigInteger numerator, BigInteger denominator) {
this(new Polynomial.Rational(new RationalNumber(numerator)),
new Polynomial.Rational(new RationalNumber(denominator)));
}
/**
* Simple constructor.
* Build a fraction from a numerator and a denominator.
* @param numerator numerator of the fraction
* @param denominator denominator of the fraction
* @exception ArithmeticException if the denominator is null
*/
public PolynomialFraction(RationalNumber numerator,
RationalNumber denominator) {
this(new Polynomial.Rational(numerator),
new Polynomial.Rational(denominator));
}
/**
* Simple constructor.
* Build a fraction from a numerator and a denominator.
* @param numerator numerator of the fraction
* @param denominator denominator of the fraction
* @exception ArithmeticException if the denominator is null
*/
public PolynomialFraction(Polynomial.Rational numerator,
Polynomial.Rational denominator) {
if (denominator.isZero()) {
throw new ArithmeticException("null denominator");
}
p = new Polynomial.Rational(numerator);
q = new Polynomial.Rational(denominator);
RationalNumber[] a = q.getCoefficients();
if (a[a.length - 1].isNegative()) {
p.negateSelf();
q.negateSelf();
}
simplify();
}
/**
* Simple constructor.
* Build a fraction from a single integer
* @param l value of the fraction
*/
public PolynomialFraction(long l) {
this(l, 1l);
}
/**
* Simple constructor.
* Build a fraction from a single integer
* @param i value of the fraction
*/
public PolynomialFraction(BigInteger i) {
this(i, BigInteger.ONE);
}
/**
* Simple constructor.
* Build a fraction from a single rational number
* @param r value of the fraction
*/
public PolynomialFraction(RationalNumber r) {
this(r.getNumerator(), r.getDenominator());
}
/**
* Simple constructor.
* Build a fraction from a single Polynom
* @param p value of the fraction
*/
public PolynomialFraction(Polynomial.Rational p) {
this(p, new Polynomial.Rational(new RationalNumber(1l)));
}
/**
* Copy-constructor.
* @param f fraction to copy
*/
public PolynomialFraction(PolynomialFraction f) {
p = new Polynomial.Rational(f.p);
q = new Polynomial.Rational(f.q);
}
/**
* Negate the instance
*/
public void negateSelf() {
p.negateSelf();
}
/**
* Negate a fraction.
* @param f fraction to negate
* @return a new fraction which is the opposite of f
*/
public static PolynomialFraction negate(PolynomialFraction f) {
PolynomialFraction copy = new PolynomialFraction(f);
copy.negateSelf();
return copy;
}
/**
* Add a fraction to the instance.
* @param f fraction to add.
*/
public void addToSelf(PolynomialFraction f) {
PolynomialFraction sum = add(this, f);
p = sum.p;
q = sum.q;
}
/** Add two fractions.
* @param f1 first fraction
* @param f2 second fraction
* @return a new fraction which is the sum of f1 and f2
*/
public static PolynomialFraction add(PolynomialFraction f1,
PolynomialFraction f2) {
Polynomial.Rational num =
Polynomial.Rational.add(Polynomial.Rational.multiply(f1.p, f2.q),
Polynomial.Rational.multiply(f2.p, f1.q));
Polynomial.Rational den = Polynomial.Rational.multiply(f1.q, f2.q);
return new PolynomialFraction(num, den);
}
/**
* Subtract a fraction to the instance.
* @param f fraction to subtract.
*/
public void subtractFromSelf(PolynomialFraction f) {
PolynomialFraction diff = subtract(this, f);
p = diff.p;
q = diff.q;
}
/** Subtract two fractions.
* @param f1 first fraction
* @param f2 second fraction
* @return a new fraction which is the difference f1 minus f2
*/
public static PolynomialFraction subtract(PolynomialFraction f1,
PolynomialFraction f2) {
Polynomial.Rational num =
Polynomial.Rational.subtract(Polynomial.Rational.multiply(f1.p, f2.q),
Polynomial.Rational.multiply(f2.p, f1.q));
Polynomial.Rational den = Polynomial.Rational.multiply(f1.q, f2.q);
return new PolynomialFraction(num, den);
}
/** Multiply the instance by a fraction.
* @param f fraction to multiply by
*/
public void multiplySelf(PolynomialFraction f) {
p.multiplySelf(f.p);
q.multiplySelf(f.q);
simplify();
}
/** Multiply two fractions.
* @param f1 first fraction
* @param f2 second fraction
* @return a new fraction which is the product of f1 and f2
*/
public static PolynomialFraction multiply(PolynomialFraction f1,
PolynomialFraction f2) {
PolynomialFraction copy = new PolynomialFraction(f1);
copy.multiplySelf(f2);
return copy;
}
/** Divide the instance by a fraction.
* @param f fraction to divide by
* @exception ArithmeticException if f is null
*/
public void divideSelf(PolynomialFraction f) {
if (f.p.isZero()) {
throw new ArithmeticException("divide by zero");
}
p.multiplySelf(f.q);
q.multiplySelf(f.p);
RationalNumber[] a = q.getCoefficients();
if (a[a.length - 1].isNegative()) {
p.negateSelf();
q.negateSelf();
}
simplify();
}
/** Divide two fractions.
* @param f1 first fraction
* @param f2 second fraction
* @return a new fraction which is the quotient of f1 by f2
*/
public static PolynomialFraction divide(PolynomialFraction f1,
PolynomialFraction f2) {
PolynomialFraction copy = new PolynomialFraction(f1);
copy.divideSelf(f2);
return copy;
}
/** Invert the instance.
* Replace the instance by its inverse.
* @exception ArithmeticException if the instance is null
*/
public void invertSelf() {
if (p.isZero()) {
throw new ArithmeticException("divide by zero");
}
Polynomial.Rational tmp = p;
p = q;
q = tmp;
RationalNumber[] a = q.getCoefficients();
if (a[a.length - 1].isNegative()) {
p.negateSelf();
q.negateSelf();
}
simplify();
}
/** Invert a fraction.
* @param f fraction to invert
* @return a new fraction which is the inverse of f
*/
public static PolynomialFraction invert(PolynomialFraction f) {
PolynomialFraction copy = new PolynomialFraction(f);
copy.invertSelf();
return copy;
}
/** Simplify a fraction.
* If the denominator polynom is a constant polynom, then
* simplification involves merging this constant in the rational
* coefficients of the numerator in order to replace the denominator
* by the constant 1. If the degree of the denominator is non null,
* then simplification involves both removing common polynomial
* factors (by euclidian division) and replacing rational
* coefficients by integer coefficients (multiplying both numerator
* and denominator by the proper value). The signs of both the
* numerator and the denominator are adjusted in order to have a
* positive leeding degree term in the denominator.
*/
private void simplify() {
Polynomial.Rational a = new Polynomial.Rational(p);
Polynomial.Rational b = new Polynomial.Rational(q);
if (a.getDegree() < b.getDegree()) {
Polynomial.Rational tmp = a;
a = b;
b = tmp;
}
Polynomial.DivisionResult res =
Polynomial.Rational.euclidianDivision(a, b);
while (res.remainder.getDegree() != 0) {
a = b;
b = res.remainder;
res = Polynomial.Rational.euclidianDivision(a, b);
}
if (res.remainder.isZero()) {
// there is a common factor we can remove
p = Polynomial.Rational.euclidianDivision(p, b).quotient;
q = Polynomial.Rational.euclidianDivision(q, b).quotient;
}
if (q.getDegree() == 0) {
if (! q.isOne()) {
RationalNumber f = q.getCoefficients()[0];
f.invertSelf();
p.multiplySelf(f);
q = new Polynomial.Rational(1l);
}
} else {
BigInteger lcm = p.getDenominatorsLCM();
if (lcm.compareTo(BigInteger.ONE) != 0) {
p.multiplySelf(lcm);
q.multiplySelf(lcm);
}
lcm = q.getDenominatorsLCM();
if (lcm.compareTo(BigInteger.ONE) != 0) {
p.multiplySelf(lcm);
q.multiplySelf(lcm);
}
}
if (q.getCoefficients()[q.getDegree()].isNegative()) {
p.negateSelf();
q.negateSelf();
}
}
/**
* Get the numerator.
* @return the numerator
*/
public Polynomial.Rational getNumerator() {
return p;
}
/**
* Get the denominator.
* @return the denominator (leeding coefficient is always positive)
*/
public Polynomial.Rational getDenominator() {
return q;
}
/** Set the name of the unknown (to appear during conversions to
* strings).
* @param name name to set (if null, the default 'x' value will be
* used)
*/
public void setUnknownName(String name) {
p.setUnknownName(name);
q.setUnknownName(name);
}
public String toString() {
if (p.isZero()) {
return "0";
} else if (q.isOne()) {
return p.toString();
} else {
StringBuffer s = new StringBuffer();
String pString = p.toString();
if (pString.indexOf(' ') > 0) {
s.append('(');
s.append(pString);
s.append(')');
} else {
s.append(pString);
}
s.append('/');
String qString = q.toString();
if (qString.indexOf(' ') > 0) {
s.append('(');
s.append(qString);
s.append(')');
} else {
s.append(qString);
}
return s.toString();
}
}
/** Numerator. */
private Polynomial.Rational p;
/** Denominator. */
private Polynomial.Rational q;
}

View File

@ -0,0 +1,536 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.algebra;
import java.math.BigInteger;
/**
* This class implements reduced rational numbers.
* @version $Id: RationalNumber.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class RationalNumber {
/**
* Simple constructor.
* Build a null rational number
*/
public RationalNumber() {
p = BigInteger.ZERO;
q = BigInteger.ONE;
}
/**
* Simple constructor.
* Build a rational number from a numerator and a denominator.
* @param numerator numerator of the rational number
* @param denominator denominator of the rational number
* @exception ArithmeticException if the denominator is zero
*/
public RationalNumber(long numerator, long denominator) {
reset(numerator, denominator);
}
/**
* Simple constructor.
* Build a rational number from a numerator and a denominator.
* @param numerator numerator of the rational number
* @param denominator denominator of the rational number
* @exception ArithmeticException if the denominator is zero
*/
public RationalNumber(BigInteger numerator, BigInteger denominator) {
reset(numerator, denominator);
}
/**
* Simple constructor.
* Build a rational number from a single integer
* @param l value of the rational number
*/
public RationalNumber(long l) {
p = BigInteger.valueOf(l);
q = BigInteger.ONE;
}
/**
* Simple constructor.
* Build a rational number from a single integer
* @param i value of the rational number
*/
public RationalNumber(BigInteger i) {
p = i;
q = BigInteger.ONE;
}
/**
* Copy-constructor.
* @param r rational number to copy
*/
public RationalNumber(RationalNumber r) {
p = r.p;
q = r.q;
}
/** Reset the instance from a numerator and a denominator.
* @param numerator numerator of the rational number
* @param denominator denominator of the rational number
* @exception ArithmeticException if the denominator is zero
*/
public void reset(long numerator, long denominator) {
if (denominator == 0l) {
throw new ArithmeticException("divide by zero");
}
p = BigInteger.valueOf(numerator);
q = BigInteger.valueOf(denominator);
if (q.signum() < 0) {
p = p.negate();
q = q.negate();
}
simplify();
}
/** Reset the instance from a numerator and a denominator.
* @param numerator numerator of the rational number
* @param denominator denominator of the rational number
* @exception ArithmeticException if the denominator is zero
*/
public void reset(BigInteger numerator, BigInteger denominator) {
if (denominator.signum() == 0) {
throw new ArithmeticException("divide by zero");
}
p = numerator;
q = denominator;
if (q.signum() < 0) {
p = p.negate();
q = q.negate();
}
simplify();
}
/** Reset the instance from a single integer
* @param l value of the rational number
*/
public void reset(long l) {
p = BigInteger.valueOf(l);
q = BigInteger.ONE;
}
/** Reset the instance from a single integer
* @param i value of the rational number
*/
public void reset(BigInteger i) {
p = i;
q = BigInteger.ONE;
}
/** Reset the instance from another rational number.
* @param r rational number to copy
*/
public void reset(RationalNumber r) {
p = r.p;
q = r.q;
}
/**
* Negate the instance
*/
public void negateSelf() {
p = p.negate();
}
/**
* Negate a rational number.
* @param r rational number to negate
* @return a new rational number which is the opposite of r
*/
public static RationalNumber negate(RationalNumber r) {
RationalNumber copy = new RationalNumber(r);
copy.negateSelf();
return copy;
}
/**
* Add a rational number to the instance.
* @param r rational number to add.
*/
public void addToSelf(RationalNumber r) {
p = p.multiply(r.q).add(r.p.multiply(q));
q = q.multiply(r.q);
simplify();
}
/** Add two rational numbers.
* @param r1 first rational number
* @param r2 second rational number
* @return a new rational number which is the sum of r1 and r2
*/
public static RationalNumber add(RationalNumber r1, RationalNumber r2) {
return new RationalNumber(r1.p.multiply(r2.q).add(r2.p.multiply(r1.q)),
r1.q.multiply(r2.q));
}
/**
* Subtract a rational number to the instance.
* @param r rational number to subtract.
*/
public void subtractFromSelf(RationalNumber r) {
p = p.multiply(r.q).subtract(r.p.multiply(q));
q = q.multiply(r.q);
simplify();
}
/** Subtract two rational numbers.
* @param r1 first rational number
* @param r2 second rational number
* @return a new rational number which is the difference r1 minus r2
*/
public static RationalNumber subtract(RationalNumber r1, RationalNumber r2) {
return new RationalNumber(r1.p.multiply(r2.q).subtract(r2.p.multiply(r1.q)),
r1.q.multiply(r2.q));
}
/** Multiply the instance by an integer.
* @param l integer to multiply by
*/
public void multiplySelf(long l) {
p = p.multiply(BigInteger.valueOf(l));
simplify();
}
/** Multiply the instance by an integer.
* @param i integer to multiply by
*/
public void multiplySelf(BigInteger i) {
p = p.multiply(i);
simplify();
}
/** Multiply a rational number by an integer.
* @param l integer to multiply by
*/
public static RationalNumber multiply(RationalNumber r, long l) {
return new RationalNumber(r.p.multiply(BigInteger.valueOf(l)), r.q);
}
/** Multiply a rational number by an integer.
* @param i integer to multiply by
*/
public static RationalNumber multiply(RationalNumber r, BigInteger i) {
return new RationalNumber(r.p.multiply(i), r.q);
}
/** Multiply the instance by a rational number.
* @param r rational number to multiply by
*/
public void multiplySelf(RationalNumber r) {
p = p.multiply(r.p);
q = q.multiply(r.q);
simplify();
}
/** Multiply two rational numbers.
* @param r1 first rational number
* @param r2 second rational number
* @return a new rational number which is the product of r1 and r2
*/
public static RationalNumber multiply(RationalNumber r1, RationalNumber r2) {
return new RationalNumber(r1.p.multiply(r2.p),
r1.q.multiply(r2.q));
}
/** Divide the instance by an integer.
* @param l integer to divide by
* @exception ArithmeticException if l is zero
*/
public void divideSelf(long l) {
if (l == 0l) {
throw new ArithmeticException("divide by zero");
} else if (l > 0l) {
q = q.multiply(BigInteger.valueOf(l));
} else {
p = p.negate();
q = q.multiply(BigInteger.valueOf(-l));
}
simplify();
}
/** Divide the instance by an integer.
* @param i integer to divide by
* @exception ArithmeticException if l is zero
*/
public void divideSelf(BigInteger i) {
if (i.signum() == 0) {
throw new ArithmeticException("divide by zero");
} else if (i.signum() > 0) {
q = q.multiply(i);
} else {
p = p.negate();
q = q.multiply(i.negate());
}
simplify();
}
/** Divide a rational number by an integer
* @param r rational number
* @param l integer
* @return a new rational number which is the quotient of r by l
* @exception ArithmeticException if l is zero
*/
public static RationalNumber divide(RationalNumber r, long l) {
RationalNumber copy = new RationalNumber(r);
copy.divideSelf(l);
return copy;
}
/** Divide a rational number by an integer
* @param r rational number
* @param i integer
* @return a new rational number which is the quotient of r by l
* @exception ArithmeticException if l is zero
*/
public static RationalNumber divide(RationalNumber r, BigInteger i) {
RationalNumber copy = new RationalNumber(r);
copy.divideSelf(i);
return copy;
}
/** Divide the instance by a rational number.
* @param r rational number to divide by
* @exception ArithmeticException if r is zero
*/
public void divideSelf(RationalNumber r) {
if (r.p.signum() == 0) {
throw new ArithmeticException("divide by zero");
}
p = p.multiply(r.q);
q = q.multiply(r.p);
if (q.signum() < 0) {
p = p.negate();
q = q.negate();
}
simplify();
}
/** Divide two rational numbers.
* @param r1 first rational number
* @param r2 second rational number
* @return a new rational number which is the quotient of r1 by r2
* @exception ArithmeticException if r2 is zero
*/
public static RationalNumber divide(RationalNumber r1, RationalNumber r2) {
RationalNumber copy = new RationalNumber(r1);
copy.divideSelf(r2);
return copy;
}
/** Invert the instance.
* Replace the instance by its inverse.
* @exception ArithmeticException if the instance is zero
*/
public void invertSelf() {
if (p.signum() == 0) {
throw new ArithmeticException("divide by zero");
}
BigInteger tmp = p;
p = q;
q = tmp;
if (q.signum() < 0) {
p = p.negate();
q = q.negate();
}
}
/** Invert a rational number.
* @param r rational number to invert
* @return a new rational number which is the inverse of r
* @exception ArithmeticException if r is zero
*/
public static RationalNumber invert(RationalNumber r) {
return new RationalNumber(r.q, r.p);
}
/**
* Add the product of two rational numbers to the instance.
* This operation is equivalent to
* <code>addToSelf(RationalNumber.multiply(r1, r2))</code> except
* that no intermediate simplification is attempted.
* @param r1 first term of the product to add
* @param r2 second term of the product to add
*/
public void multiplyAndAddToSelf(RationalNumber r1, RationalNumber r2) {
BigInteger r1qr2q = r1.q.multiply(r2.q);
p = p.multiply(r1qr2q).add(r1.p.multiply(r2.p).multiply(q));
q = q.multiply(r1qr2q);
simplify();
}
/**
* Subtract the product of two rational numbers from the instance.
* This operation is equivalent to
* <code>subtractFromSelf(RationalNumber.multiply(r1, r2))</code>
* except that no intermediate simplification is attempted.
* @param r1 first term of the product to subtract
* @param r2 second term of the product to subtract
*/
public void multiplyAndSubtractFromSelf(RationalNumber r1, RationalNumber r2) {
BigInteger r1qr2q = r1.q.multiply(r2.q);
p = p.multiply(r1qr2q).subtract(r1.p.multiply(r2.p).multiply(q));
q = q.multiply(r1qr2q);
simplify();
}
/** Simplify a rational number by removing common factors.
*/
private void simplify() {
if (p.signum() == 0) {
q = BigInteger.ONE;
} else {
BigInteger gcd = p.gcd(q);
p = p.divide(gcd);
q = q.divide(gcd);
}
}
/**
* Get the numerator.
* @return the signed numerator
*/
public BigInteger getNumerator() {
return p;
}
/**
* Get the denominator.
* @return the denominator (always positive)
*/
public BigInteger getDenominator() {
return q;
}
/** Check if the number is zero.
* @return true if the number is zero
*/
public boolean isZero() {
return p.signum() == 0;
}
/** Check if the number is one.
* @return true if the number is one
*/
public boolean isOne() {
return (p.compareTo(BigInteger.ONE) == 0)
&& (q.compareTo(BigInteger.ONE) == 0);
}
/** Check if the number is integer.
* @return true if the number is an integer
*/
public boolean isInteger() {
return q.compareTo(BigInteger.ONE) == 0;
}
/** Check if the number is negative.
* @return true if the number is negative
*/
public boolean isNegative() {
return p.signum() < 0;
}
/** Get the absolute value of a rational number.
* @param r rational number from which we want the absolute value
* @return a new rational number which is the absolute value of r
*/
public static RationalNumber abs(RationalNumber r) {
return new RationalNumber(r.p.abs(), r.q);
}
/** Return the <code>double</code> value of the instance.
* @return the double value of the instance
*/
public double doubleValue() {
BigInteger[] result = p.divideAndRemainder(q);
return result[0].doubleValue()
+ (result[1].doubleValue() / q.doubleValue());
}
/** Check if the instance is equal to another rational number.
* Equality here is having the same value.
* @return true if the object is a rational number which has the
* same value as the instance
*/
public boolean equals(Object o) {
if (o instanceof RationalNumber) {
RationalNumber r = (RationalNumber) o;
return (p.compareTo(r.p) == 0) && (q.compareTo(r.q) == 0);
}
return false;
}
/** Returns a hash code value for the object.
* The hash code value is computed from the reduced numerator and
* denominator, hence equal rational numbers have the same hash code,
* as required by the method specification.
* @return a hash code value for this object.
*/
public int hashCode() {
return p.hashCode() ^ q.hashCode();
}
/** Returns a string representation of the rational number.
* The representation is reduced: there is no common factor left
* between the numerator and the denominator. The '/' character and
* the denominator are displayed only if the denominator is not
* one. The sign is on the numerator.
* @return string representation of the rational number
*/
public String toString() {
return p + ((q.compareTo(BigInteger.ONE) == 0) ? "" : ("/" + q));
}
/** Numerator. */
private BigInteger p;
/** Denominator. */
private BigInteger q;
}

View File

@ -0,0 +1,123 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import java.io.Serializable;
/** This class represent the estimated parameters of an estimation problem.
* <p>The parameters of an estimation problem have a name, a value and
* a bound flag. The value of bound parameters is considered trusted
* and the solvers should not adjust them. On the other hand, the
* solvers should adjust the value of unbounds parameters until they
* satisfy convergence criterions specific to each solver.</p>
* @version $Id: EstimatedParameter.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class EstimatedParameter
implements Serializable {
/** Simple constructor.
* Build an instance from a first estimate of the parameter,
* initially considered unbound.
* @param name name of the parameter
* @param firstEstimate first estimate of the parameter
*/
public EstimatedParameter(String name, double firstEstimate) {
this.name = name;
estimate = firstEstimate;
bound = false;
}
/** Simple constructor.
* Build an instance from a first estimate of the parameter and a
* bound flag
* @param name name of the parameter
* @param firstEstimate first estimate of the parameter
* @param bound flag, should be true if the parameter is bound
*/
public EstimatedParameter(String name,
double firstEstimate,
boolean bound) {
this.name = name;
estimate = firstEstimate;
this.bound = bound;
}
/** Copy constructor.
* Build a copy of a parameter
* @param parameter instance to copy
*/
public EstimatedParameter(EstimatedParameter parameter) {
name = parameter.name;
estimate = parameter.estimate;
bound = parameter.bound;
}
/** Set a new estimated value for the parameter.
* @param estimate new estimate for the parameter
*/
public void setEstimate(double estimate) {
this.estimate = estimate;
}
/** Get the current estimate of the parameter
* @return current estimate
*/
public double getEstimate() {
return estimate;
}
/** get the name of the parameter
* @return parameter name
*/
public String getName() {
return name;
}
/** Set the bound flag of the parameter
* @param bound this flag should be set to true if the parameter is
* bound (i.e. if it should not be adjusted by the solver).
*/
public void setBound(boolean bound) {
this.bound = bound;
}
/** Check if the parameter is bound
* @return true if the parameter is bound */
public boolean isBound() {
return bound;
}
/** Name of the parameter */
private String name;
/** Current value of the parameter */
protected double estimate;
/** Indicator for bound parameters
* (ie parameters that should not be estimated)
*/
private boolean bound;
private static final long serialVersionUID = -555440800213416949L;
}

View File

@ -0,0 +1,59 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import org.spaceroots.mantissa.MantissaException;
/** This class represents exceptions thrown by the estimation solvers.
* @version $Id: EstimationException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class EstimationException
extends MantissaException {
/** Simple constructor.
* Build an exception by translating the specified message
* @param message message to translate
*/
public EstimationException(String message) {
super(message);
}
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public EstimationException(String specifier, String[] parts) {
super(specifier, parts);
}
/** Simple constructor.
* Build an exception from a cause
* @param cause cause of this exception
*/
public EstimationException(Throwable cause) {
super(cause);
}
private static final long serialVersionUID = 1613719630569355278L;
}

View File

@ -0,0 +1,61 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
/** This interface represents an estimation problem.
* <p>This interface should be implemented by all real estimation
* problems before they can be handled by the estimators through the
* {@link Estimator#estimate Estimator.estimate} method.</p>
* <p>An estimation problem, as seen by a solver is a set of
* parameters and a set of measurements. The parameters are adjusted
* during the estimation through the {@link #getUnboundParameters
* getUnboundParameters} and {@link EstimatedParameter#setEstimate
* EstimatedParameter.setEstimate} methods. The measurements both have
* a measured value which is generally fixed at construction and a
* theoretical value which depends on the model and hence varies as
* the parameters are adjusted. The purpose of the solver is to reduce
* the residual between these values, it can retrieve the measurements
* through the {@link #getMeasurements getMeasurements} method.</p>
* @see Estimator
* @see WeightedMeasurement
* @version $Id: EstimationProblem.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface EstimationProblem {
/** Get the measurements of an estimation problem.
* @return measurements
*/
public WeightedMeasurement[] getMeasurements();
/** Get the unbound parameters of the problem.
* @return unbound parameters
*/
public EstimatedParameter[] getUnboundParameters();
/** Get all the parameters of the problem.
* @return parameters
*/
public EstimatedParameter[] getAllParameters();
}

View File

@ -0,0 +1,66 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
/** This interface represents solvers for estimation problems.
* <p>The classes which are devoted to solve estimation problems
* should implement this interface. The problems which can be handled
* should implement the {@link EstimationProblem} interface which
* gather all the information needed by the solver.</p>
* <p>The interface is composed only of the {@link #estimate estimate}
* method.</p>
* @see EstimationProblem
* @version $Id: Estimator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface Estimator {
/** Solve an estimation problem.
* <p>The method should set the parameters of the problem to several
* trial values until it reaches convergence. If this method returns
* normally (i.e. without throwing an exception), then the best
* estimate of the parameters can be retrieved from the problem
* itself, through the {@link EstimationProblem#getAllParameters
* EstimationProblem.getAllParameters} method.</p>
* @param problem estimation problem to solve
* @exception EstimationException if the problem cannot be solved
*/
public void estimate(EstimationProblem problem)
throws EstimationException;
/** Get the Root Mean Square value.
* Get the Root Mean Square value, i.e. the root of the arithmetic
* mean of the square of all weighted residuals. This is related to the
* criterion that is minimized by the estimator as follows: if
* <em>c</em> if the criterion, and <em>n</em> is the number of
* measurements, the the RMS is <em>sqrt (c/n)</em>.
* @param problem estimation problem
* @return RMS value
*/
public double getRMS(EstimationProblem problem);
}

View File

@ -0,0 +1,230 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import java.io.Serializable;
import org.spaceroots.mantissa.linalg.Matrix;
import org.spaceroots.mantissa.linalg.GeneralMatrix;
import org.spaceroots.mantissa.linalg.SymetricalMatrix;
import org.spaceroots.mantissa.linalg.SingularMatrixException;
/** This class implements a solver for estimation problems.
* <p>This class solves estimation problems using a weighted least
* squares criterion on the measurement residuals. It uses a
* Gauss-Newton algorithm.</p>
* @version $Id: GaussNewtonEstimator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class GaussNewtonEstimator
implements Estimator, Serializable {
/** Simple constructor.
* <p>This constructor build an estimator and store its convergence
* characteristics.</p>
* <p>An estimator is considered to have converged whenever either
* the criterion goes below a physical threshold under which
* improvements are considered useless or when the algorithm is
* unable to improve it (even if it is still high). The first
* condition that is met stops the iterations.</p>
* <p>The fact an estimator has converged does not mean that the
* model accurately fits the measurements. It only means no better
* solution can be found, it does not mean this one is good. Such an
* analysis is left to the caller.</p>
* <p>If neither conditions are fulfilled before a given number of
* iterations, the algorithm is considered to have failed and an
* {@link EstimationException} is thrown.</p>
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has converged has reached a steady state if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(Matrix,double)
* SquareMatrix.solve}). */
public GaussNewtonEstimator(int maxIterations,
double convergence,
double steadyStateThreshold,
double epsilon) {
this.maxIterations = maxIterations;
this.steadyStateThreshold = steadyStateThreshold;
this.convergence = convergence;
this.epsilon = epsilon;
}
/** Solve an estimation problem using a least squares criterion.
* <p>This method set the unbound parameters of the given problem
* starting from their current values through several iterations. At
* each step, the unbound parameters are changed in order to
* minimize a weighted least square criterion based on the
* measurements of the problem.</p>
* <p>The iterations are stopped either when the criterion goes
* below a physical threshold under which improvement are considered
* useless or when the algorithm is unable to improve it (even if it
* is still high). The first condition that is met stops the
* iterations. If the convergence it nos reached before the maximum
* number of iterations, an {@link EstimationException} is
* thrown.</p>
* @param problem estimation problem to solve
* @exception EstimationException if the problem cannot be solved
* @see EstimationProblem
*/
public void estimate(EstimationProblem problem)
throws EstimationException {
int iterations = 0;
double previous = 0.0;
double current = 0.0;
// iterate until convergence is reached
do {
if (++iterations > maxIterations) {
throw new EstimationException ("unable to converge in {0} iterations",
new String[] {
Integer.toString(maxIterations)
});
}
// perform one iteration
linearEstimate(problem);
previous = current;
current = evaluateCriterion(problem);
} while ((iterations < 2)
|| (Math.abs(previous - current) > (current * steadyStateThreshold)
&& (Math.abs(current) > convergence)));
}
/** Estimate the solution of a linear least square problem.
* <p>The Gauss-Newton algorithm is iterative. Each iteration
* consist in solving a linearized least square problem. Several
* iterations are needed for general problems since the
* linearization is only an approximation of the problem
* behaviour. However, for linear problems one iteration is enough
* to get the solution. This method is provided in the public
* interface in order to handle more efficiently these linear
* problems.</p>
* @param problem estimation problem to solve
* @exception EstimationException if the problem cannot be solved
*/
public void linearEstimate(EstimationProblem problem)
throws EstimationException {
EstimatedParameter[] parameters = problem.getUnboundParameters();
WeightedMeasurement[] measurements = problem.getMeasurements();
// build the linear problem
GeneralMatrix b = new GeneralMatrix(parameters.length, 1);
SymetricalMatrix a = new SymetricalMatrix(parameters.length);
for (int i = 0; i < measurements.length; ++i) {
if (! measurements [i].isIgnored()) {
double weight = measurements[i].getWeight();
double residual = measurements[i].getResidual();
// compute the normal equation
double[] grad = new double[parameters.length];
Matrix bDecrement = new GeneralMatrix(parameters.length, 1);
for (int j = 0; j < parameters.length; ++j) {
grad[j] = measurements[i].getPartial(parameters[j]);
bDecrement.setElement(j, 0, weight * residual * grad[j]);
}
// update the matrices
a.selfAddWAAt(weight, grad);
b.selfAdd(bDecrement);
}
}
try {
// solve the linearized least squares problem
Matrix dX = a.solve(b, epsilon);
// update the estimated parameters
for (int i = 0; i < parameters.length; ++i) {
parameters[i].setEstimate(parameters[i].getEstimate()
+ dX.getElement(i, 0));
}
} catch(SingularMatrixException e) {
throw new EstimationException(e);
}
}
private double evaluateCriterion(EstimationProblem problem) {
double criterion = 0.0;
WeightedMeasurement[] measurements = problem.getMeasurements();
for (int i = 0; i < measurements.length; ++i) {
double residual = measurements[i].getResidual();
criterion += measurements[i].getWeight() * residual * residual;
}
return criterion;
}
/** Get the Root Mean Square value.
* Get the Root Mean Square value, i.e. the root of the arithmetic
* mean of the square of all weighted residuals. This is related to the
* criterion that is minimized by the estimator as follows: if
* <em>c</em> if the criterion, and <em>n</em> is the number of
* measurements, then the RMS is <em>sqrt (c/n)</em>.
* @param problem estimation problem
* @return RMS value
*/
public double getRMS(EstimationProblem problem) {
double criterion = evaluateCriterion(problem);
int n = problem.getMeasurements().length;
return Math.sqrt(criterion / n);
}
private int maxIterations;
private double steadyStateThreshold;
private double convergence;
private double epsilon;
private static final long serialVersionUID = -7606628156644194170L;
}

View File

@ -0,0 +1,73 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import java.io.Serializable;
/** This class implements a solver for estimation problems.
* @deprecated this class has been replaced by the {@link
* org.spaceroots.mantissa.estimation.GaussNewtonEstimator GaussNewtonEstimator}
* class. It is now a simple wrapper delegating everything to {@link
* org.spaceroots.mantissa.estimation.GaussNewtonEstimator GaussNewtonEstimator}
* @version $Id: LeastSquaresEstimator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class LeastSquaresEstimator implements Estimator, Serializable {
/** Simple constructor.
* @see org.spaceroots.mantissa.estimation.GaussNewtonEstimator#GaussNewtonEstimator(int,
* double, double, double)
*/
public LeastSquaresEstimator(int maxIterations,
double convergence,
double steadyStateThreshold,
double epsilon) {
estimator = new GaussNewtonEstimator(maxIterations,
convergence,
steadyStateThreshold,
epsilon);
}
/** Solve an estimation problem using a least squares criterion.
* @see org.spaceroots.mantissa.estimation.GaussNewtonEstimator#estimate
*/
public void estimate(EstimationProblem problem)
throws EstimationException {
estimator.estimate(problem);
}
/** Estimate the solution of a linear least square problem.
* @see org.spaceroots.mantissa.estimation.GaussNewtonEstimator#linearEstimate
*/
public void linearEstimate(EstimationProblem problem)
throws EstimationException {
estimator.linearEstimate(problem);
}
/** Get the Root Mean Square value.
* @see org.spaceroots.mantissa.estimation.GaussNewtonEstimator#getRMS
*/
public double getRMS(EstimationProblem problem) {
return estimator.getRMS(problem);
}
private GaussNewtonEstimator estimator;
private static final long serialVersionUID = -7542643494637247770L;
}

View File

@ -0,0 +1,972 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import java.io.Serializable;
import java.util.Arrays;
/** This class solves a least squares problem.
* <p>This implementation <em>should</em> work even for over-determined systems
* (i.e. systems having more variables than equations). Over-determined systems
* are solved by ignoring the variables which have the smallest impact according
* to their jacobian column norm. Only the rank of the matrix and some loop bounds
* are changed to implement this. This feature has undergone only basic testing
* for now and should still be considered experimental.</p>
* <p>The resolution engine is a simple translation of the MINPACK <a
* href="http://www.netlib.org/minpack/lmder.f">lmder</a> routine with minor
* changes. The changes include the over-determined resolution and the Q.R.
* decomposition which has been rewritten following the algorithm described in the
* P. Lascaux and R. Theodor book <i>Analyse num&eacute;rique matricielle
* appliqu&eacute;e &agrave; l'art de l'ing&eacute;nieur</i>, Masson 1986. The
* redistribution policy for MINPACK is available <a
* href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it
* is reproduced below.</p>
* <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
* <tr><td>
* Minpack Copyright Notice (1999) University of Chicago.
* All rights reserved
* </td></tr>
* <tr><td>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* <ol>
* <li>Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.</li>
* <li>Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.</li>
* <li>The end-user documentation included with the redistribution, if any,
* must include the following acknowledgment:
* <code>This product includes software developed by the University of
* Chicago, as Operator of Argonne National Laboratory.</code>
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.</li>
* <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
* WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
* UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
* THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
* OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
* OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
* USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
* THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
* DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
* UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
* BE CORRECTED.</strong></li>
* <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
* HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
* ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
* INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
* ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
* PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
* SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
* (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
* EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
* POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
* <ol></td></tr>
* </table>
* @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran)
* @author Burton S. Garbow (original fortran)
* @author Kenneth E. Hillstrom (original fortran)
* @author Jorge J. More (original fortran)
* @author Luc Maisonobe (Java translation)
*/
public class LevenbergMarquardtEstimator implements Serializable, Estimator {
/** Build an estimator for least squares problems.
* <p>The default values for the algorithm settings are:
* <ul>
* <li>{@link #setInitialStepBoundFactor initial step bound factor}: 100.0</li>
* <li>{@link #setMaxCostEval maximal cost evaluations}: 1000</li>
* <li>{@link #setCostRelativeTolerance cost relative tolerance}: 1.0e-10</li>
* <li>{@link #setParRelativeTolerance parameters relative tolerance}: 1.0e-10</li>
* <li>{@link #setOrthoTolerance orthogonality tolerance}: 1.0e-10</li>
* </ul>
* </p>
*/
public LevenbergMarquardtEstimator() {
// default values for the tuning parameters
setInitialStepBoundFactor(100.0);
setMaxCostEval(1000);
setCostRelativeTolerance(1.0e-10);
setParRelativeTolerance(1.0e-10);
setOrthoTolerance(1.0e-10);
}
/** Set the positive input variable used in determining the initial step bound.
* This bound is set to the product of initialStepBoundFactor and the euclidean norm of diag*x if nonzero,
* or else to initialStepBoundFactor itself. In most cases factor should lie
* in the interval (0.1, 100.0). 100.0 is a generally recommended value
* @param initialStepBoundFactor initial step bound factor
* @see #estimate
*/
public void setInitialStepBoundFactor(double initialStepBoundFactor) {
this.initialStepBoundFactor = initialStepBoundFactor;
}
/** Set the maximal number of cost evaluations.
* @param maxCostEval maximal number of cost evaluations
* @see #estimate
*/
public void setMaxCostEval(int maxCostEval) {
this.maxCostEval = maxCostEval;
}
/** Set the desired relative error in the sum of squares.
* @param costRelativeTolerance desired relative error in the sum of squares
* @see #estimate
*/
public void setCostRelativeTolerance(double costRelativeTolerance) {
this.costRelativeTolerance = costRelativeTolerance;
}
/** Set the desired relative error in the approximate solution parameters.
* @param parRelativeTolerance desired relative error
* in the approximate solution parameters
* @see #estimate
*/
public void setParRelativeTolerance(double parRelativeTolerance) {
this.parRelativeTolerance = parRelativeTolerance;
}
/** Set the desired max cosine on the orthogonality.
* @param orthoTolerance desired max cosine on the orthogonality
* between the function vector and the columns of the jacobian
* @see #estimate
*/
public void setOrthoTolerance(double orthoTolerance) {
this.orthoTolerance = orthoTolerance;
}
/** Get the number of cost evaluations.
* @return number of cost evaluations
* */
public int getCostEvaluations() {
return costEvaluations;
}
/** Get the number of jacobian evaluations.
* @return number of jacobian evaluations
* */
public int getJacobianEvaluations() {
return jacobianEvaluations;
}
/** Update the jacobian matrix.
*/
private void updateJacobian() {
++jacobianEvaluations;
Arrays.fill(jacobian, 0);
for (int i = 0, index = 0; i < rows; i++) {
WeightedMeasurement wm = measurements[i];
double factor = -Math.sqrt(wm.getWeight());
for (int j = 0; j < cols; ++j) {
jacobian[index++] = factor * wm.getPartial(parameters[j]);
}
}
}
/** Update the residuals array and cost function value.
*/
private void updateResidualsAndCost() {
++costEvaluations;
cost = 0;
for (int i = 0, index = 0; i < rows; i++, index += cols) {
WeightedMeasurement wm = measurements[i];
double residual = wm.getResidual();
residuals[i] = Math.sqrt(wm.getWeight()) * residual;
cost += wm.getWeight() * residual * residual;
}
cost = Math.sqrt(cost);
}
/** Get the Root Mean Square value.
* Get the Root Mean Square value, i.e. the root of the arithmetic
* mean of the square of all weighted residuals. This is related to the
* criterion that is minimized by the estimator as follows: if
* <em>c</em> if the criterion, and <em>n</em> is the number of
* measurements, then the RMS is <em>sqrt (c/n)</em>.
* @param problem estimation problem
* @return RMS value
*/
public double getRMS(EstimationProblem problem) {
WeightedMeasurement[] wm = problem.getMeasurements();
double criterion = 0;
for (int i = 0; i < wm.length; ++i) {
double residual = wm[i].getResidual();
criterion += wm[i].getWeight() * residual * residual;
}
return Math.sqrt(criterion / wm.length);
}
/** Solve an estimation problem using the Levenberg-Marquardt algorithm.
* <p>The algorithm used is a modified Levenberg-Marquardt one, based
* on the MINPACK <a href="http://www.netlib.org/minpack/lmder.f">lmder</a>
* routine. The algorithm settings must have been set up before this method
* is called with the {@link #setInitialStepBoundFactor},
* {@link #setMaxCostEval}, {@link #setCostRelativeTolerance},
* {@link #setParRelativeTolerance} and {@link #setOrthoTolerance} methods.
* If these methods have not been called, the default values set up by the
* {@link #LevenbergMarquardtEstimator() constructor} will be used.</p>
* <p>The authors of the original fortran function are:</p>
* <ul>
* <li>Argonne National Laboratory. MINPACK project. March 1980</li>
* <li>Burton S. Garbow</li>
* <li>Kenneth E. Hillstrom</li>
* <li>Jorge J. More</li>
* </ul>
* <p>Luc Maisonobe did the Java translation.</p>
* @param problem estimation problem to solve
* @exception EstimationException if convergence cannot be
* reached with the specified algorithm settings or if there are more variables
* than equations
* @see #setInitialStepBoundFactor
* @see #setMaxCostEval
* @see #setCostRelativeTolerance
* @see #setParRelativeTolerance
* @see #setOrthoTolerance
*/
public void estimate(EstimationProblem problem)
throws EstimationException {
// retrieve the equations and the parameters
measurements = problem.getMeasurements();
parameters = problem.getUnboundParameters();
// arrays shared with the other private methods
rows = measurements.length;
cols = parameters.length;
solvedCols = Math.min(rows, cols);
jacobian = new double[rows * cols];
diagR = new double[cols];
jacNorm = new double[cols];
beta = new double[cols];
permutation = new int[cols];
lmDir = new double[cols];
residuals = new double[rows];
// local variables
double delta = 0, xNorm = 0;
double[] diag = new double[cols];
double[] oldX = new double[cols];
double[] oldRes = new double[rows];
double[] work1 = new double[cols];
double[] work2 = new double[cols];
double[] work3 = new double[cols];
// evaluate the function at the starting point and calculate its norm
updateResidualsAndCost();
// outer loop
lmPar = 0;
costEvaluations = 0;
jacobianEvaluations = 0;
boolean firstIteration = true;
while (costEvaluations < maxCostEval) {
// compute the Q.R. decomposition of the jacobian matrix
updateJacobian();
qrDecomposition();
// compute Qt.res
qTy(residuals);
// now we don't need Q anymore,
// so let jacobian contain the R matrix with its diagonal elements
for (int k = 0; k < solvedCols; ++k) {
int pk = permutation[k];
jacobian[k * cols + pk] = diagR[pk];
}
if (firstIteration) {
// scale the variables according to the norms of the columns
// of the initial jacobian
xNorm = 0;
for (int k = 0; k < cols; ++k) {
double dk = jacNorm[k];
if (dk == 0) {
dk = 1.0;
}
double xk = dk * parameters[k].getEstimate();
xNorm += xk * xk;
diag[k] = dk;
}
xNorm = Math.sqrt(xNorm);
// initialize the step bound delta
delta = (xNorm == 0)
? initialStepBoundFactor : (initialStepBoundFactor * xNorm);
}
// check orthogonality between function vector and jacobian columns
double maxCosine = 0;
if (cost != 0) {
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double s = jacNorm[pj];
if (s != 0) {
double sum = 0;
for (int i = 0, index = pj; i <= j; ++i, index += cols) {
sum += jacobian[index] * residuals[i];
}
maxCosine = Math.max(maxCosine, Math.abs(sum) / (s * cost));
}
}
}
if (maxCosine <= orthoTolerance) {
return;
}
// rescale if necessary
for (int j = 0; j < cols; ++j) {
diag[j] = Math.max(diag[j], jacNorm[j]);
}
// inner loop
for (double ratio = 0; ratio < 1.0e-4;) {
// save the state
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
oldX[pj] = parameters[pj].getEstimate();
}
double previousCost = cost;
double[] tmpVec = residuals;
residuals = oldRes;
oldRes = tmpVec;
// determine the Levenberg-Marquardt parameter
determineLMParameter(oldRes, delta, diag, work1, work2, work3);
// compute the new point and the norm of the evolution direction
double lmNorm = 0;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
lmDir[pj] = -lmDir[pj];
parameters[pj].setEstimate(oldX[pj] + lmDir[pj]);
double s = diag[pj] * lmDir[pj];
lmNorm += s * s;
}
lmNorm = Math.sqrt(lmNorm);
// on the first iteration, adjust the initial step bound.
if (firstIteration) {
delta = Math.min(delta, lmNorm);
}
// evaluate the function at x + p and calculate its norm
updateResidualsAndCost();
// compute the scaled actual reduction
double actRed = -1.0;
if (0.1 * cost < previousCost) {
double r = cost / previousCost;
actRed = 1.0 - r * r;
}
// compute the scaled predicted reduction
// and the scaled directional derivative
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double dirJ = lmDir[pj];
work1[j] = 0;
for (int i = 0, index = pj; i <= j; ++i, index += cols) {
work1[i] += jacobian[index] * dirJ;
}
}
double coeff1 = 0;
for (int j = 0; j < solvedCols; ++j) {
coeff1 += work1[j] * work1[j];
}
double pc2 = previousCost * previousCost;
coeff1 = coeff1 / pc2;
double coeff2 = lmPar * lmNorm * lmNorm / pc2;
double preRed = coeff1 + 2 * coeff2;
double dirDer = -(coeff1 + coeff2);
// ratio of the actual to the predicted reduction
ratio = (preRed == 0) ? 0 : (actRed / preRed);
// update the step bound
if (ratio <= 0.25) {
double tmp =
(actRed < 0) ? (0.5 * dirDer / (dirDer + 0.5 * actRed)) : 0.5;
if ((0.1 * cost >= previousCost) || (tmp < 0.1)) {
tmp = 0.1;
}
delta = tmp * Math.min(delta, 10.0 * lmNorm);
lmPar /= tmp;
} else if ((lmPar == 0) || (ratio >= 0.75)) {
delta = 2 * lmNorm;
lmPar *= 0.5;
}
// test for successful iteration.
if (ratio >= 1.0e-4) {
// successful iteration, update the norm
firstIteration = false;
xNorm = 0;
for (int k = 0; k < cols; ++k) {
double xK = diag[k] * parameters[k].getEstimate();
xNorm += xK * xK;
}
xNorm = Math.sqrt(xNorm);
} else {
// failed iteration, reset the previous values
cost = previousCost;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
parameters[pj].setEstimate(oldX[pj]);
}
tmpVec = residuals;
residuals = oldRes;
oldRes = tmpVec;
}
// tests for convergence.
if (((Math.abs(actRed) <= costRelativeTolerance)
&& (preRed <= costRelativeTolerance)
&& (ratio <= 2.0))
|| (delta <= parRelativeTolerance * xNorm)) {
return;
}
// tests for termination and stringent tolerances
// (2.2204e-16 is the machine epsilon for IEEE754)
if (costEvaluations >= maxCostEval) {
break;
}
if ((Math.abs(actRed) <= 2.2204e-16)
&& (preRed <= 2.2204e-16)
&& (ratio <= 2.0)) {
throw new EstimationException("cost relative tolerance is too small ({0}),"
+ " no further reduction in the"
+ " sum of squares is possible",
new String[] {
Double.toString(costRelativeTolerance)
});
} else if (delta <= 2.2204e-16 * xNorm) {
throw new EstimationException("parameters relative tolerance is too small"
+ " ({0}), no further improvement in"
+ " the approximate solution is possible",
new String[] {
Double.toString(parRelativeTolerance)
});
} else if (maxCosine <= 2.2204e-16) {
throw new EstimationException("orthogonality tolerance is too small ({0}),"
+ " solution is orthogonal to the jacobian",
new String[] {
Double.toString(orthoTolerance)
});
}
}
}
throw new EstimationException("maximal number of evaluations exceeded ({0})",
new String[] {
Integer.toString(maxCostEval)
});
}
/** Determine the Levenberg-Marquardt parameter.
* <p>This implementation is a translation in Java of the MINPACK
* <a href="http://www.netlib.org/minpack/lmpar.f">lmpar</a>
* routine.</p>
* <p>This method sets the lmPar and lmDir attributes.</p>
* <p>The authors of the original fortran function are:</p>
* <ul>
* <li>Argonne National Laboratory. MINPACK project. March 1980</li>
* <li>Burton S. Garbow</li>
* <li>Kenneth E. Hillstrom</li>
* <li>Jorge J. More</li>
* </ul>
* <p>Luc Maisonobe did the Java translation.</p>
* @param qy array containing qTy
* @param delta upper bound on the euclidean norm of diagR * lmDir
* @param diag diagonal matrix
* @param work1 work array
* @param work2 work array
* @param work3 work array
*/
private void determineLMParameter(double[] qy, double delta, double[] diag,
double[] work1, double[] work2, double[] work3) {
// compute and store in x the gauss-newton direction, if the
// jacobian is rank-deficient, obtain a least squares solution
for (int j = 0; j < rank; ++j) {
lmDir[permutation[j]] = qy[j];
}
for (int j = rank; j < cols; ++j) {
lmDir[permutation[j]] = 0;
}
for (int k = rank - 1; k >= 0; --k) {
int pk = permutation[k];
double ypk = lmDir[pk] / diagR[pk];
for (int i = 0, index = pk; i < k; ++i, index += cols) {
lmDir[permutation[i]] -= ypk * jacobian[index];
}
lmDir[pk] = ypk;
}
// evaluate the function at the origin, and test
// for acceptance of the Gauss-Newton direction
double dxNorm = 0;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double s = diag[pj] * lmDir[pj];
work1[pj] = s;
dxNorm += s * s;
}
dxNorm = Math.sqrt(dxNorm);
double fp = dxNorm - delta;
if (fp <= 0.1 * delta) {
lmPar = 0;
return;
}
// if the jacobian is not rank deficient, the Newton step provides
// a lower bound, parl, for the zero of the function,
// otherwise set this bound to zero
double sum2, parl = 0;
if (rank == solvedCols) {
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
work1[pj] *= diag[pj] / dxNorm;
}
sum2 = 0;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double sum = 0;
for (int i = 0, index = pj; i < j; ++i, index += cols) {
sum += jacobian[index] * work1[permutation[i]];
}
double s = (work1[pj] - sum) / diagR[pj];
work1[pj] = s;
sum2 += s * s;
}
parl = fp / (delta * sum2);
}
// calculate an upper bound, paru, for the zero of the function
sum2 = 0;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double sum = 0;
for (int i = 0, index = pj; i <= j; ++i, index += cols) {
sum += jacobian[index] * qy[i];
}
sum /= diag[pj];
sum2 += sum * sum;
}
double gNorm = Math.sqrt(sum2);
double paru = gNorm / delta;
if (paru == 0) {
// 2.2251e-308 is the smallest positive real for IEE754
paru = 2.2251e-308 / Math.min(delta, 0.1);
}
// if the input par lies outside of the interval (parl,paru),
// set par to the closer endpoint
lmPar = Math.min(paru, Math.max(lmPar, parl));
if (lmPar == 0) {
lmPar = gNorm / dxNorm;
}
for (int countdown = 10; countdown >= 0; --countdown) {
// evaluate the function at the current value of lmPar
if (lmPar == 0) {
lmPar = Math.max(2.2251e-308, 0.001 * paru);
}
double sPar = Math.sqrt(lmPar);
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
work1[pj] = sPar * diag[pj];
}
determineLMDirection(qy, work1, work2, work3);
dxNorm = 0;
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
double s = diag[pj] * lmDir[pj];
work3[pj] = s;
dxNorm += s * s;
}
dxNorm = Math.sqrt(dxNorm);
double previousFP = fp;
fp = dxNorm - delta;
// if the function is small enough, accept the current value
// of lmPar, also test for the exceptional cases where parl is zero
if ((Math.abs(fp) <= 0.1 * delta)
|| ((parl == 0) && (fp <= previousFP) && (previousFP < 0))) {
return;
}
// compute the Newton correction
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
work1[pj] = work3[pj] * diag[pj] / dxNorm;
}
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
work1[pj] /= work2[j];
double tmp = work1[pj];
for (int i = j + 1; i < solvedCols; ++i) {
work1[permutation[i]] -= jacobian[i * cols + pj] * tmp;
}
}
sum2 = 0;
for (int j = 0; j < solvedCols; ++j) {
double s = work1[permutation[j]];
sum2 += s * s;
}
double correction = fp / (delta * sum2);
// depending on the sign of the function, update parl or paru.
if (fp > 0) {
parl = Math.max(parl, lmPar);
} else if (fp < 0) {
paru = Math.min(paru, lmPar);
}
// compute an improved estimate for lmPar
lmPar = Math.max(parl, lmPar + correction);
}
}
/** Solve a*x = b and d*x = 0 in the least squares sense.
* <p>This implementation is a translation in Java of the MINPACK
* <a href="http://www.netlib.org/minpack/qrsolv.f">qrsolv</a>
* routine.</p>
* <p>This method sets the lmDir and lmDiag attributes.</p>
* <p>The authors of the original fortran function are:</p>
* <ul>
* <li>Argonne National Laboratory. MINPACK project. March 1980</li>
* <li>Burton S. Garbow</li>
* <li>Kenneth E. Hillstrom</li>
* <li>Jorge J. More</li>
* </ul>
* <p>Luc Maisonobe did the Java translation.</p>
* @param qy array containing qTy
* @param diag diagonal matrix
* @param lmDiag diagonal elements associated with lmDir
* @param work work array
*/
private void determineLMDirection(double[] qy, double[] diag,
double[] lmDiag, double[] work) {
// copy R and Qty to preserve input and initialize s
// in particular, save the diagonal elements of R in lmDir
for (int j = 0; j < solvedCols; ++j) {
int pj = permutation[j];
for (int i = j + 1; i < solvedCols; ++i) {
jacobian[i * cols + pj] = jacobian[j * cols + permutation[i]];
}
lmDir[j] = diagR[pj];
work[j] = qy[j];
}
// eliminate the diagonal matrix d using a Givens rotation
for (int j = 0; j < solvedCols; ++j) {
// prepare the row of d to be eliminated, locating the
// diagonal element using p from the Q.R. factorization
int pj = permutation[j];
double dpj = diag[pj];
if (dpj != 0) {
Arrays.fill(lmDiag, j + 1, lmDiag.length, 0);
}
lmDiag[j] = dpj;
// the transformations to eliminate the row of d
// modify only a single element of Qty
// beyond the first n, which is initially zero.
double qtbpj = 0;
for (int k = j; k < solvedCols; ++k) {
int pk = permutation[k];
// determine a Givens rotation which eliminates the
// appropriate element in the current row of d
if (lmDiag[k] != 0) {
double sin, cos;
double rkk = jacobian[k * cols + pk];
if (Math.abs(rkk) < Math.abs(lmDiag[k])) {
double cotan = rkk / lmDiag[k];
sin = 1.0 / Math.sqrt(1.0 + cotan * cotan);
cos = sin * cotan;
} else {
double tan = lmDiag[k] / rkk;
cos = 1.0 / Math.sqrt(1.0 + tan * tan);
sin = cos * tan;
}
// compute the modified diagonal element of R and
// the modified element of (Qty,0)
jacobian[k * cols + pk] = cos * rkk + sin * lmDiag[k];
double temp = cos * work[k] + sin * qtbpj;
qtbpj = -sin * work[k] + cos * qtbpj;
work[k] = temp;
// accumulate the tranformation in the row of s
for (int i = k + 1; i < solvedCols; ++i) {
double rik = jacobian[i * cols + pk];
temp = cos * rik + sin * lmDiag[i];
lmDiag[i] = -sin * rik + cos * lmDiag[i];
jacobian[i * cols + pk] = temp;
}
}
}
// store the diagonal element of s and restore
// the corresponding diagonal element of R
int index = j * cols + permutation[j];
lmDiag[j] = jacobian[index];
jacobian[index] = lmDir[j];
}
// solve the triangular system for z, if the system is
// singular, then obtain a least squares solution
int nSing = solvedCols;
for (int j = 0; j < solvedCols; ++j) {
if ((lmDiag[j] == 0) && (nSing == solvedCols)) {
nSing = j;
}
if (nSing < solvedCols) {
work[j] = 0;
}
}
if (nSing > 0) {
for (int j = nSing - 1; j >= 0; --j) {
int pj = permutation[j];
double sum = 0;
for (int i = j + 1; i < nSing; ++i) {
sum += jacobian[i * cols + pj] * work[i];
}
work[j] = (work[j] - sum) / lmDiag[j];
}
}
// permute the components of z back to components of lmDir
for (int j = 0; j < lmDir.length; ++j) {
lmDir[permutation[j]] = work[j];
}
}
/** Decompose a matrix A as A.P = Q.R using Householder transforms.
* <p>As suggested in the P. Lascaux and R. Theodor book
* <i>Analyse num&eacute;rique matricielle appliqu&eacute;e &agrave;
* l'art de l'ing&eacute;nieur</i> (Masson, 1986), instead of representing
* the Householder transforms with u<sub>k</sub> unit vectors such that:
* <pre>
* H<sub>k</sub> = I - 2u<sub>k</sub>.u<sub>k</sub><sup>t</sup>
* </pre>
* we use <sub>k</sub> non-unit vectors such that:
* <pre>
* H<sub>k</sub> = I - beta<sub>k</sub>v<sub>k</sub>.v<sub>k</sub><sup>t</sup>
* </pre>
* where v<sub>k</sub> = a<sub>k</sub> - alpha<sub>k</sub> e<sub>k</sub>.
* The beta<sub>k</sub> coefficients are provided upon exit as recomputing
* them from the v<sub>k</sub> vectors would be costly.</p>
* <p>This decomposition handles rank deficient cases since the tranformations
* are performed in non-increasing columns norms order thanks to columns
* pivoting. The diagonal elements of the R matrix are therefore also in
* non-increasing absolute values order.</p>
*/
private void qrDecomposition() {
// initializations
for (int k = 0; k < cols; ++k) {
permutation[k] = k;
double norm2 = 0;
for (int index = k; index < jacobian.length; index += cols) {
double akk = jacobian[index];
norm2 += akk * akk;
}
jacNorm[k] = Math.sqrt(norm2);
}
// transform the matrix column after column
for (int k = 0; k < cols; ++k) {
// select the column with the greatest norm on active components
int nextColumn = -1;
double ak2 = Double.NEGATIVE_INFINITY;
for (int i = k; i < cols; ++i) {
double norm2 = 0;
int iDiag = k * cols + permutation[i];
for (int index = iDiag; index < jacobian.length; index += cols) {
double aki = jacobian[index];
norm2 += aki * aki;
}
if (norm2 > ak2) {
nextColumn = i;
ak2 = norm2;
}
}
if (ak2 == 0) {
rank = k;
return;
}
int pk = permutation[nextColumn];
permutation[nextColumn] = permutation[k];
permutation[k] = pk;
// choose alpha such that Hk.u = alpha ek
int kDiag = k * cols + pk;
double akk = jacobian[kDiag];
double alpha = (akk > 0) ? -Math.sqrt(ak2) : Math.sqrt(ak2);
double betak = 1.0 / (ak2 - akk * alpha);
beta[pk] = betak;
// transform the current column
diagR[pk] = alpha;
jacobian[kDiag] -= alpha;
// transform the remaining columns
for (int dk = cols - 1 - k; dk > 0; --dk) {
int dkp = permutation[k + dk] - pk;
double gamma = 0;
for (int index = kDiag; index < jacobian.length; index += cols) {
gamma += jacobian[index] * jacobian[index + dkp];
}
gamma *= betak;
for (int index = kDiag; index < jacobian.length; index += cols) {
jacobian[index + dkp] -= gamma * jacobian[index];
}
}
}
rank = solvedCols;
}
/** Compute the product Qt.y for some Q.R. decomposition.
* @param y vector to multiply (will be overwritten with the result)
*/
private void qTy(double[] y) {
for (int k = 0; k < cols; ++k) {
int pk = permutation[k];
int kDiag = k * cols + pk;
double gamma = 0;
for (int i = k, index = kDiag; i < rows; ++i, index += cols) {
gamma += jacobian[index] * y[i];
}
gamma *= beta[pk];
for (int i = k, index = kDiag; i < rows; ++i, index += cols) {
y[i] -= gamma * jacobian[index];
}
}
}
/** Array of measurements. */
private WeightedMeasurement[] measurements;
/** Array of parameters. */
private EstimatedParameter[] parameters;
/** Jacobian matrix.
* <p>Depending on the computation phase, this matrix is either in
* canonical form (just after the calls to updateJacobian) or in
* Q.R. decomposed form (after calls to qrDecomposition)</p>
*/
private double[] jacobian;
/** Number of columns of the jacobian matrix. */
private int cols;
/** Number of solved variables. */
private int solvedCols;
/** Number of rows of the jacobian matrix. */
private int rows;
/** Diagonal elements of the R matrix in the Q.R. decomposition. */
private double[] diagR;
/** Norms of the columns of the jacobian matrix. */
private double[] jacNorm;
/** Coefficients of the Householder transforms vectors. */
private double[] beta;
/** Columns permutation array. */
private int[] permutation;
/** Rank of the jacobian matrix. */
private int rank;
/** Levenberg-Marquardt parameter. */
private double lmPar;
/** Parameters evolution direction associated with lmPar. */
private double[] lmDir;
/** Residuals array.
* <p>Depending on the computation phase, this array is either in
* canonical form (just after the calls to updateResiduals) or in
* premultiplied by Qt form (just after calls to qTy)</p>
*/
private double[] residuals;
/** Cost value (square root of the sum of the residuals). */
private double cost;
/** Positive input variable used in determining the initial step bound. */
private double initialStepBoundFactor;
/** Maximal number of cost evaluations. */
private int maxCostEval;
/** Number of cost evaluations. */
private int costEvaluations;
/** Number of jacobian evaluations. */
private int jacobianEvaluations;
/** Desired relative error in the sum of squares. */
private double costRelativeTolerance;
/** Desired relative error in the approximate solution parameters. */
private double parRelativeTolerance;
/** Desired max cosine on the orthogonality between the function vector
* and the columns of the jacobian. */
private double orthoTolerance;
private static final long serialVersionUID = 5387476316105068340L;
}

View File

@ -0,0 +1,143 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.estimation;
import java.io.Serializable;
/** This class represents measurements in estimation problems.
* <p>This abstract class implements all the methods needed to handle
* measurements in a general way. It defines neither the {@link
* #getTheoreticalValue getTheoreticalValue} nor the {@link
* #getPartial getPartial} methods, which should be defined by
* sub-classes according to the specific problem.</p>
* <p>The {@link #getTheoreticalValue getTheoreticalValue} and {@link
* #getPartial getPartial} methods must always use the current
* estimate of the parameters set by the solver in the problem. These
* parameters can be retrieved through the {@link
* EstimationProblem#getAllParameters
* EstimationProblem.getAllParameters} method if the measurements are
* independant of the problem, or directly if they are implemented as
* inner classes of the problem.</p>
* <p>The instances for which the <code>ignored</code> flag is set
* through the {@link #setIgnored setIgnored} method are ignored by the
* solvers. This can be used to reject wrong measurements at some
* steps of the estimation.</p>
* @see EstimationProblem
* @version $Id: WeightedMeasurement.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class WeightedMeasurement implements Serializable {
/** Simple constructor.
* Build a measurement with the given parameters, and set its ignore
* flag to false.
* @param weight weight of the measurement in the least squares problem
* (two common choices are either to use 1.0 for all measurements, or to
* use a value proportional to the inverse of the variance of the measurement
* type)
* @param measuredValue measured value
*/
public WeightedMeasurement(double weight, double measuredValue) {
this.weight = weight;
this.measuredValue = measuredValue;
ignored = false;
}
/** Simple constructor.
* Build a measurement with the given parameters
* @param weight weight of the measurement in the least squares problem
* @param measuredValue measured value
* @param ignored true if the measurement should be ignored
*/
public WeightedMeasurement(double weight, double measuredValue,
boolean ignored) {
this.weight = weight;
this.measuredValue = measuredValue;
this.ignored = ignored;
}
/** Get the weight of the measurement in the least squares problem
* @return weight
*/
public double getWeight() {
return weight;
}
/** Get the measured value
* @return measured value
*/
public double getMeasuredValue() {
return measuredValue;
}
/** Get the residual for this measurement
* The residual is the measured value minus the theoretical value.
* @return residual
*/
public double getResidual() {
return measuredValue - getTheoreticalValue();
}
/** Get the theoretical value expected for this measurement
* <p>The theoretical value is the value expected for this measurement
* if the model and its parameter were all perfectly known.</p>
* <p>The value must be computed using the current estimate of the parameters
* set by the solver in the problem.</p>
* @return theoretical value
*/
public abstract double getTheoreticalValue();
/** Get the partial derivative of the {@link #getTheoreticalValue
* theoretical value} according to the parameter.
* <p>The value must be computed using the current estimate of the parameters
* set by the solver in the problem.</p>
* @param parameter parameter against which the partial derivative
* should be computed
* @return partial derivative of the {@link #getTheoreticalValue
* theoretical value}
*/
public abstract double getPartial(EstimatedParameter parameter);
/** Set the ignore flag to the specified value
* Setting the ignore flag to true allow to reject wrong
* measurements, which sometimes can be detected only rather late.
* @param ignored value for the ignore flag
*/
public void setIgnored(boolean ignored) {
this.ignored = ignored;
}
/** Check if this measurement should be ignored
* @return true if the measurement should be ignored
*/
public boolean isIgnored() {
return ignored;
}
private final double weight;
private final double measuredValue;
private boolean ignored;
}

View File

@ -0,0 +1,68 @@
<?xml version = "1.0" encoding = "ISO-8859-1" ?>
<!DOCTYPE argo SYSTEM "argo.dtd" >
<argo>
<documentation>
<authorname></authorname>
<version></version>
<description>
</description>
</documentation>
<searchpath href="PROJECT_DIR" />
<member
type="pgml"
name="estimation_classdiagram1.pgml"
/>
<member
type="xmi"
name="estimation.xmi"
/>
<historyfile name="" />
<stats>
<stat name="clicksInToDoPane"
value="0" />
<stat name="dblClicksInToDoPane"
value="0" />
<stat name="longestToDoList"
value="39" />
<stat name="longestAdd"
value="0" />
<stat name="longestHot"
value="0" />
<stat name="numCriticsFired"
value="16084" />
<stat name="numNotValid"
value="1" />
<stat name="numCriticsApplied"
value="0" />
<stat name="toDoPerspectivesChanged"
value="1" />
<stat name="navPerspectivesChanged"
value="1" />
<stat name="clicksInNavPane"
value="0" />
<stat name="numFinds"
value="1" />
<stat name="numJumpToRelated"
value="0" />
<stat name="numDecisionModel"
value="0" />
<stat name="numGoalsModel"
value="0" />
<stat name="numCriticBrowser"
value="0" />
<stat name="numNavConfig"
value="0" />
<stat name="numHushes"
value="0" />
<stat name="numChecks"
value="0" />
<stat name="Num_Button_Clicks"
value="0" />
<stat name="Drags_To_New"
value="0" />
<stat name="Drags_To_Existing"
value="0" />
</stats>
</argo>

View File

@ -0,0 +1,868 @@
<?xml version="1.0" encoding="UTF-8"?>
<XMI xmi.version="1.0">
<XMI.header>
<XMI.documentation>
<XMI.exporter>Novosoft UML Library</XMI.exporter>
<XMI.exporterVersion>0.4.17</XMI.exporterVersion>
</XMI.documentation>
<XMI.metamodel xmi.name="UML" xmi.version="1.3"/>
</XMI.header>
<XMI.content>
<Model_Management.Model xmi.id="xmi.1" xmi.uuid="25af9f:e3b707cd28:-8000">
<Foundation.Core.ModelElement.name>estimation</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Namespace.ownedElement>
<Foundation.Core.Class xmi.id="xmi.2" xmi.uuid="25af9f:e3b707cd28:-7fff">
<Foundation.Core.ModelElement.name>WeightedMeasurement</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="true"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.ModelElement.clientDependency>
<Foundation.Core.Dependency xmi.idref="xmi.3"/>
</Foundation.Core.ModelElement.clientDependency>
<Foundation.Core.GeneralizableElement.specialization>
<Foundation.Core.Generalization xmi.idref="xmi.4"/>
<Foundation.Core.Generalization xmi.idref="xmi.5"/>
</Foundation.Core.GeneralizableElement.specialization>
<Foundation.Core.Classifier.feature>
<Foundation.Core.Attribute xmi.id="xmi.6">
<Foundation.Core.ModelElement.name>weight_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="protected"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Attribute xmi.id="xmi.8">
<Foundation.Core.ModelElement.name>measuredValue_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="protected"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Attribute xmi.id="xmi.9">
<Foundation.Core.ModelElement.name>ignored_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="protected"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.10"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Operation xmi.id="xmi.11">
<Foundation.Core.ModelElement.name>getWeight</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.12">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.11"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.13">
<Foundation.Core.ModelElement.name>getMeasuredValue</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.14">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.13"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.15">
<Foundation.Core.ModelElement.name>setIgnored</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.16">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.15"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.17"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
<Foundation.Core.Parameter xmi.id="xmi.18">
<Foundation.Core.ModelElement.name>ignored</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="in"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.15"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.10"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.19">
<Foundation.Core.ModelElement.name>isIgnored</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.20">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.19"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.10"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.21">
<Foundation.Core.ModelElement.name>getTheoreticalValue</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.22">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.21"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.23">
<Foundation.Core.ModelElement.name>getResidual</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.24">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.23"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.25">
<Foundation.Core.ModelElement.name>getPartial</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.26">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.25"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
<Foundation.Core.Parameter xmi.id="xmi.27">
<Foundation.Core.ModelElement.name>parameter</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="in"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.25"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
</Foundation.Core.Classifier.feature>
</Foundation.Core.Class>
<Foundation.Core.DataType xmi.id="xmi.7">
<Foundation.Core.ModelElement.name>double</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.DataType>
<Foundation.Core.DataType xmi.id="xmi.10">
<Foundation.Core.ModelElement.name>boolean</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.DataType>
<Foundation.Core.DataType xmi.id="xmi.29">
<Foundation.Core.ModelElement.name>EstimatedParameter[]</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.DataType>
<Foundation.Core.DataType xmi.id="xmi.30">
<Foundation.Core.ModelElement.name>WeightedMeasurement[]</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.DataType>
<Foundation.Core.DataType xmi.id="xmi.17">
<Foundation.Core.ModelElement.name>void</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.DataType>
<Foundation.Core.Dependency xmi.id="xmi.31" xmi.uuid="25af9f:e3b707cd28:-7ffb">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Dependency.client>
<Foundation.Core.ModelElement xmi.idref="xmi.32"/>
</Foundation.Core.Dependency.client>
<Foundation.Core.Dependency.supplier>
<Foundation.Core.ModelElement xmi.idref="xmi.33"/>
</Foundation.Core.Dependency.supplier>
</Foundation.Core.Dependency>
<Foundation.Core.Association xmi.id="xmi.34" xmi.uuid="25af9f:e3b707cd28:-7ffa">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.35">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="false"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.34"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.32"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.36">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.34"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
<Foundation.Core.Class xmi.id="xmi.37" xmi.uuid="25af9f:e3b707cd28:-7ff9">
<Foundation.Core.ModelElement.name>LeastSquaresEstimator</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.ModelElement.clientDependency>
<Foundation.Core.Dependency xmi.idref="xmi.38"/>
</Foundation.Core.ModelElement.clientDependency>
<Foundation.Core.Classifier.feature>
<Foundation.Core.Attribute xmi.id="xmi.39">
<Foundation.Core.ModelElement.name>maxIterations_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="private"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.37"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.40"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Attribute xmi.id="xmi.41">
<Foundation.Core.ModelElement.name>convergence_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="private"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.37"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
</Foundation.Core.Classifier.feature>
</Foundation.Core.Class>
<Foundation.Core.Abstraction xmi.id="xmi.38" xmi.uuid="25af9f:e3b707cd28:-7ff8">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Dependency.client>
<Foundation.Core.ModelElement xmi.idref="xmi.37"/>
</Foundation.Core.Dependency.client>
<Foundation.Core.Dependency.supplier>
<Foundation.Core.ModelElement xmi.idref="xmi.33"/>
</Foundation.Core.Dependency.supplier>
</Foundation.Core.Abstraction>
<Foundation.Extension_Mechanisms.Stereotype xmi.id="xmi.42">
<Foundation.Core.ModelElement.name>realize</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Extension_Mechanisms.Stereotype.extendedElement>
<Foundation.Core.ModelElement xmi.idref="xmi.38"/>
</Foundation.Extension_Mechanisms.Stereotype.extendedElement>
</Foundation.Extension_Mechanisms.Stereotype>
<Foundation.Core.Class xmi.id="xmi.43" xmi.uuid="3a34af:e3c5366c0a:-7fff">
<Foundation.Core.ModelElement.name>MyProblem</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.ModelElement.clientDependency>
<Foundation.Core.Dependency xmi.idref="xmi.44"/>
</Foundation.Core.ModelElement.clientDependency>
</Foundation.Core.Class>
<Foundation.Core.Abstraction xmi.id="xmi.44" xmi.uuid="3a34af:e3c5366c0a:-7ffe">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Dependency.client>
<Foundation.Core.ModelElement xmi.idref="xmi.43"/>
</Foundation.Core.Dependency.client>
<Foundation.Core.Dependency.supplier>
<Foundation.Core.ModelElement xmi.idref="xmi.32"/>
</Foundation.Core.Dependency.supplier>
</Foundation.Core.Abstraction>
<Foundation.Extension_Mechanisms.Stereotype xmi.id="xmi.45">
<Foundation.Core.ModelElement.name>realize</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Extension_Mechanisms.Stereotype.extendedElement>
<Foundation.Core.ModelElement xmi.idref="xmi.44"/>
</Foundation.Extension_Mechanisms.Stereotype.extendedElement>
</Foundation.Extension_Mechanisms.Stereotype>
<Foundation.Core.Class xmi.id="xmi.46" xmi.uuid="3a34af:e3c5366c0a:-7ffd">
<Foundation.Core.ModelElement.name>MyFirstMeasurementType</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.GeneralizableElement.generalization>
<Foundation.Core.Generalization xmi.idref="xmi.4"/>
</Foundation.Core.GeneralizableElement.generalization>
</Foundation.Core.Class>
<Foundation.Core.Generalization xmi.id="xmi.4" xmi.uuid="3a34af:e3c5366c0a:-7ffc">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Generalization.child>
<Foundation.Core.GeneralizableElement xmi.idref="xmi.46"/>
</Foundation.Core.Generalization.child>
<Foundation.Core.Generalization.parent>
<Foundation.Core.GeneralizableElement xmi.idref="xmi.2"/>
</Foundation.Core.Generalization.parent>
</Foundation.Core.Generalization>
<Foundation.Core.Class xmi.id="xmi.47" xmi.uuid="3a34af:e3c5366c0a:-7ffb">
<Foundation.Core.ModelElement.name>MySecondMeasurementType</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.GeneralizableElement.generalization>
<Foundation.Core.Generalization xmi.idref="xmi.5"/>
</Foundation.Core.GeneralizableElement.generalization>
</Foundation.Core.Class>
<Foundation.Core.Generalization xmi.id="xmi.5" xmi.uuid="3a34af:e3c5366c0a:-7ffa">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Generalization.child>
<Foundation.Core.GeneralizableElement xmi.idref="xmi.47"/>
</Foundation.Core.Generalization.child>
<Foundation.Core.Generalization.parent>
<Foundation.Core.GeneralizableElement xmi.idref="xmi.2"/>
</Foundation.Core.Generalization.parent>
</Foundation.Core.Generalization>
<Foundation.Core.Association xmi.id="xmi.48" xmi.uuid="3a34af:e3c5366c0a:-7ff9">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.49">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Data_Types.Multiplicity xmi.id="xmi.50">
<Foundation.Data_Types.Multiplicity.range>
<Foundation.Data_Types.MultiplicityRange xmi.id="xmi.51">
<Foundation.Data_Types.MultiplicityRange.lower>1</Foundation.Data_Types.MultiplicityRange.lower>
<Foundation.Data_Types.MultiplicityRange.upper>-1</Foundation.Data_Types.MultiplicityRange.upper>
</Foundation.Data_Types.MultiplicityRange>
</Foundation.Data_Types.Multiplicity.range>
</Foundation.Data_Types.Multiplicity>
</Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.48"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.47"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.52">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="composite"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.48"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.43"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
<Foundation.Core.Association xmi.id="xmi.53" xmi.uuid="3a34af:e3c5366c0a:-7ff8">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.54">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Data_Types.Multiplicity xmi.idref="xmi.50"/>
</Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.53"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.46"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.55">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="composite"/>
<Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Data_Types.Multiplicity xmi.id="xmi.56">
<Foundation.Data_Types.Multiplicity.range>
<Foundation.Data_Types.MultiplicityRange xmi.id="xmi.57">
<Foundation.Data_Types.MultiplicityRange.lower>1</Foundation.Data_Types.MultiplicityRange.lower>
<Foundation.Data_Types.MultiplicityRange.upper>1</Foundation.Data_Types.MultiplicityRange.upper>
</Foundation.Data_Types.MultiplicityRange>
</Foundation.Data_Types.Multiplicity.range>
</Foundation.Data_Types.Multiplicity>
</Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.53"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.43"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
<Foundation.Core.Class xmi.id="xmi.40">
<Foundation.Core.ModelElement.name>int</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.Class>
<Foundation.Core.Class xmi.id="xmi.28" xmi.uuid="148603:e3c5d490c2:-7fff">
<Foundation.Core.ModelElement.name>EstimatedParameter</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.ModelElement.supplierDependency>
<Foundation.Core.Dependency xmi.idref="xmi.3"/>
</Foundation.Core.ModelElement.supplierDependency>
<Foundation.Core.Classifier.feature>
<Foundation.Core.Attribute xmi.id="xmi.58">
<Foundation.Core.ModelElement.name>name_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="private"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.59"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Attribute xmi.id="xmi.60">
<Foundation.Core.ModelElement.name>estimate_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="private"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Attribute xmi.id="xmi.61">
<Foundation.Core.ModelElement.name>bound_</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="private"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.StructuralFeature.type>
<Foundation.Core.Classifier xmi.idref="xmi.10"/>
</Foundation.Core.StructuralFeature.type>
</Foundation.Core.Attribute>
<Foundation.Core.Operation xmi.id="xmi.62">
<Foundation.Core.ModelElement.name>setEstimate</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.63">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.62"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.17"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
<Foundation.Core.Parameter xmi.id="xmi.64">
<Foundation.Core.ModelElement.name>estimate</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="in"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.62"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.65">
<Foundation.Core.ModelElement.name>getEstimate</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.66">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.65"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.7"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
<Foundation.Core.Operation xmi.id="xmi.67">
<Foundation.Core.ModelElement.name>isBound</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.visibility xmi.value="public"/>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
<Foundation.Core.Operation.isRoot xmi.value="false"/>
<Foundation.Core.Operation.isLeaf xmi.value="false"/>
<Foundation.Core.Operation.isAbstract xmi.value="false"/>
<Foundation.Core.Feature.owner>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.Feature.owner>
<Foundation.Core.BehavioralFeature.parameter>
<Foundation.Core.Parameter xmi.id="xmi.68">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.Parameter.kind xmi.value="return"/>
<Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.BehavioralFeature xmi.idref="xmi.67"/>
</Foundation.Core.Parameter.behavioralFeature>
<Foundation.Core.Parameter.type>
<Foundation.Core.Classifier xmi.idref="xmi.10"/>
</Foundation.Core.Parameter.type>
</Foundation.Core.Parameter>
</Foundation.Core.BehavioralFeature.parameter>
</Foundation.Core.Operation>
</Foundation.Core.Classifier.feature>
</Foundation.Core.Class>
<Foundation.Core.Class xmi.id="xmi.59">
<Foundation.Core.ModelElement.name>String</Foundation.Core.ModelElement.name>
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.Class.isActive xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
</Foundation.Core.Class>
<Foundation.Core.Association xmi.id="xmi.69" xmi.uuid="148603:e3c5d490c2:-7ffd">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.70">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="false"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.69"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.32"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.71">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.69"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
<Foundation.Core.Dependency xmi.id="xmi.3" xmi.uuid="148603:e3c5d490c2:-7ffc">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Dependency.client>
<Foundation.Core.ModelElement xmi.idref="xmi.2"/>
</Foundation.Core.Dependency.client>
<Foundation.Core.Dependency.supplier>
<Foundation.Core.ModelElement xmi.idref="xmi.28"/>
</Foundation.Core.Dependency.supplier>
</Foundation.Core.Dependency>
<Foundation.Core.Association xmi.id="xmi.72" xmi.uuid="148603:e3c5d490c2:-7ffb">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.73">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="composite"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.72"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.43"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.74">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.aggregation xmi.value="none"/>
<Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Data_Types.Multiplicity xmi.idref="xmi.50"/>
</Foundation.Core.AssociationEnd.multiplicity>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.72"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.28"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
<Foundation.Core.Association xmi.id="xmi.75" xmi.uuid="127-0-0-2-2d6513:e47a7c3046:-7fff">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isRoot xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isLeaf xmi.value="false"/>
<Foundation.Core.GeneralizableElement.isAbstract xmi.value="false"/>
<Foundation.Core.ModelElement.namespace>
<Foundation.Core.Namespace xmi.idref="xmi.1"/>
</Foundation.Core.ModelElement.namespace>
<Foundation.Core.Association.connection>
<Foundation.Core.AssociationEnd xmi.id="xmi.76">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.75"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.2"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
<Foundation.Core.AssociationEnd xmi.id="xmi.77">
<Foundation.Core.ModelElement.isSpecification xmi.value="false"/>
<Foundation.Core.AssociationEnd.isNavigable xmi.value="true"/>
<Foundation.Core.AssociationEnd.association>
<Foundation.Core.Association xmi.idref="xmi.75"/>
</Foundation.Core.AssociationEnd.association>
<Foundation.Core.AssociationEnd.type>
<Foundation.Core.Classifier xmi.idref="xmi.46"/>
</Foundation.Core.AssociationEnd.type>
</Foundation.Core.AssociationEnd>
</Foundation.Core.Association.connection>
</Foundation.Core.Association>
</Foundation.Core.Namespace.ownedElement>
</Model_Management.Model>
</XMI.content>
</XMI>

View File

@ -0,0 +1,815 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE pgml SYSTEM "pgml.dtd">
<pgml description="org.argouml.uml.diagram.static_structure.ui.UMLClassDiagram|25af9f:e3b707cd28:-8000"
name="overview"
>
<group name="Fig0"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[16, 168, 268, 156]"
href="25af9f:e3b707cd28:-7fff"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig0.0"
x="16"
y="168"
width="268"
height="155"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig0.1"
x="10"
y="10"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig0.2"
x="16"
y="168"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog.italic"
textsize="9"
>WeightedMeasurement</text>
<rectangle name="Fig0.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-1"
/>
<text name="Fig0.4"
x="16"
y="193"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>protected double weight_
protected double measuredValue_
protected boolean ignored_</text>
<text name="Fig0.5"
x="16"
y="238"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>public double getWeight()
public double getMeasuredValue()
public void setIgnored(boolean ignored)
public boolean isIgnored()
public double getTheoreticalValue()
public double getResidual()
public double getPartial(EstimatedParameter parameter)</text>
</group>
<group name="Fig1"
description="org.argouml.uml.diagram.static_structure.ui.FigInterface[288, 96, 271, 62]"
href="25af9f:e3b707cd28:-7ffd"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig1.0"
x="289"
y="97"
width="269"
height="60"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<rectangle name="Fig1.1"
x="288"
y="96"
width="271"
height="26"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig1.2"
x="289"
y="97"
fill="1"
fillcolor="-1"
stroke="0"
strokecolor="-16777216"
font="Lucida Sans"
textsize="10"
>&lt;&lt;Interface&gt;&gt;</text>
<text name="Fig1.3"
x="289"
y="109"
fill="1"
fillcolor="-1"
stroke="0"
strokecolor="-16777216"
font="dialog"
textsize="9"
>EstimationProblem</text>
<text name="Fig1.4"
x="288"
y="121"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>public WeightedMeasurement[] getMeasurements()
public EstimatedParameter[] getUnboundParameters()
public EstimatedParameter[] getAllParameters()</text>
</group>
<group name="Fig2"
description="org.argouml.uml.diagram.static_structure.ui.FigInterface[112, 0, 237, 64]"
href="25af9f:e3b707cd28:-7ffc"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig2.0"
x="113"
y="1"
width="235"
height="62"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<rectangle name="Fig2.1"
x="112"
y="0"
width="237"
height="26"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig2.2"
x="113"
y="1"
fill="1"
fillcolor="-1"
stroke="0"
strokecolor="-16777216"
font="Lucida Sans"
textsize="10"
>&lt;&lt;Interface&gt;&gt;</text>
<text name="Fig2.3"
x="113"
y="13"
fill="1"
fillcolor="-1"
stroke="0"
strokecolor="-16777216"
font="dialog"
textsize="9"
>Estimator</text>
<text name="Fig2.4"
x="112"
y="25"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>public void estimate(EstimationProblem problem)</text>
</group>
<group name="Fig3"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[459, 4, 140, 62]"
href="25af9f:e3b707cd28:-7ff9"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig3.0"
x="459"
y="4"
width="140"
height="61"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig3.1"
x="10"
y="10"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig3.2"
x="459"
y="4"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>LeastSquaresEstimator</text>
<rectangle name="Fig3.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-1"
/>
<text name="Fig3.4"
x="459"
y="21"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>private int maxIterations_
private double convergence_</text>
<text name="Fig3.5"
x="459"
y="48"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
</group>
<group name="Fig4"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[168, 436, 214, 43]"
href="3a34af:e3c5366c0a:-7fff"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig4.0"
x="168"
y="436"
width="214"
height="42"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig4.1"
x="10"
y="10"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig4.2"
x="168"
y="436"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>MyProblem</text>
<rectangle name="Fig4.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-256"
/>
<text name="Fig4.4"
x="168"
y="450"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig4.5"
x="168"
y="464"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
</group>
<group name="Fig5"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[24, 349, 125, 43]"
href="3a34af:e3c5366c0a:-7ffd"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig5.0"
x="24"
y="349"
width="125"
height="42"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig5.1"
x="10"
y="10"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig5.2"
x="24"
y="349"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>MyFirstMeasurementType</text>
<rectangle name="Fig5.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-256"
/>
<text name="Fig5.4"
x="24"
y="363"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig5.5"
x="24"
y="377"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
</group>
<group name="Fig6"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[168, 349, 137, 43]"
href="3a34af:e3c5366c0a:-7ffb"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig6.0"
x="168"
y="349"
width="137"
height="42"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig6.1"
x="10"
y="10"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig6.2"
x="168"
y="349"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>MySecondMeasurementType</text>
<rectangle name="Fig6.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-256"
/>
<text name="Fig6.4"
x="168"
y="363"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig6.5"
x="168"
y="377"
fill="1"
fillcolor="-256"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
</group>
<group name="Fig7"
description="org.argouml.uml.diagram.static_structure.ui.FigClass[320, 240, 197, 89]"
href="148603:e3c5d490c2:-7fff"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<rectangle name="Fig7.0"
x="320"
y="240"
width="197"
height="88"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
/>
<text name="Fig7.1"
x="10"
y="10"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
></text>
<text name="Fig7.2"
x="320"
y="240"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>EstimatedParameter</text>
<rectangle name="Fig7.3"
x="10"
y="15"
width="2"
height="60"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-1"
/>
<text name="Fig7.4"
x="320"
y="256"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>private String name_
private double estimate_
private boolean bound_</text>
<text name="Fig7.5"
x="320"
y="292"
fill="1"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
font="dialog"
textsize="9"
>public void setEstimate(double estimate)
public double getEstimate()
public boolean isBound()</text>
</group>
<group name="Fig8"
description="org.argouml.uml.diagram.ui.FigDependency"
href="25af9f:e3b707cd28:-7ffb"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig9"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="25af9f:e3b707cd28:-7ffa"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig10"
description="org.argouml.uml.diagram.ui.FigDependency"
href="25af9f:e3b707cd28:-7ff8"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig11"
description="org.argouml.uml.diagram.ui.FigDependency"
href="3a34af:e3c5366c0a:-7ffe"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig12"
description="org.argouml.uml.diagram.ui.FigGeneralization"
href="3a34af:e3c5366c0a:-7ffc"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig13"
description="org.argouml.uml.diagram.ui.FigGeneralization"
href="3a34af:e3c5366c0a:-7ffa"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig14"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="3a34af:e3c5366c0a:-7ff9"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig15"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="3a34af:e3c5366c0a:-7ff8"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig16"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="148603:e3c5d490c2:-7ffd"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig17"
description="org.argouml.uml.diagram.ui.FigDependency"
href="148603:e3c5d490c2:-7ffc"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig18"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="148603:e3c5d490c2:-7ffb"
stroke="1"
strokecolor="-16777216"
>
<private>
</private>
<path name="LAYER_NULL"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="0"
y="0" />
</path>
</group>
<group name="Fig19"
description="org.argouml.uml.diagram.ui.FigAssociation"
href="127-0-0-2-2d6513:e47a7c3046:-7fff"
stroke="1"
strokecolor="-16777216"
>
<private>
sourcePortFig="Fig0.0"
destPortFig="Fig5.0"
sourceFigNode="Fig0"
destFigNode="Fig5"
</private>
<path name="Fig19.1"
description="org.tigris.gef.presentation.FigPoly"
fill="0"
fillcolor="-1"
stroke="1"
strokecolor="-16777216"
>
<moveto x="224"
y="323" />
<lineto x="224"
y="336" />
<lineto x="48"
y="336" />
<lineto x="48"
y="349" />
</path>
</group>
</pgml>

View File

@ -0,0 +1,52 @@
<html>
<body>
This package provides classes to solve estimation problems.
<p>The estimation problems considered here are parametric problems where a user model
depends on initially unknown scalar parameters and several measurements made on
values that depend on the model are available. As an example, one can consider the
flow rate of a river given rain data on its vicinity, or the center and radius of a
circle given points on a ring.</p>
<p>One important class of estimation problems is weighted least squares problems.
They basically consist in finding the values for some parameters p<sub>k</sub> such
that a cost function J = sum(w<sub>i</sub> r<sub>i</sub><sup>2</sup>) is minimized.
The various r<sub>i</sub> terms represent the deviation r<sub>i</sub> =
mes<sub>i</sub> - mod<sub>i</sub> between the measurements and the parameterized
models. The w<sub>i</sub> factors are the measurements weights, they are often chosen
either all equal to 1.0 or proportional to the inverse of the variance of the
measurement type. The solver adjusts the values of the estimated parameters
p<sub>k</sub> which are not bound. It does not touch the parameters which have been
put in a bound state by the user.</p>
<p>This package provides the {@link
org.spaceroots.mantissa.estimation.EstimatedParameter EstimatedParameter} class to
represent each estimated parameter, and the {@link
org.spaceroots.mantissa.estimation.WeightedMeasurement WeightedMeasurement} abstract
class the user can extend to define its measurements. All parameters and measurements
are then provided to some {@link org.spaceroots.mantissa.estimation.Estimator
Estimator} packed together in an {@link
org.spaceroots.mantissa.estimation.EstimationProblem EstimationProblem} instance
which acts only as a container. The package provides two common estimators for
weighted least squares problems, one based on the {@link
org.spaceroots.mantissa.estimation.GaussNewtonEstimator Gauss-Newton} method and the
other one based on the {@link
org.spaceroots.mantissa.estimation.LevenbergMarquardtEstimator Levenberg-Marquardt}
method.</p>
<p>The class diagram for the public classes of this package is displayed below. The
orange boxes <code>UserProblem</code>, <code>UserFirstMeasurementType</code> and
<code>UserSecondMeasurementType</code> are exemple of what the user should create to
use this package: implement his own problem and measurement types using the {@link
org.spaceroots.mantissa.estimation.EstimationProblem} interface and {@link
org.spaceroots.mantissa.estimation.WeightedMeasurement} abstract class, and then use
one of the provided estimators (for example {@link
org.spaceroots.mantissa.estimation.GaussNewtonEstimator} or {@link
org.spaceroots.mantissa.estimation.LevenbergMarquardtEstimator}) to solve it. The
white boxes are the interfaces and classes already provided by the library.</p>
<img src="doc-files/org_spaceroots_mantissa_estimation_classes.png" />
@author L. Maisonobe
</body>
</html>

View File

@ -0,0 +1,286 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import org.spaceroots.mantissa.estimation.*;
/** This class is the base class for all curve fitting classes in the package.
* <p>This class handles all common features of curve fitting like the
* sample points handling. It declares two methods ({@link
* #valueAt} and {@link #partial}) which should be implemented by
* sub-classes to define the precise shape of the curve they
* represent.</p>
* @version $Id: AbstractCurveFitter.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class AbstractCurveFitter
implements EstimationProblem, Serializable {
/**
* Simple constructor.
* @param n number of coefficients in the underlying function
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
protected AbstractCurveFitter(int n,
int maxIterations,
double convergence,
double steadyStateThreshold,
double epsilon) {
coefficients = new EstimatedParameter[n];
measurements = new ArrayList();
measurementsArray = null;
this.maxIterations = maxIterations;
this.steadyStateThreshold = steadyStateThreshold;
this.convergence = convergence;
this.epsilon = epsilon;
}
/**
* Simple constructor.
* @param coefficients first estimate of the coefficients. A
* reference to this array is hold by the newly created object. Its
* elements will be adjusted during the fitting process and they will
* be set to the adjusted coefficients at the end.
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
protected AbstractCurveFitter(EstimatedParameter[] coefficients,
int maxIterations,
double convergence,
double steadyStateThreshold,
double epsilon) {
this.coefficients = coefficients;
measurements = new ArrayList();
measurementsArray = null;
this.maxIterations = maxIterations;
this.steadyStateThreshold = steadyStateThreshold;
this.convergence = convergence;
this.epsilon = epsilon;
}
/** Add a weighted (x,y) pair to the sample.
* @param weight weight of this pair in the fit
* @param x abscissa
* @param y ordinate, we have <code>y = f (x)</code>
*/
public void addWeightedPair(double weight, double x, double y) {
measurementsArray = null;
measurements.add(new FitMeasurement(weight, x, y));
}
/** Perform the fitting.
* <p>This method compute the coefficients of the curve that best
* fit the sample of weighted pairs previously given through calls
* to the {@link #addWeightedPair addWeightedPair} method.</p>
* @return coefficients of the curve
* @exception EstimationException if the fitting is not possible
* (for example if the sample has to few independant points)
*/
public double[] fit()
throws EstimationException {
// perform the fit using a linear least square estimator
new GaussNewtonEstimator(maxIterations, convergence,
steadyStateThreshold, epsilon).estimate(this);
// extract the coefficients
double[] fittedCoefficients = new double[coefficients.length];
for (int i = 0; i < coefficients.length; ++i) {
fittedCoefficients[i] = coefficients[i].getEstimate();
}
return fittedCoefficients;
}
public WeightedMeasurement[] getMeasurements() {
if (measurementsArray == null) {
measurementsArray = new FitMeasurement[measurements.size()];
int i = 0;
for (Iterator iterator = measurements.iterator(); iterator.hasNext(); ++i) {
measurementsArray[i] = (FitMeasurement) iterator.next();
}
}
return measurementsArray;
}
/** Get the unbound parameters of the problem.
* For a curve fitting, none of the function coefficient is bound.
* @return unbound parameters
*/
public EstimatedParameter[] getUnboundParameters() {
return coefficients;
}
/** Get all the parameters of the problem.
* @return parameters
*/
public EstimatedParameter[] getAllParameters() {
return coefficients;
}
/** Utility method to sort the measurements with respect to the abscissa.
* <p>This method is provided as a utility for derived classes. As
* an example, the {@link HarmonicFitter} class needs it in order to
* compute a first guess of the coefficients to initialize the
* estimation algorithm.</p>
*/
protected void sortMeasurements() {
// Since the samples are almost always already sorted, this
// method is implemented as an insertion sort that reorders the
// elements in place. Insertion sort is very efficient in this case.
FitMeasurement curr = (FitMeasurement) measurements.get(0);
for (int j = 1; j < measurements.size (); ++j) {
FitMeasurement prec = curr;
curr = (FitMeasurement) measurements.get(j);
if (curr.x < prec.x) {
// the current element should be inserted closer to the beginning
int i = j - 1;
FitMeasurement mI = (FitMeasurement) measurements.get(i);
while ((i >= 0) && (curr.x < mI.x)) {
measurements.set(i + 1, mI);
if (i-- != 0) {
mI = (FitMeasurement) measurements.get(i);
} else {
mI = null;
}
}
measurements.set(i + 1, curr);
curr = (FitMeasurement) measurements.get(j);
}
}
// make sure subsequent calls to getMeasurements
// will not use the unsorted array
measurementsArray = null;
}
/** Get the value of the function at x according to the current parameters value.
* @param x abscissa at which the theoretical value is requested
* @return theoretical value at x
*/
public abstract double valueAt(double x);
/** Get the derivative of the function at x with respect to parameter p.
* @param x abscissa at which the partial derivative is requested
* @param p parameter with respect to which the derivative is requested
* @return partial derivative
*/
public abstract double partial(double x, EstimatedParameter p);
/** This class represents the fit measurements.
* One measurement is a weighted pair (x, y), where <code>y = f
* (x)</code> is the value of the function at x abscissa. This class
* is an inner class because the methods related to the computation
* of f values and derivative are proveded by the fitter
* implementations.
*/
public class FitMeasurement
extends WeightedMeasurement {
/** Simple constructor.
* @param weight weight of the measurement in the fitting process
* @param x abscissa of the measurement
* @param y ordinate of the measurement
*/
public FitMeasurement(double weight, double x, double y) {
super(weight, y);
this.x = x;
}
/** Get the value of the fitted function at x.
* @return theoretical value at the measurement abscissa
*/
public double getTheoreticalValue() {
return valueAt(x);
}
/** Partial derivative with respect to one function coefficient.
* @param p parameter with respect to which the derivative is requested
* @return partial derivative
*/
public double getPartial(EstimatedParameter p) {
return partial(x, p);
}
/** Abscissa of the measurement. */
public final double x;
private static final long serialVersionUID = -2682582852369995960L;
}
/** Coefficients of the function */
protected EstimatedParameter[] coefficients;
/** Measurements vector */
protected List measurements;
/** Measurements array.
* This array contains the same entries as measurements_, but in a
* different structure.
*/
private FitMeasurement[] measurementsArray;
private int maxIterations;
private double convergence;
private double steadyStateThreshold;
private double epsilon;
}

View File

@ -0,0 +1,74 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator;
import org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair;
/** This class provides sampled values of the function t -> [f(t)^2, f'(t)^2].
* This class is a helper class used to compute a first guess of the
* harmonic coefficients of a function <code>f (t) = a cos (omega t +
* phi)</code>.
* @see FFPIterator
* @see HarmonicCoefficientsGuesser
* @version $Id: F2FP2Iterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class F2FP2Iterator
implements SampledFunctionIterator, Serializable {
public F2FP2Iterator(AbstractCurveFitter.FitMeasurement[] measurements) {
ffpIterator = new FFPIterator(measurements);
}
public int getDimension() {
return 2;
}
public boolean hasNext() {
return ffpIterator.hasNext();
}
public VectorialValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException {
// get the raw values from the underlying FFPIterator
VectorialValuedPair point = ffpIterator.nextSamplePoint();
// hack the values (to avoid building a new object)
double[] y = point.getY();
y[0] *= y[0];
y[1] *= y[1];
return point;
}
private FFPIterator ffpIterator;
private static final long serialVersionUID = -8113110433795298072L;
}

View File

@ -0,0 +1,100 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator;
import org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair;
/** This class provides sampled values of the function t -> [f(t), f'(t)].
* This class is a helper class used to compute a first guess of the
* harmonic coefficients of a function <code>f (t) = a cos (omega t +
* phi)</code>.
* @see F2FP2Iterator
* @see HarmonicCoefficientsGuesser
* @version $Id: FFPIterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class FFPIterator
implements SampledFunctionIterator, Serializable {
public FFPIterator(AbstractCurveFitter.FitMeasurement[] measurements) {
this.measurements = measurements;
// initialize the points of the raw sample
current = measurements[0];
currentY = current.getMeasuredValue();
next = measurements[1];
nextY = next.getMeasuredValue();
nextIndex = 2;
}
public int getDimension() {
return 2;
}
public boolean hasNext() {
return nextIndex < measurements.length;
}
public VectorialValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException {
if (nextIndex >= measurements.length) {
throw new ExhaustedSampleException(measurements.length);
}
// shift the points
previous = current;
previousY = currentY;
current = next;
currentY = nextY;
next = measurements[nextIndex++];
nextY = next.getMeasuredValue();
// return the two dimensions vector [f(x), f'(x)]
double[] table = new double[2];
table[0] = currentY;
table[1] = (nextY - previousY) / (next.x - previous.x);
return new VectorialValuedPair(current.x, table);
}
private AbstractCurveFitter.FitMeasurement[] measurements;
private int nextIndex;
private AbstractCurveFitter.FitMeasurement previous;
private double previousY;
private AbstractCurveFitter.FitMeasurement current;
private double nextY;
private AbstractCurveFitter.FitMeasurement next;
private double currentY;
private static final long serialVersionUID = -3187229691615380125L;
}

View File

@ -0,0 +1,271 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
import org.spaceroots.mantissa.functions.vectorial.SampledFunctionIterator;
import org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair;
import org.spaceroots.mantissa.quadrature.vectorial.EnhancedSimpsonIntegratorSampler;
import org.spaceroots.mantissa.estimation.EstimationException;
/** This class guesses harmonic coefficients from a sample.
* <p>The algorithm used to guess the coefficients is as follows:</p>
* <p>We know f (t) at some sampling points ti and want to find a,
* omega and phi such that f (t) = a cos (omega t + phi).
* </p>
*
* <p>From the analytical expression, we can compute two primitives :
* <pre>
* If2 (t) = int (f^2) = a^2 * [t + S (t)] / 2
* If'2 (t) = int (f'^2) = a^2 * omega^2 * [t - S (t)] / 2
* where S (t) = sin (2 * (omega * t + phi)) / (2 * omega)
* </pre>
* </p>
*
* <p>We can remove S between these expressions :
* <pre>
* If'2 (t) = a^2 * omega ^ 2 * t - omega ^ 2 * If2 (t)
* </pre>
* </p>
*
* <p>The preceding expression shows that If'2 (t) is a linear
* combination of both t and If2 (t): If'2 (t) = A * t + B * If2 (t)
* </p>
*
* <p>From the primitive, we can deduce the same form for definite
* integrals between t1 and ti for each ti :
* <pre>
* If2 (ti) - If2 (t1) = A * (ti - t1) + B * (If2 (ti) - If2 (t1))
* </pre>
* </p>
*
* <p>We can find the coefficients A and B that best fit the sample
* to this linear expression by computing the definite integrals for
* each sample points.
* </p>
*
* <p>For a bilinear expression z (xi, yi) = A * xi + B * yi, the
* coefficients a and b that minimize a least square criterion
* Sum ((zi - z (xi, yi))^2) are given by these expressions:</p>
* <pre>
*
* Sum (yi^2) Sum (xi zi) - Sum (xi yi) Sum (yi zi)
* A = ------------------------------------------------
* Sum (xi^2) Sum (yi^2) - Sum (xi yi) Sum (xi yi)
*
* Sum (xi^2) Sum (yi zi) - Sum (xi yi) Sum (xi zi)
* B = ------------------------------------------------
* Sum (xi^2) Sum (yi^2) - Sum (xi yi) Sum (xi yi)
* </pre>
* </p>
*
*
* <p>In fact, we can assume both a and omega are positive and
* compute them directly, knowing that A = a^2 * omega^2 and that
* B = - omega^2. The complete algorithm is therefore:</p>
* <pre>
*
* for each ti from t1 to t(n-1), compute:
* f (ti)
* f' (ti) = (f (t(i+1)) - f(t(i-1))) / (t(i+1) - t(i-1))
* xi = ti - t1
* yi = int (f^2) from t1 to ti
* zi = int (f'^2) from t1 to ti
* update the sums Sum (xi^2), Sum (yi^2),
* Sum (xi yi), Sum (xi zi)
* and Sum (yi zi)
* end for
*
* |-------------------------------------------------
* \ | Sum (yi^2) Sum (xi zi) - Sum (xi yi) Sum (yi zi)
* a = \ | ------------------------------------------------
* \| Sum (xi yi) Sum (xi zi) - Sum (xi^2) Sum (yi zi)
*
*
* |-------------------------------------------------
* \ | Sum (xi yi) Sum (xi zi) - Sum (xi^2) Sum (yi zi)
* omega = \ | ------------------------------------------------
* \| Sum (xi^2) Sum (yi^2) - Sum (xi yi) Sum (xi yi)
*
* </pre>
* </p>
* <p>Once we know omega, we can compute:
* <pre>
* fc = omega * f (t) * cos (omega * t) - f' (t) * sin (omega * t)
* fs = omega * f (t) * sin (omega * t) + f' (t) * cos (omega * t)
* </pre>
* </p>
* <p>It appears that <code>fc = a * omega * cos (phi)</code> and
* <code>fs = -a * omega * sin (phi)</code>, so we can use these
* expressions to compute phi. The best estimate over the sample is
* given by averaging these expressions.
* </p>
* <p>Since integrals and means are involved in the preceding
* estimations, these operations run in O(n) time, where n is the
* number of measurements.</p>
* @version $Id: HarmonicCoefficientsGuesser.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class HarmonicCoefficientsGuesser
implements Serializable{
public HarmonicCoefficientsGuesser(AbstractCurveFitter.FitMeasurement[] measurements) {
this.measurements = measurements;
a = Double.NaN;
omega = Double.NaN;
}
/** Estimate a first guess of the coefficients.
* @exception ExhaustedSampleException if the sample is exhausted.
* @exception FunctionException if the integrator throws one.
* @exception EstimationException if the sample is too short or if
* the first guess cannot be computed (when the elements under the
* square roots are negative).
* */
public void guess()
throws ExhaustedSampleException, FunctionException, EstimationException {
guessAOmega();
guessPhi();
}
/** Estimate a first guess of the a and omega coefficients.
* @exception ExhaustedSampleException if the sample is exhausted.
* @exception FunctionException if the integrator throws one.
* @exception EstimationException if the sample is too short or if
* the first guess cannot be computed (when the elements under the
* square roots are negative).
*/
private void guessAOmega()
throws ExhaustedSampleException, FunctionException, EstimationException {
// initialize the sums for the linear model between the two integrals
double sx2 = 0.0;
double sy2 = 0.0;
double sxy = 0.0;
double sxz = 0.0;
double syz = 0.0;
// build the integrals sampler
F2FP2Iterator iter = new F2FP2Iterator(measurements);
SampledFunctionIterator sampler =
new EnhancedSimpsonIntegratorSampler(iter);
VectorialValuedPair p0 = sampler.nextSamplePoint();
double p0X = p0.getX();
double[] p0Y = p0.getY();
// get the points for the linear model
while (sampler.hasNext()) {
VectorialValuedPair point = sampler.nextSamplePoint();
double pX = point.getX();
double[] pY = point.getY();
double dx = pX - p0X;
double dy0 = pY[0] - p0Y[0];
double dy1 = pY[1] - p0Y[1];
sx2 += dx * dx;
sy2 += dy0 * dy0;
sxy += dx * dy0;
sxz += dx * dy1;
syz += dy0 * dy1;
}
// compute the amplitude and pulsation coefficients
double c1 = sy2 * sxz - sxy * syz;
double c2 = sxy * sxz - sx2 * syz;
double c3 = sx2 * sy2 - sxy * sxy;
if ((c1 / c2 < 0.0) || (c2 / c3 < 0.0)) {
throw new EstimationException("unable to guess a first estimate");
}
a = Math.sqrt(c1 / c2);
omega = Math.sqrt(c2 / c3);
}
/** Estimate a first guess of the phi coefficient.
* @exception ExhaustedSampleException if the sample is exhausted.
* @exception FunctionException if the sampler throws one.
*/
private void guessPhi()
throws ExhaustedSampleException, FunctionException {
SampledFunctionIterator iter = new FFPIterator(measurements);
// initialize the means
double fcMean = 0.0;
double fsMean = 0.0;
while (iter.hasNext()) {
VectorialValuedPair point = iter.nextSamplePoint();
double omegaX = omega * point.getX();
double[] pY = point.getY();
double cosine = Math.cos(omegaX);
double sine = Math.sin(omegaX);
fcMean += omega * pY[0] * cosine - pY[1] * sine;
fsMean += omega * pY[0] * sine + pY[1] * cosine;
}
phi = Math.atan2(-fsMean, fcMean);
}
public double getOmega() {
return omega;
}
public double getA() {
return a;
}
public double getPhi() {
return phi;
}
private AbstractCurveFitter.FitMeasurement[] measurements;
private double a;
private double omega;
private double phi;
private static final long serialVersionUID = 2400399048702758814L;
}

View File

@ -0,0 +1,197 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import org.spaceroots.mantissa.estimation.*;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
import org.spaceroots.mantissa.functions.FunctionException;
/** This class implements a curve fitting specialized for sinusoids.
* <p>Harmonic fitting is a very simple case of curve fitting. The
* estimated coefficients are the amplitude a, the pulsation omega and
* the phase phi: <code>f (t) = a cos (omega t + phi)</code>. They are
* searched by a least square estimator initialized with a rough guess
* based on integrals.</p>
* <p>This class <emph>is by no means optimized</emph>, neither versus
* space nor versus time performance.</p>
* @version $Id: HarmonicFitter.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class HarmonicFitter
extends AbstractCurveFitter
implements EstimationProblem, Serializable {
/**
* Simple constructor.
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
public HarmonicFitter(int maxIterations, double convergence,
double steadyStateThreshold, double epsilon) {
super(3, maxIterations, convergence, steadyStateThreshold, epsilon);
coefficients[0] = new EstimatedParameter("a", 2.0 * Math.PI);
coefficients[1] = new EstimatedParameter("omega", 0.0);
coefficients[2] = new EstimatedParameter("phi", 0.0);
firstGuessNeeded = true;
}
/**
* Simple constructor.
* <p>This constructor can be used when a first estimate of the
* coefficients is already known.</p>
* @param coefficients first estimate of the coefficients.
* A reference to this array is hold by the newly created
* object. Its elements will be adjusted during the fitting process
* and they will be set to the adjusted coefficients at the end.
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
public HarmonicFitter(EstimatedParameter[] coefficients,
int maxIterations, double convergence,
double steadyStateThreshold, double epsilon) {
super(coefficients,
maxIterations, convergence,
steadyStateThreshold, epsilon);
firstGuessNeeded = false;
}
public double[] fit()
throws EstimationException {
if (firstGuessNeeded) {
if (measurements.size() < 4) {
throw new EstimationException("sample must contain at least {0} points",
new String[] {
Integer.toString(4)
});
}
sortMeasurements();
try {
HarmonicCoefficientsGuesser guesser =
new HarmonicCoefficientsGuesser((FitMeasurement[]) getMeasurements());
guesser.guess();
coefficients[0].setEstimate(guesser.getA());
coefficients[1].setEstimate(guesser.getOmega());
coefficients[2].setEstimate(guesser.getPhi());
} catch(ExhaustedSampleException e) {
throw new EstimationException(e);
} catch(FunctionException e) {
throw new EstimationException(e);
}
firstGuessNeeded = false;
}
return super.fit();
}
/** Get the current amplitude coefficient estimate.
* Get a, where <code>f (t) = a cos (omega t + phi)</code>
* @return current amplitude coefficient estimate
*/
public double getAmplitude() {
return coefficients[0].getEstimate();
}
/** Get the current pulsation coefficient estimate.
* Get omega, where <code>f (t) = a cos (omega t + phi)</code>
* @return current pulsation coefficient estimate
*/
public double getPulsation() {
return coefficients[1].getEstimate();
}
/** Get the current phase coefficient estimate.
* Get phi, where <code>f (t) = a cos (omega t + phi)</code>
* @return current phase coefficient estimate
*/
public double getPhase() {
return coefficients[2].getEstimate();
}
/** Get the value of the function at x according to the current parameters value.
* @param x abscissa at which the theoretical value is requested
* @return theoretical value at x
*/
public double valueAt(double x) {
double a = coefficients[0].getEstimate();
double omega = coefficients[1].getEstimate();
double phi = coefficients[2].getEstimate();
return a * Math.cos(omega * x + phi);
}
/** Get the derivative of the function at x with respect to parameter p.
* @param x abscissa at which the partial derivative is requested
* @param p parameter with respect to which the derivative is requested
* @return partial derivative
*/
public double partial(double x, EstimatedParameter p) {
double a = coefficients[0].getEstimate();
double omega = coefficients[1].getEstimate();
double phi = coefficients[2].getEstimate();
if (p == coefficients[0]) {
return Math.cos(omega * x + phi);
} else if (p == coefficients[1]) {
return -a * x * Math.sin(omega * x + phi);
} else {
return -a * Math.sin(omega * x + phi);
}
}
/** Indicator of the need to compute a first guess of the coefficients. */
private boolean firstGuessNeeded;
private static final long serialVersionUID = -8722683066277473450L;
}

View File

@ -0,0 +1,44 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import org.spaceroots.mantissa.estimation.EstimatedParameter;
/** This class represents a polynomial coefficient.
* <p>Each coefficient is uniquely defined by its degree.</p>
* @see PolynomialFitter
* @version $Id: PolynomialCoefficient.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class PolynomialCoefficient
extends EstimatedParameter {
public PolynomialCoefficient(int degree) {
super("a" + degree, 0.0);
this.degree = degree;
}
public final int degree;
private static final long serialVersionUID = 5775845068390259552L;
}

View File

@ -0,0 +1,143 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.fitting;
import java.io.Serializable;
import org.spaceroots.mantissa.estimation.*;
/** This class implements a curve fitting specialized for polynomials.
* <p>Polynomial fitting is a very simple case of curve fitting. The
* estimated coefficients are the polynom coefficients. They are
* searched by a least square estimator.</p>
* <p>This class <emph>is by no means optimized</emph>, neither in
* space nor in time performance.</p>
* @see PolynomialCoefficient
* @version $Id: PolynomialFitter.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class PolynomialFitter
extends AbstractCurveFitter
implements EstimationProblem, Serializable {
/**
* Simple constructor.
* <p>The polynomial fitter built this way are complete polynoms,
* ie. a n-degree polynom has n+1 coefficients. In order to build
* fitter for sparse polynoms (for example <code>a x^20 - b
* x^30</code>, on should first build the coefficients array and
* provide it to {@link
* #PolynomialFitter(PolynomialCoefficient[], int, double, double,
* double)}.</p>
* @param degree maximal degree of the polynom
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
public PolynomialFitter(int degree,
int maxIterations, double convergence,
double steadyStateThreshold, double epsilon) {
super(degree + 1,
maxIterations, steadyStateThreshold,
convergence, epsilon);
for (int i = 0; i < coefficients.length; ++i) {
coefficients[i] = new PolynomialCoefficient(i);
}
}
/**
* Simple constructor.
* <p>This constructor can be used either when a first estimate of
* the coefficients is already known (which is of little interest
* because the fit cost is the same whether a first guess is known or
* not) or when one needs to handle sparse polynoms like <code>a
* x^20 - b x^30</code>.</p>
* @param coefficients first estimate of the coefficients.
* A reference to this array is hold by the newly created
* object. Its elements will be adjusted during the fitting process
* and they will be set to the adjusted coefficients at the end.
* @param maxIterations maximum number of iterations allowed
* @param convergence criterion threshold below which we do not need
* to improve the criterion anymore
* @param steadyStateThreshold steady state detection threshold, the
* problem has reached a steady state (read converged) if
* <code>Math.abs (Jn - Jn-1) < Jn * convergence</code>, where
* <code>Jn</code> and <code>Jn-1</code> are the current and
* preceding criterion value (square sum of the weighted residuals
* of considered measurements).
* @param epsilon threshold under which the matrix of the linearized
* problem is considered singular (see {@link
* org.spaceroots.mantissa.linalg.SquareMatrix#solve(
* org.spaceroots.mantissa.linalg.Matrix,double) SquareMatrix.solve}).
*/
public PolynomialFitter(PolynomialCoefficient[] coefficients,
int maxIterations, double convergence,
double steadyStateThreshold, double epsilon) {
super(coefficients,
maxIterations, steadyStateThreshold,
convergence, epsilon);
}
/** Get the value of the function at x according to the current parameters value.
* @param x abscissa at which the theoretical value is requested
* @return theoretical value at x
*/
public double valueAt(double x) {
double y = coefficients[coefficients.length - 1].getEstimate();
for (int i = coefficients.length - 2; i >= 0; --i) {
y = y * x + coefficients[i].getEstimate();
}
return y;
}
/** Get the derivative of the function at x with respect to parameter p.
* @param x abscissa at which the partial derivative is requested
* @param p parameter with respect to which the derivative is requested
* @return partial derivative
*/
public double partial(double x, EstimatedParameter p) {
return Math.pow(x, ((PolynomialCoefficient) p).degree);
}
private static final long serialVersionUID = -226724596015163603L;
}

View File

@ -0,0 +1,26 @@
<html>
<body>
This package provides classes to perform curve fitting.
<p>Curve fitting is a special case of an {@link
org.spaceroots.mantissa.estimation.EstimationProblem estimation problem}
were the parameters are the coefficients of a function <code>f</code>
whose graph <code>y=f(x)</code> should pass through sample points, and
were the measurements are the ordinates of the curve
<code>yi=f(xi)</code>.</p>
<p>The organisation of this package is explained in the class diagram
below. The {@link org.spaceroots.mantissa.fitting.AbstractCurveFitter
AbstractCurveFitter} class is the base class for all curve fitting
classes, it handles all common features like sample points
handling. Each specific curve fitting class should sub-class this
abstract class and implement the {@link
org.spaceroots.mantissa.fitting.AbstractCurveFitter#valueAt valueAt} and
{@link org.spaceroots.mantissa.fitting.AbstractCurveFitter#partial
partial} methods.</p>
<img src="doc-files/org_spaceroots_mantissa_fitting_classes.png" />
@author L. Maisonobe
</body>
</html>

View File

@ -0,0 +1,42 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions;
import org.spaceroots.mantissa.MantissaException;
/** This class represents exceptions thrown by sample iterators.
* @version $Id: ExhaustedSampleException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ExhaustedSampleException
extends MantissaException {
/** Simple constructor.
* @param size size of the sample
*/
public ExhaustedSampleException(int size) {
super("sample contains only {0} elements",
new String[] { Integer.toString(size) });
}
private static final long serialVersionUID = -1490493298938282440L;
}

View File

@ -0,0 +1,59 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions;
import org.spaceroots.mantissa.MantissaException;
/** This class represents exceptions thrown by scalar functions.
* @version $Id: FunctionException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class FunctionException
extends MantissaException {
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public FunctionException(String specifier, String[] parts) {
super(specifier, parts);
}
/** Simple constructor.
* Build an exception by translating the specified message
* @param message message to translate
*/
public FunctionException(String message) {
super(message);
}
/** Simple constructor.
* Build an exception from a cause
* @param cause cause of this exception
*/
public FunctionException(Throwable cause) {
super(cause);
}
private static final long serialVersionUID = 1455885104381976115L;
}

View File

@ -0,0 +1,73 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
/** This class is a simple wrapper allowing to iterate over a
* SampledFunction.
* <p>The basic implementation of the iteration interface does not
* perform any transformation on the sample, it only handles a loop
* over the underlying sampled function.</p>
* @see SampledFunction
* @version $Id: BasicSampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class BasicSampledFunctionIterator
implements SampledFunctionIterator, Serializable {
/** Underlying sampled function. */
private final SampledFunction function;
/** Next sample element. */
private int next;
/** Simple constructor.
* Build an instance from a SampledFunction
* @param function smapled function over which we want to iterate
*/
public BasicSampledFunctionIterator(SampledFunction function) {
this.function = function;
next = 0;
}
public boolean hasNext() {
return next < function.size();
}
public ScalarValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException {
if (next >= function.size()) {
throw new ExhaustedSampleException(function.size());
}
int current = next++;
return function.samplePointAt(current);
}
private static final long serialVersionUID = -9106690005598356403L;
}

View File

@ -0,0 +1,55 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import org.spaceroots.mantissa.functions.FunctionException;
/** This interface represents scalar functions of one real variable.
* <p>This interface should be implemented by all scalar functions
* that can be evaluated at any point. This does not imply that an
* explicit definition is available, a function given by an implicit
* function that should be numerically solved for each point for
* example is considered a computable function.</p>
* <p>The {@link ComputableFunctionSampler} class can be used to
* transform classes implementing this interface into classes
* implementing the {@link SampledFunction} interface.</p>
* <p>Several numerical algorithms (Gauss-Legendre integrators for
* example) need to choose themselves the evaluation points, so they
* can handle only objects that implement this interface.</p>
* @see org.spaceroots.mantissa.quadrature.scalar.ComputableFunctionIntegrator
* @see SampledFunction
* @version $Id: ComputableFunction.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface ComputableFunction {
/** Get the value of the function at the specified abscissa.
* @param x current abscissa
* @return function value
* @exception FunctionException if something goes wrong
*/
public double valueAt(double x)
throws FunctionException;
}

View File

@ -0,0 +1,146 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
/** This class is a wrapper allowing to sample a
* {@link ComputableFunction}.
* <p>The sample produced is a regular sample. It can be specified by
* several means :
* <ul>
* <li> from an initial point a step and a number of points</li>
* <li> from a range and a number of points</li>
* <li> from a range and a step between points.</li>
* </ul>
* In the latter case, the step can optionaly be adjusted in order to
* have the last point exactly at the upper bound of the range.</p>
* <p>The sample points are computed on demand, they are not
* stored. This allow to use this method for very large sample with
* little memory overhead. The drawback is that if the same sample
* points are going to be requested several times, they will be
* recomputed each time. In this case, the user should consider
* storing the points by some other means.</p>
* @see ComputableFunction
* @version $Id: ComputableFunctionSampler.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ComputableFunctionSampler
implements SampledFunction, Serializable {
/** Underlying computable function. */
private ComputableFunction function;
/** Beginning abscissa. */
private double begin;
/** Step between points. */
private double step;
/** Total number of points. */
private int n;
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}. Beware of the
* classical off-by-one problem ! If you want to have a sample like
* this : 0.0, 0.1, 0.2 ..., 1.0, then you should specify step = 0.1
* and n = 11 (not n = 10).
* @param begin beginning of the range (will be the abscissa of the
* first point)
* @param step step between points
* @param n number of points
*/
public ComputableFunctionSampler(ComputableFunction function,
double begin, double step, int n) {
this.function = function;
this.begin = begin;
this.step = step;
this.n = n;
}
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}.
* @param range abscissa range (from <code>range [0]</code> to
* <code>range [1]</code>)
* @param n number of points
*/
public ComputableFunctionSampler(ComputableFunction function,
double[] range, int n) {
this.function = function;
begin = range[0];
step = (range[1] - range[0]) / (n - 1);
this.n = n;
}
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}.
* @param range abscissa range (from <code>range [0]</code> to
* <code>range [1]</code>)
* @param step step between points
* @param adjustStep if true, the step is reduced in order to have
* the last point of the sample exactly at <code>range [1]</code>,
* if false the last point will be between <code>range [1] -
* step</code> and <code>range [1]</code> */
public ComputableFunctionSampler(ComputableFunction function,
double[] range, double step,
boolean adjustStep) {
this.function = function;
begin = range [0];
if (adjustStep) {
n = (int) Math.ceil((range[1] - range[0]) / step);
this.step = (range[1] - range[0]) / (n - 1);
} else {
n = (int) Math.floor((range[1] - range[0]) / step);
this.step = step;
}
}
public int size() {
return n;
}
public ScalarValuedPair samplePointAt(int index)
throws ArrayIndexOutOfBoundsException, FunctionException {
if (index < 0 || index >= n) {
throw new ArrayIndexOutOfBoundsException();
}
double x = begin + index * step;
return new ScalarValuedPair(x, function.valueAt(x));
}
private static final long serialVersionUID = -5127043442851795719L;
}

View File

@ -0,0 +1,66 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import org.spaceroots.mantissa.functions.FunctionException;
/** This interface represent sampled scalar functions.
* <p>A function sample is an ordered set of points of the form (x, y)
* where x is the abscissa of the point and y is the function value at
* x. It is typically a function that has been computed by external
* means or the result of measurements.</p>
* <p>The {@link ComputableFunctionSampler} class can be used to
* transform classes implementing the {@link ComputableFunction}
* interface into classes implementing this interface.</p>
* <p>Sampled functions cannot be directly handled by integrators
* implementing the {@link
* org.spaceroots.mantissa.quadrature.scalar.SampledFunctionIntegrator
* SampledFunctionIntegrator}. These integrators need a {@link
* SampledFunctionIterator} object to iterate over the
* sample.</p>
* @see SampledFunctionIterator
* @see ComputableFunctionSampler
* @see ComputableFunction
* @version $Id: SampledFunction.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface SampledFunction {
/** Get the number of points in the sample.
* @return number of points in the sample
*/
public int size();
/** Get the abscissa and value of the sample at the specified index.
* @param index index in the sample, should be between 0 and
* {@link #size} - 1
* @return abscissa and value of the sample at the specified index
* @exception ArrayIndexOutOfBoundsException if the index is wrong
* @exception FunctionException if an eventual underlying function
* throws one
*/
public ScalarValuedPair samplePointAt(int index)
throws ArrayIndexOutOfBoundsException, FunctionException;
}

View File

@ -0,0 +1,47 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
/** This interface provides iteration services over scalar functions
* samples.
* @see SampledFunction
* @version $Id: SampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface SampledFunctionIterator {
/** Check if the iterator can provide another point.
* @return true if the iterator can provide another point.
*/
public boolean hasNext();
/** Get the next point of a sampled function.
* @return the next point of the sampled function
* @exception ExhaustedSampleException if the sample has been exhausted
* @exception FunctionException if the underlying function throws one
*/
public ScalarValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException;
}

View File

@ -0,0 +1,97 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.scalar;
import java.io.Serializable;
/** This class represents an (x, f(x)) pair for scalar functions.
* <p>A scalar function is a function of one scalar parameter x whose
* value is a scalar. This class is used has a simple placeholder to
* contain both an abscissa and the value of the function at this
* abscissa.</p>
* @see SampledFunction
* @see org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair
* @version $Id: ScalarValuedPair.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ScalarValuedPair
implements Serializable {
/** Simple constructor.
* Build an instance from its coordinates
* @param x abscissa
* @param y ordinate (value of the function)
*/
public ScalarValuedPair(double x, double y) {
this.x = x;
this.y = y;
}
/** Copy-constructor.
* @param p point to copy
*/
public ScalarValuedPair(ScalarValuedPair p) {
x = p.x;
y = p.y;
}
/**
* Getter for the abscissa.
* @return value of the abscissa
*/
public double getX() {
return x;
}
/**
* Getter for the ordinate.
* @return value of the ordinate
*/
public double getY() {
return y;
}
/**
* Setter for the abscissa.
* @param x new value for the abscissa
*/
public void setX(double x) {
this.x = x;
}
/**
* Setter for the ordinate.
* @param y new value for the ordinate
*/
public void setY(double y) {
this.y = y;
}
/** Abscissa of the point. */
private double x;
/** Scalar ordinate of the point, y = f (x). */
private double y;
private static final long serialVersionUID = 1884346552569300794L;
}

View File

@ -0,0 +1,78 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
/** This class is a wrapper allowing to iterate over a
* SampledFunction.
* <p>The basic implementation of the iteration interface does not
* perform any transformation on the sample, it only handles a loop
* over the underlying sampled function.</p>
* @see SampledFunction
* @version $Id: BasicSampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class BasicSampledFunctionIterator
implements SampledFunctionIterator, Serializable {
/** Simple constructor.
* Build an instance from a SampledFunction
* @param function smapled function over which we want to iterate
*/
public BasicSampledFunctionIterator(SampledFunction function) {
this.function = function;
next = 0;
}
public int getDimension() {
return function.getDimension();
}
public boolean hasNext() {
return next < function.size();
}
public VectorialValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException {
if (next >= function.size()) {
throw new ExhaustedSampleException(function.size());
}
int current = next++;
return function.samplePointAt(current);
}
/** Underlying sampled function. */
private final SampledFunction function;
/** Next sample element. */
private int next;
private static final long serialVersionUID = -4386278658288500627L;
}

View File

@ -0,0 +1,59 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import org.spaceroots.mantissa.functions.FunctionException;
/** This interface represents vectorial functions of one real variable.
* <p>This interface should be implemented by all vectorial functions
* that can be evaluated at any point. This does not imply that an
* explicit definition is available, a function given by an implicit
* function that should be numerically solved for each point for
* example is considered a computable function.</p>
* <p>The {@link ComputableFunctionSampler} class can be used to
* transform classes implementing this interface into classes
* implementing the {@link SampledFunction} interface.</p>
* <p>Several numerical algorithms (Gauss-Legendre integrators for
* example) need to choose themselves the evaluation points, so they
* can handle only objects that implement this interface.</p>
* @see org.spaceroots.mantissa.quadrature.vectorial.ComputableFunctionIntegrator
* @see SampledFunction
* @version $Id: ComputableFunction.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface ComputableFunction {
/** Get the dimension of the vectorial values of the function.
* @return dimension
*/
public int getDimension();
/** Get the value of the function at the specified abscissa.
* @param x current abscissa
* @return function value
* @exception FunctionException if something goes wrong
*/
public double[] valueAt(double x)
throws FunctionException;
}

View File

@ -0,0 +1,150 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import java.io.Serializable;
import org.spaceroots.mantissa.functions.FunctionException;
/** This class is a wrapper allowing to sample a
* {@link ComputableFunction}.
* <p>The sample produced is a regular sample. It can be specified by
* several means :
* <ul>
* <li> from an initial point a step and a number of points</li>
* <li> from a range and a number of points</li>
* <li> from a range and a step between points.</li>
* </ul>
* In the latter case, the step can optionaly be adjusted in order to
* have the last point exactly at the upper bound of the range.</p>
* <p>The sample points are computed on demand, they are not
* stored. This allow to use this method for very large sample with
* little memory overhead. The drawback is that if the same sample
* points are going to be requested several times, they will be
* recomputed each time. In this case, the user should consider
* storing the points by some other means.</p>
* @see ComputableFunction
* @version $Id: ComputableFunctionSampler.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ComputableFunctionSampler
implements SampledFunction, Serializable {
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}. Beware of the
* classical off-by-one problem ! If you want to have a sample like
* this : 0.0, 0.1, 0.2 ..., 1.0, then you should specify step = 0.1
* and n = 11 (not n = 10).
* @param begin beginning of the range (will be the abscissa of the
* first point)
* @param step step between points
* @param n number of points
*/
public ComputableFunctionSampler(ComputableFunction function,
double begin, double step, int n) {
this.function = function;
this.begin = begin;
this.step = step;
this.n = n;
}
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}.
* @param range abscissa range (from <code>range [0]</code> to
* <code>range [1]</code>)
* @param n number of points
*/
public ComputableFunctionSampler(ComputableFunction function,
double[] range, int n) {
this.function = function;
begin = range[0];
step = (range[1] - range[0]) / (n - 1);
this.n = n;
}
/**
* Constructor.
* Build a sample from an {@link ComputableFunction}.
* @param range abscissa range (from <code>range [0]</code> to
* <code>range [1]</code>)
* @param step step between points
* @param adjustStep if true, the step is reduced in order to have
* the last point of the sample exactly at <code>range [1]</code>,
* if false the last point will be between <code>range [1] -
* step</code> and <code>range [1]</code> */
public ComputableFunctionSampler(ComputableFunction function,
double[] range, double step,
boolean adjustStep) {
this.function = function;
begin = range[0];
if (adjustStep) {
n = (int) Math.ceil((range[1] - range[0]) / step);
this.step = (range[1] - range[0]) / (n - 1);
} else {
n = (int) Math.floor((range[1] - range[0]) / step);
this.step = step;
}
}
public int size() {
return n;
}
public int getDimension() {
return function.getDimension();
}
public VectorialValuedPair samplePointAt(int index)
throws ArrayIndexOutOfBoundsException, FunctionException {
if (index < 0 || index >= n) {
throw new ArrayIndexOutOfBoundsException();
}
double x = begin + index * step;
return new VectorialValuedPair (x, function.valueAt(x));
}
/** Underlying computable function. */
private ComputableFunction function;
/** Beginning abscissa. */
private double begin;
/** Step between points. */
private double step;
/** Total number of points. */
private int n;
private static final long serialVersionUID = 1368582688313212821L;
}

View File

@ -0,0 +1,71 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import org.spaceroots.mantissa.functions.FunctionException;
/** This interface represent sampled vectorial functions.
* <p>A function sample is an ordered set of points of the form (x, y)
* where x is the abscissa of the point and y is the function value at
* x. It is typically a function that has been computed by external
* means or the result of measurements.</p>
* <p>The {@link ComputableFunctionSampler} class can be used to
* transform classes implementing the {@link ComputableFunction}
* interface into classes implementing this interface.</p>
* <p>Sampled functions cannot be directly handled by integrators
* implementing the {@link
* org.spaceroots.mantissa.quadrature.vectorial.SampledFunctionIntegrator
* SampledFunctionIntegrator}. These integrators need a {@link
* SampledFunctionIterator} object to iterate over the
* sample.</p>
* @see SampledFunctionIterator
* @see ComputableFunctionSampler
* @see ComputableFunction
* @version $Id: SampledFunction.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface SampledFunction {
/** Get the number of points in the sample.
* @return number of points in the sample
*/
public int size();
/** Get the dimension of the vectorial values of the function.
* @return dimension
*/
public int getDimension();
/** Get the abscissa and value of the sample at the specified index.
* @param index index in the sample, should be between 0 and
* {@link #size} - 1
* @return abscissa and value of the sample at the specified index
* @exception ArrayIndexOutOfBoundsException if the index is wrong
* @exception FunctionException if an eventual underlying function
* throws one
*/
public VectorialValuedPair samplePointAt(int index)
throws ArrayIndexOutOfBoundsException, FunctionException;
}

View File

@ -0,0 +1,52 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import org.spaceroots.mantissa.functions.FunctionException;
import org.spaceroots.mantissa.functions.ExhaustedSampleException;
/** This interface provides iteration services over vectorial functions
* samples.
* @see SampledFunction
* @version $Id: SampledFunctionIterator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface SampledFunctionIterator {
/** Get the dimension of the vectorial values of the function.
* @return dimension
*/
public int getDimension();
/** Check if the iterator can provide another point.
* @return true if the iterator can provide another point.
*/
public boolean hasNext();
/** Get the next point of a sampled function.
* @return the next point of the sampled function
* @exception ExhaustedSampleException if the sample has been exhausted
* @exception FunctionException if the underlying function throws one
*/
public VectorialValuedPair nextSamplePoint()
throws ExhaustedSampleException, FunctionException;
}

View File

@ -0,0 +1,99 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.functions.vectorial;
import java.io.Serializable;
/** This class represents an (x, f(x)) pair for vectorial functions.
* <p>A vectorial function is a function of one vectorial parameter x whose
* value is a vector. This class is used has a simple placeholder to
* contain both an abscissa and the value of the function at this
* abscissa.</p>
* @see SampledFunction
* @see org.spaceroots.mantissa.functions.vectorial.VectorialValuedPair
* @version $Id: VectorialValuedPair.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class VectorialValuedPair
implements Serializable {
/**
* Simple constructor.
* Build an instance from its coordinates
* @param x abscissa
* @param y ordinate (value of the function)
*/
public VectorialValuedPair(double x, double[] y) {
this.x = x;
this.y = y;
}
/**
* Copy-constructor.
* @param p point to copy
*/
public VectorialValuedPair(VectorialValuedPair p) {
x = p.x;
y = p.y;
}
/**
* Getter for the abscissa.
* @return value of the abscissa
*/
public double getX() {
return x;
}
/**
* Getter for the ordinate.
* @return value of the ordinate
*/
public double[] getY() {
return y;
}
/**
* Setter for the abscissa.
* @param x new value for the abscissa
*/
public void setX(double x) {
this.x = x;
}
/**
* Setter for the ordinate.
* @param y new value for the ordinate
*/
public void setY(double[] y) {
this.y = y;
}
/** Abscissa of the point. */
private double x;
/** Vectorial ordinate of the point, y = f (x). */
private double[] y;
private static final long serialVersionUID = -1336411215846160578L;
}

View File

@ -0,0 +1,44 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.geometry;
import org.spaceroots.mantissa.MantissaException;
/** This class represents exceptions thrown while extractiong Cardan
* or Euler angles from a rotation.
* @version $Id: CardanEulerSingularityException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class CardanEulerSingularityException
extends MantissaException {
/** Simple constructor.
* build an exception with a default message.
* @param isCardan if true, the rotation is related to Cardan angles,
* if false it is related to EulerAngles
*/
public CardanEulerSingularityException(boolean isCardan) {
super(isCardan ? "Cardan angles singularity" : "Euler angles singularity");
}
private static final long serialVersionUID = -1360952845582206770L;
}

View File

@ -0,0 +1,204 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.geometry;
/**
* This class implements immutable vectors in a three-dimensional space.
* @version $Id: ImmutableVector3D.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ImmutableVector3D
extends Vector3D {
/** Simple constructor.
* Build a vector from its coordinates
* @param x abscissa
* @param y ordinate
* @param z height
*/
public ImmutableVector3D(double x, double y, double z) {
super(x, y, z);
computeNorm();
}
/** Simple constructor.
* Build a vector from its azimuthal coordinates
* @param alpha azimuth around Z
* (0 is +X, PI/2 is +Y, PI is -X and 3PI/2 is -Y)
* @param delta elevation above (XY) plane, from -PI to +PI
*/
public ImmutableVector3D(double alpha, double delta) {
super(alpha, delta);
computeNorm();
}
/** Copy constructor.
* Build a copy of a vector
* @param v vector to copy
*/
public ImmutableVector3D(Vector3D v) {
super(v);
computeNorm();
}
/** Multiplicative constructor
* Build a vector from another one and a scale factor.
* The vector built will be a * u
* @param a scale factor
* @param u base (unscaled) vector
*/
public ImmutableVector3D(double a, Vector3D u) {
super(a, u);
computeNorm();
}
/** Linear constructor
* Build a vector from two other ones and corresponding scale factors.
* The vector built will be a * u + b * v
* @param a first scale factor
* @param u first base (unscaled) vector
* @param b second scale factor
* @param v second base (unscaled) vector
*/
public ImmutableVector3D(double a, Vector3D u, double b, Vector3D v) {
super(a, u, b, v);
computeNorm();
}
/** Set the abscissa of the vector.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param x new abscissa for the vector
* @exception UnsupportedOperationException thrown in every case
*/
public void setX(double x) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Set the ordinate of the vector.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param y new ordinate for the vector
* @exception UnsupportedOperationException thrown in every case
*/
public void setY(double y) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Set the height of the vector.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param z new height for the vector
* @exception UnsupportedOperationException thrown in every case
*/
public void setZ(double z) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Set all coordinates of the vector.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param x new abscissa for the vector
* @param y new ordinate for the vector
* @param z new height for the vector
* @exception UnsupportedOperationException thrown in every case
*/
public void setCoordinates(double x, double y, double z) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Compute the norm once and for all. */
private void computeNorm() {
norm = Math.sqrt(x * x + y * y + z * z);
}
/** Get the norm for the vector.
* @return euclidian norm for the vector
*/
public double getNorm() {
return norm;
}
/** Add a vector to the instance.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param v vector to add
* @exception UnsupportedOperationException thrown in every case
*/
public void addToSelf(Vector3D v) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Subtract a vector from the instance.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param v vector to subtract
* @exception UnsupportedOperationException thrown in every case
*/
public void subtractFromSelf(Vector3D v) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Normalize the instance.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @exception UnsupportedOperationException thrown in every case
*/
public void normalizeSelf() {
throw new UnsupportedOperationException("vector is immutable");
}
/** Revert the instance.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @exception UnsupportedOperationException thrown in every case
*/
public void negateSelf() {
throw new UnsupportedOperationException("vector is immutable");
}
/** Multiply the instance by a scalar
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param a scalar by which the instance should be multiplied
* @exception UnsupportedOperationException thrown in every case
*/
public void multiplySelf(double a) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Reinitialize internal state from the specified array slice data.
* This method should not be called for immutable vectors, it always
* throws an <code>UnsupportedOperationException</code> exception
* @param start start index in the array
* @param array array holding the data to extract
* @exception UnsupportedOperationException thrown in every case
*/
public void mapStateFromArray(int start, double[] array) {
throw new UnsupportedOperationException("vector is immutable");
}
/** Norm of the vector. */
private double norm;
private static final long serialVersionUID = 5377895850033895270L;
}

View File

@ -0,0 +1,44 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.geometry;
import org.spaceroots.mantissa.MantissaException;
/** This class represents exceptions thrown while building rotations
* from matrices.
* @version $Id: NotARotationMatrixException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class NotARotationMatrixException
extends MantissaException {
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public NotARotationMatrixException(String specifier, String[] parts) {
super(specifier, parts);
}
private static final long serialVersionUID = 5647178478658937642L;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.geometry;
/**
* This class is a utility representing a rotation order specification
* for Cardan or Euler angles specification.
* This class cannot be instanciated by the user. He can only use one
* of the twelve predefined supported orders as an argument to either
* the {@link Rotation#Rotation(RotationOrder,double,double,double)}
* constructor or the {@link Rotation#getAngles} method.
* @version $Id: RotationOrder.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public final class RotationOrder {
/** Private constructor.
* This is a utility class that cannot be instantiated by the user,
* so its only constructor is private.
* @param name name of the rotation order
*/
private RotationOrder(String name) {
this.name = name;
}
/** Get a string representation of the instance.
* @return a string representation of the instance (in fact, its name)
*/
public String toString() {
return name;
}
/** Set of Cardan angles.
* this ordered set of rotations is around X, then around Y, then
* around Z
*/
public static final RotationOrder XYZ = new RotationOrder("XYZ");
/** Set of Cardan angles.
* this ordered set of rotations is around X, then around Z, then
* around Y
*/
public static final RotationOrder XZY = new RotationOrder("XZY");
/** Set of Cardan angles.
* this ordered set of rotations is around Y, then around X, then
* around Z
*/
public static final RotationOrder YXZ = new RotationOrder("YXZ");
/** Set of Cardan angles.
* this ordered set of rotations is around Y, then around Z, then
* around X
*/
public static final RotationOrder YZX = new RotationOrder("YZX");
/** Set of Cardan angles.
* this ordered set of rotations is around Z, then around X, then
* around Y
*/
public static final RotationOrder ZXY = new RotationOrder("ZXY");
/** Set of Cardan angles.
* this ordered set of rotations is around Z, then around Y, then
* around X
*/
public static final RotationOrder ZYX = new RotationOrder("ZYX");
/** Set of Euler angles.
* this ordered set of rotations is around X, then around Y, then
* around X
*/
public static final RotationOrder XYX = new RotationOrder("XYX");
/** Set of Euler angles.
* this ordered set of rotations is around X, then around Z, then
* around X
*/
public static final RotationOrder XZX = new RotationOrder("XZX");
/** Set of Euler angles.
* this ordered set of rotations is around Y, then around X, then
* around Y
*/
public static final RotationOrder YXY = new RotationOrder("YXY");
/** Set of Euler angles.
* this ordered set of rotations is around Y, then around Z, then
* around Y
*/
public static final RotationOrder YZY = new RotationOrder("YZY");
/** Set of Euler angles.
* this ordered set of rotations is around Z, then around X, then
* around Z
*/
public static final RotationOrder ZXZ = new RotationOrder("ZXZ");
/** Set of Euler angles.
* this ordered set of rotations is around Z, then around Y, then
* around Z
*/
public static final RotationOrder ZYZ = new RotationOrder("ZYZ");
/** Name of the rotations order. */
private final String name;
}

View File

@ -0,0 +1,491 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.geometry;
import java.io.Serializable;
import org.spaceroots.mantissa.utilities.ArraySliceMappable;
/** This class implements vectors in a three-dimensional space.
* @version $Id: Vector3D.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class Vector3D
implements ArraySliceMappable, Serializable {
/** First canonical vector (coordinates : 1, 0, 0).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D plusI = new ImmutableVector3D(1, 0, 0);
/** Opposite of the first canonical vector (coordinates : -1, 0, 0).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D minusI = new ImmutableVector3D(-1, 0, 0);
/** Second canonical vector (coordinates : 0, 1, 0).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D plusJ = new ImmutableVector3D(0, 1, 0);
/** Opposite of the second canonical vector (coordinates : 0, -1, 0).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D minusJ = new ImmutableVector3D(0, -1, 0);
/** Third canonical vector (coordinates : 0, 0, 1).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D plusK = new ImmutableVector3D(0, 0, 1);
/** Opposite of the third canonical vector (coordinates : 0, 0, -1).
* This is really an {@link ImmutableVector3D ImmutableVector3D},
* hence it can't be changed in any way.
*/
public static final Vector3D minusK = new ImmutableVector3D(0, 0, -1);
/** Simple constructor.
* Build a null vector.
*/
public Vector3D() {
x = 0;
y = 0;
z = 0;
}
/** Simple constructor.
* Build a vector from its coordinates
* @param x abscissa
* @param y ordinate
* @param z height
* @see #getX()
* @see #getY()
* @see #getZ()
*/
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/** Simple constructor.
* Build a vector from its azimuthal coordinates
* @param alpha azimuth (&alpha;) around Z
* (0 is +X, &pi;/2 is +Y, &pi; is -X and 3&pi;/2 is -Y)
* @param delta elevation (&delta;) above (XY) plane, from -&pi;/2 to +&pi;/2
* @see #getAlpha()
* @see #getDelta()
*/
public Vector3D(double alpha, double delta) {
double cosDelta = Math.cos(delta);
this.x = Math.cos(alpha) * cosDelta;
this.y = Math.sin(alpha) * cosDelta;
this.z = Math.sin(delta);
}
/** Multiplicative constructor
* Build a vector from another one and a scale factor.
* The vector built will be a * u
* @param a scale factor
* @param u base (unscaled) vector
*/
public Vector3D(double a, Vector3D u) {
this.x = a * u.x;
this.y = a * u.y;
this.z = a * u.z;
}
/** Linear constructor
* Build a vector from two other ones and corresponding scale factors.
* The vector built will be a * u + b * v
* @param a first scale factor
* @param u first base (unscaled) vector
* @param b second scale factor
* @param v second base (unscaled) vector
*/
public Vector3D(double a, Vector3D u, double b, Vector3D v) {
this.x = a * u.x + b * v.x;
this.y = a * u.y + b * v.y;
this.z = a * u.z + b * v.z;
}
/** Linear constructor
* Build a vector from three other ones and corresponding scale factors.
* The vector built will be a * u + b * v + c * w
* @param a first scale factor
* @param u first base (unscaled) vector
* @param b second scale factor
* @param v second base (unscaled) vector
* @param c third scale factor
* @param w third base (unscaled) vector
*/
public Vector3D(double a, Vector3D u,
double b, Vector3D v,
double c, Vector3D w) {
this.x = a * u.x + b * v.x + c * w.x;
this.y = a * u.y + b * v.y + c * w.y;
this.z = a * u.z + b * v.z + c * w.z;
}
/** Copy constructor.
* Build a copy of a vector
* @param v vector to copy
*/
public Vector3D(Vector3D v) {
x = v.x;
y = v.y;
z = v.z;
}
/** Reset the instance.
* @param v vector to copy data from
*/
public void reset(Vector3D v) {
x = v.x;
y = v.y;
z = v.z;
}
/** Get the abscissa of the vector.
* @return abscissa of the vector
* @see #Vector3D(double, double, double)
* @see #setX(double)
*/
public double getX() {
return x;
}
/** Set the abscissa of the vector.
* @param x new abscissa for the vector
* @see #getX()
* @see #setCoordinates(double, double, double)
*/
public void setX(double x) {
this.x = x;
}
/** Get the ordinate of the vector.
* @return ordinate of the vector
* @see #Vector3D(double, double, double)
* @see #setY(double)
*/
public double getY() {
return y;
}
/** Set the ordinate of the vector.
* @param y new ordinate for the vector
* @see #getY()
* @see #setCoordinates(double, double, double)
*/
public void setY(double y) {
this.y = y;
}
/** Get the height of the vector.
* @return height of the vector
* @see #Vector3D(double, double, double)
* @see #setZ(double)
*/
public double getZ() {
return z;
}
/** Set the height of the vector.
* @param z new height for the vector
* @see #getZ()
* @see #setCoordinates(double, double, double)
*/
public void setZ(double z) {
this.z = z;
}
/** Set all coordinates of the vector.
* @param x new abscissa for the vector
* @param y new ordinate for the vector
* @param z new height for the vector
* @see #Vector3D(double, double, double)
*/
public void setCoordinates(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/** Get the norm for the vector.
* @return euclidian norm for the vector
*/
public double getNorm() {
return Math.sqrt (x * x + y * y + z * z);
}
/** Get the azimuth of the vector.
* @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
* @see #Vector3D(double, double)
*/
public double getAlpha() {
return Math.atan2(y, x);
}
/** Get the elevation of the vector.
* @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
* @see #Vector3D(double, double)
*/
public double getDelta() {
return Math.asin(z / getNorm());
}
/** Add a vector to the instance.
* Add a vector to the instance. The instance is changed.
* @param v vector to add
*/
public void addToSelf(Vector3D v) {
x += v.x;
y += v.y;
z += v.z;
}
/** Add a scaled vector to the instance.
* Add a scaled vector to the instance. The instance is changed.
* @param factor scale factor to apply to v before adding it
* @param v vector to add
*/
public void addToSelf(double factor, Vector3D v) {
x += factor * v.x;
y += factor * v.y;
z += factor * v.z;
}
/** Add two vectors.
* Add two vectors and return the sum as a new vector
* @param v1 first vector
* @param v2 second vector
* @return a new vector equal to v1 + v2
*/
public static Vector3D add(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
/** Subtract a vector from the instance.
* Subtract a vector from the instance. The instance is changed.
* @param v vector to subtract
*/
public void subtractFromSelf(Vector3D v) {
x -= v.x;
y -= v.y;
z -= v.z;
}
/** Subtract two vectors.
* Subtract two vectors and return the difference as a new vector
* @param v1 first vector
* @param v2 second vector
* @return a new vector equal to v1 - v2
*/
public static Vector3D subtract(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
/** Normalize the instance.
* Divide the instance by its norm in order to have a unit
* vector. The instance is changed.
* @exception ArithmeticException if the norm is null
*/
public void normalizeSelf() {
double s = getNorm();
if (s == 0) {
throw new ArithmeticException("null norm");
}
double invNorm = 1 / s;
x *= invNorm;
y *= invNorm;
z *= invNorm;
}
/** Get a vector orthogonal to the instance.
* <p>There are an infinite number of normalized vectors orthogonal
* to the instance. This method picks up one of them almost
* arbitrarily. It is useful when one needs to compute a reference
* frame with one of the axes in a predefined direction. The
* following example shows how to build a frame having the k axis
* aligned with the known vector u :
* <pre><code>
* Vector3D k = u;
* k.normalizeSelf();
* Vector3D i = k.orthogonal();
* Vector3D j = Vector3D.crossProduct(k, i);
* </code></pre></p>
* @return a new normalized vector orthogonal to the instance
* @exception ArithmeticException if the norm of the instance is null
*/
public Vector3D orthogonal() {
double threshold = 0.6 * getNorm();
if (threshold == 0) {
throw new ArithmeticException("null norm");
}
if ((x >= -threshold) && (x <= threshold)) {
double inverse = 1 / Math.sqrt(y * y + z * z);
return new Vector3D(0, inverse * z, -inverse * y);
} else if ((y >= -threshold) && (y <= threshold)) {
double inverse = 1 / Math.sqrt(x * x + z * z);
return new Vector3D(-inverse * z, 0, inverse * x);
} else {
double inverse = 1 / Math.sqrt(x * x + y * y);
return new Vector3D(inverse * y, -inverse * x, 0);
}
}
/** Compute the angular separation between two vectors.
* <p>This method computes the angular separation between two
* vectors using the dot product for well separated vectors and the
* cross product for almost aligned vectors. This allow to have a
* good accuracy in all cases, even for vectors very close to each
* other.</p>
* @param v1 first vector
* @param v2 second vector
* @exception ArithmeticException if either vector has a null norm
*/
public static double angle(Vector3D v1, Vector3D v2) {
double normProduct = v1.getNorm() * v2.getNorm();
if (normProduct == 0) {
throw new ArithmeticException("null norm");
}
double dot = dotProduct(v1, v2);
double threshold = normProduct * 0.9999;
if ((dot < -threshold) || (dot > threshold)) {
// the vectors are almost aligned, compute using the sine
Vector3D v3 = crossProduct(v1, v2);
if (dot >= 0) {
return Math.asin(v3.getNorm() / normProduct);
}
return Math.PI - Math.asin(v3.getNorm() / normProduct);
}
// the vectors are sufficiently separated to use the cosine
return Math.acos(dot / normProduct);
}
/** Revert the instance.
* Replace the instance u by -u
*/
public void negateSelf() {
x = -x;
y = -y;
z = -z;
}
/** Get the opposite of a vector.
* @param u vector to revert
* @return a new vector which is -u
*/
public static Vector3D negate(Vector3D u) {
return new Vector3D(-u.x, -u.y, -u.z);
}
/** Multiply the instance by a scalar
* Multiply the instance by a scalar. The instance is changed.
* @param a scalar by which the instance should be multiplied
*/
public void multiplySelf(double a) {
x *= a;
y *= a;
z *= a;
}
/** Multiply a vector by a scalar
* Multiply a vectors by a scalar and return the product as a new vector
* @param a scalar
* @param v vector
* @return a new vector equal to a * v
*/
public static Vector3D multiply(double a, Vector3D v) {
return new Vector3D(a * v.x, a * v.y, a * v.z);
}
/** Compute the dot-product of two vectors.
* @param v1 first vector
* @param v2 second vector
* @return the dot product v1.v2
*/
public static double dotProduct(Vector3D v1, Vector3D v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
/** Set the instance to the result of the cross-product of two vectors.
* @param v1 first vector (can be the instance)
* @param v2 second vector (can be the instance)
*/
public void setToCrossProduct(Vector3D v1, Vector3D v2) {
double newX = v1.y * v2.z - v1.z * v2.y;
double newY = v1.z * v2.x - v1.x * v2.z;
z = v1.x * v2.y - v1.y * v2.x;
x = newX;
y = newY;
}
/** Compute the cross-product of two vectors.
* @param v1 first vector
* @param v2 second vector
* @return the cross product v1 ^ v2 as a new Vector
*/
public static Vector3D crossProduct(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x);
}
public int getStateDimension() {
return 3;
}
public void mapStateFromArray(int start, double[] array) {
x = array[start];
y = array[start + 1];
z = array[start + 2];
}
public void mapStateToArray(int start, double[] array) {
array[start] = x;
array[start + 1] = y;
array[start + 2] = z;
}
/** Abscissa. */
protected double x;
/** Ordinate. */
protected double y;
/** Height. */
protected double z;
private static final long serialVersionUID = 4115635019045864211L;
}

View File

@ -0,0 +1,142 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class implements diagonal matrices of linear algebra.
* @version $Id: DiagonalMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class DiagonalMatrix
extends SquareMatrix
implements Serializable, Cloneable {
/** Simple constructor.
* This constructor builds a diagonal matrix of specified order, all
* elements on the diagonal being ones (so this is an identity matrix).
* @param order order of the matrix
*/
public DiagonalMatrix(int order) {
this(order, 1.0);
}
/** Simple constructor.
* This constructor builds a diagonal matrix of specified order and
* set all diagonal elements to the same value.
* @param order order of the matrix
* @param value value for the diagonal elements
*/
public DiagonalMatrix(int order, double value) {
super(order);
for (int index = 0; index < order * order; index += order + 1) {
data[index] = value;
}
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public DiagonalMatrix(int order, double[] data) {
super(order, data);
}
/** Copy constructor.
* @param d diagonal matrix to copy
*/
public DiagonalMatrix(DiagonalMatrix d) {
super(d);
}
public Matrix duplicate() {
return new DiagonalMatrix(this);
}
public void setElement(int i, int j, double value) {
if (i != j) {
throw new ArrayIndexOutOfBoundsException("cannot set elements"
+ " out of diagonal in a"
+ " diagonal matrix");
}
super.setElement(i, j, value);
}
public double getDeterminant(double epsilon) {
double determinant = data[0];
for (int index = columns + 1; index < columns * columns; index += columns + 1) {
determinant *= data[index];
}
return determinant;
}
public SquareMatrix getInverse(double epsilon)
throws SingularMatrixException {
DiagonalMatrix inv = new DiagonalMatrix (columns);
for (int index = 0; index < columns * columns; index += columns + 1) {
if (Math.abs(data[index]) < epsilon) {
throw new SingularMatrixException();
}
inv.data[index] = 1.0 / data[index];
}
return inv;
}
public Matrix solve(Matrix b, double epsilon)
throws SingularMatrixException {
Matrix result = b.duplicate();
for (int i = 0; i < columns; ++i) {
double diag = data[i * (columns + 1)];
if (Math.abs(diag) < epsilon) {
throw new SingularMatrixException();
}
double inv = 1.0 / diag;
NonNullRange range = result.getRangeForRow(i);
for (int index = i * b.columns + range.begin;
index < i * b.columns + range.end;
++index) {
result.data[index] = inv * b.data[index];
}
}
return result;
}
public NonNullRange getRangeForRow(int i) {
return new NonNullRange(i, i + 1);
}
public NonNullRange getRangeForColumn(int j) {
return new NonNullRange(j, j + 1);
}
private static final long serialVersionUID = -2965166085913895323L;
}

View File

@ -0,0 +1,122 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class represents matrices of the most general type.
* <p>This class is the basic implementation of matrices to use when
* nothing special is known about the structure of the matrix.</p>
* @version $Id: GeneralMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class GeneralMatrix
extends Matrix
implements Serializable {
/** Simple constructor.
* Build a matrix with null elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
*/
public GeneralMatrix(int rows, int columns) {
super(rows, columns);
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public GeneralMatrix(int rows, int columns, double[] data) {
super(rows, columns, data);
}
/** Copy constructor.
* @param m matrix to copy
*/
public GeneralMatrix(Matrix m) {
super(m);
}
public Matrix duplicate() {
return new GeneralMatrix(this);
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It does modify the instance.
* @param m matrix to add
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAdd(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot add a "
+ m.rows + 'x' + m.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
// addition loop
for (int index = 0; index < rows * columns; ++index) {
data[index] += m.data[index];
}
}
/** Substract a matrix from the instance.
* This method substracts a matrix from the instance. It does modify the instance.
* @param m matrix to substract
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfSub(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ m.rows + 'x' + m.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
// substraction loop
for (int index = 0; index < rows * columns; ++index) {
data[index] -= m.data[index];
}
}
protected NonNullRange getRangeForRow(int i) {
return new NonNullRange(0, columns);
}
protected NonNullRange getRangeForColumn(int j) {
return new NonNullRange(0, rows);
}
private static final long serialVersionUID = 4350328622456299819L;
}

View File

@ -0,0 +1,282 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class implements general square matrices of linear algebra.
* @version $Id: GeneralSquareMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class GeneralSquareMatrix
extends SquareMatrix
implements Serializable, Cloneable {
/** Simple constructor.
* This constructor builds a square matrix of specified order, all
* elements beeing zeros.
* @param order order of the matrix
*/
public GeneralSquareMatrix(int order) {
super(order);
permutations = null;
evenPermutations = true;
lower = null;
upper = null;
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public GeneralSquareMatrix(int order, double[] data) {
super(order, data);
permutations = null;
evenPermutations = true;
lower = null;
upper = null;
}
/** Copy constructor.
* @param s square matrix to copy
*/
public GeneralSquareMatrix(GeneralSquareMatrix s) {
super(s);
if (s.permutations != null) {
permutations = (int[]) s.permutations.clone();
evenPermutations = s.evenPermutations;
lower = new LowerTriangularMatrix(s.lower);
upper = new UpperTriangularMatrix(s.upper);
} else {
permutations = null;
evenPermutations = true;
lower = null;
upper = null;
}
}
public Matrix duplicate() {
return new GeneralSquareMatrix(this);
}
public void setElement(int i, int j, double value) {
super.setElement(i, j, value);
permutations = null;
evenPermutations = true;
lower = null;
upper = null;
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It does modify the instance.
* @param s square matrix to add
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAdd(SquareMatrix s) {
// validity check
if ((rows != s.rows) || (columns != s.columns)) {
throw new IllegalArgumentException("cannot add a "
+ s.rows + 'x' + s.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
// addition loop
for (int index = 0; index < rows * columns; ++index) {
data[index] += s.data[index];
}
}
/** Substract a matrix from the instance.
* This method substracts a matrix from the instance. It does modify the instance.
* @param s square matrix to substract
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfSub(SquareMatrix s) {
// validity check
if ((rows != s.rows) || (columns != s.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ s.rows + 'x' + s.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
// substraction loop
for (int index = 0; index < rows * columns; ++index) {
data[index] -= s.data[index];
}
}
public double getDeterminant(double epsilon) {
try {
if (permutations == null)
computeLUFactorization(epsilon);
double d = upper.getDeterminant(epsilon);
return evenPermutations ? d : -d;
} catch (SingularMatrixException e) {
return 0.0;
}
}
public Matrix solve(Matrix b, double epsilon)
throws SingularMatrixException {
// validity check
if (b.getRows() != rows) {
throw new IllegalArgumentException("dimension mismatch");
}
if (permutations == null) {
computeLUFactorization(epsilon);
}
// apply the permutations to the second member
double[] permData = new double[b.data.length];
int bCols = b.getColumns();
for (int i = 0; i < rows; ++i) {
NonNullRange range = b.getRangeForRow(permutations[i]);
for (int j = range.begin; j < range.end; ++j) {
permData[i * bCols + j] = b.data[permutations[i] * bCols + j];
}
}
Matrix permB = MatrixFactory.buildMatrix(b.getRows(), bCols, permData);
// solve the permuted system
return upper.solve(lower.solve(permB, epsilon), epsilon);
}
protected NonNullRange getRangeForRow(int i) {
return new NonNullRange(0, columns);
}
protected NonNullRange getRangeForColumn(int j) {
return new NonNullRange(0, rows);
}
private void computeLUFactorization(double epsilon)
throws SingularMatrixException {
// build a working copy of the matrix data
double[] work = new double[rows * columns];
for (int index = 0; index < work.length; ++index) {
work[index] = data[index];
}
// initialize the permutations table to identity
permutations = new int[rows];
for (int i = 0; i < rows; ++i) {
permutations[i] = i;
}
evenPermutations = true;
for (int k = 0; k < rows; ++k) {
// find the maximal element in the column
double maxElt = Math.abs(work[permutations[k] * columns + k]);
int jMax = k;
for (int i = k + 1; i < rows; ++i) {
double curElt = Math.abs(work[permutations[i] * columns + k]);
if (curElt > maxElt) {
maxElt = curElt;
jMax = i;
}
}
if (maxElt < epsilon) {
throw new SingularMatrixException();
}
if (k != jMax) {
// do the permutation to have a large enough diagonal element
int tmp = permutations[k];
permutations[k] = permutations[jMax];
permutations[jMax] = tmp;
evenPermutations = ! evenPermutations;
}
double inv = 1.0 / work[permutations[k] * columns + k];
// compute the contribution of the row to the triangular matrices
for (int i = k + 1; i < rows; ++i) {
double factor = inv * work[permutations[i] * columns + k];
// lower triangular matrix
work[permutations[i] * columns + k] = factor;
// upper triangular matrix
int index1 = permutations[i] * columns + k;
int index2 = permutations[k] * columns + k;
for (int j = k + 1; j < columns; ++j) {
work[++index1] -= factor * work[++index2];
}
}
}
// build the matrices
double[] lowerData = new double[rows * columns];
double[] upperData = new double[rows * columns];
int index = 0;
for (int i = 0; i < rows; ++i) {
int workIndex = permutations[i] * columns;
int j = 0;
// lower part
while (j++ < i) {
lowerData[index] = work[workIndex++];
upperData[index++] = 0.0;
}
// diagonal
lowerData[index] = 1.0;
upperData[index++] = work[workIndex++];
// upper part
while (j++ < columns) {
lowerData[index] = 0.0;
upperData[index++] = work[workIndex++];
}
}
// release the memory as soon as possible
work = null;
lower = new LowerTriangularMatrix(rows, lowerData);
upper = new UpperTriangularMatrix(rows, upperData);
}
private int[] permutations;
private boolean evenPermutations;
private LowerTriangularMatrix lower;
private UpperTriangularMatrix upper;
private static final long serialVersionUID = -506293526695298279L;
}

View File

@ -0,0 +1,219 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class implements lower triangular matrices of linear algebra.
* @version $Id: LowerTriangularMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class LowerTriangularMatrix
extends SquareMatrix
implements Serializable, Cloneable {
/** Simple constructor.
* This constructor builds a lower triangular matrix of specified order, all
* elements being zeros.
* @param order order of the matrix
*/
public LowerTriangularMatrix(int order) {
super(order);
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public LowerTriangularMatrix(int order, double[] data) {
super(order, data);
}
/** Copy constructor.
* @param l lower triangular matrix to copy
*/
public LowerTriangularMatrix(LowerTriangularMatrix l) {
super(l);
}
public Matrix duplicate() {
return new LowerTriangularMatrix(this);
}
public void setElement(int i, int j, double value) {
if (i < j) {
throw new ArrayIndexOutOfBoundsException("cannot set elements"
+ " above diagonal of a"
+ " lower triangular matrix");
}
super.setElement(i, j, value);
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It does modify the instance.
* @param l lower triangular matrix to add
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAdd(LowerTriangularMatrix l) {
// validity check
if ((rows != l.rows) || (columns != l.columns)) {
throw new IllegalArgumentException("cannot add a "
+ l.rows + 'x' + l.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
// addition loop
for (int i = 0; i < rows; ++i) {
for (int index = i * columns; index < i * (columns + 1) + 1; ++index) {
data[index] += l.data[index];
}
}
}
/** Substract a matrix from the instance.
* This method substract a matrix from the instance. It does modify the instance.
* @param l lower triangular matrix to substract
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfSub(LowerTriangularMatrix l) {
// validity check
if ((rows != l.rows) || (columns != l.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ l.rows + 'x' + l.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
// substraction loop
for (int i = 0; i < rows; ++i) {
for (int index = i * columns; index < i * (columns + 1) + 1; ++index) {
data[index] -= l.data[index];
}
}
}
public double getDeterminant(double epsilon) {
double determinant = data[0];
for (int index = columns + 1; index < columns * columns; index += columns + 1) {
determinant *= data[index];
}
return determinant;
}
public Matrix solve(Matrix b, double epsilon)
throws SingularMatrixException {
// validity check
if (b.getRows () != rows) {
throw new IllegalArgumentException("dimension mismatch");
}
// prepare the data storage
int bRows = b.getRows();
int bCols = b.getColumns();
double[] resultData = new double[bRows * bCols];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
int minJ = columns;
int maxJ = 0;
// solve the linear system
for (int i = 0; i < rows; ++i) {
double diag = data[i * (columns + 1)];
if (Math.abs(diag) < epsilon) {
throw new SingularMatrixException();
}
double inv = 1.0 / diag;
NonNullRange range = b.getRangeForRow(i);
minJ = Math.min(minJ, range.begin);
maxJ = Math.max(maxJ, range.end);
int j = 0;
while (j < minJ) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
// compute the possibly non null elements
int bIndex = i * bCols + minJ;
while (j < maxJ) {
// compute the current element
int index1 = i * columns;
int index2 = j;
double value = b.data[bIndex];
while (index1 < i * (columns + 1)) {
value -= data[index1] * resultData[index2];
++index1;
index2 += bCols;
}
value *= inv;
resultData[resultIndex] = value;
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
++bIndex;
++resultIndex;
++j;
}
while (j < bCols) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(bRows, bCols, resultData,
lowerElements, upperElements);
}
public NonNullRange getRangeForRow(int i) {
return new NonNullRange(0, i + 1);
}
public NonNullRange getRangeForColumn(int j) {
return new NonNullRange(j, rows);
}
private static final long serialVersionUID = 3592505328858227281L;
}

View File

@ -0,0 +1,484 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class factor all services common to matrices.
* <p>This class is the base class of all matrix implementations, it
* is also the base class of the {@link SquareMatrix} class which adds
* methods specific to square matrices.</p>
* <p>This class both handles the storage of matrix elements and
* implements the classical operations on matrices (addition,
* substraction, multiplication, transposition). It relies on two
* protected methods ({@link #getRangeForRow} and {@link
* #getRangeForColumn}) to get tight loop bounds for matrices with
* known structures full of zeros. These methods should be
* implemented by derived classes to provide information about their
* specific shape to the general algorithms implemented by this
* abstract class.</p>
* @version $Id: Matrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class Matrix
implements Serializable {
/** Simple constructor.
* Build a matrix with null elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
*/
protected Matrix(int rows, int columns) {
// sanity check
if (rows <= 0 || columns <= 0) {
throw new IllegalArgumentException("cannot build a matrix"
+ " with negative or null dimension");
}
this.rows = rows;
this.columns = columns;
data = new double[rows * columns];
for (int i = 0; i < data.length; ++i) {
data[i] = 0.0;
}
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param rows number of rows of the matrix
* @param columns number of columns of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public Matrix(int rows, int columns, double[] data) {
// sanity check
if (rows <= 0 || columns <= 0) {
throw new IllegalArgumentException("cannot build a matrix"
+ " with negative or null dimension");
}
this.rows = rows;
this.columns = columns;
this.data = data;
}
/** Copy constructor.
* @param m matrix to copy
*/
protected Matrix(Matrix m) {
rows = m.rows;
columns = m.columns;
data = new double[rows * columns];
System.arraycopy(m.data, 0, data, 0, m.data.length);
}
/** Polymorphic copy operator.
* This method build a new object of the same type of the
* instance. It is somewhat similar to the {@link Object#clone}
* method, except that it has public access, it doesn't throw any
* specific exception and it returns a Matrix.
*@see Object#clone
*/
public abstract Matrix duplicate();
/** Get the number of rows of the matrix.
* @return number of rows
* @see #getColumns
*/
public int getRows() {
return rows;
}
/** Get the number of columns of the matrix.
* @return number of columns
* @see #getRows
*/
public int getColumns() {
return columns;
}
/** Get a matrix element.
* @param i row index, from 0 to rows - 1
* @param j column index, from 0 to cols - 1
* @return value of the element
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #setElement
*/
public double getElement(int i, int j) {
if (i < 0 || i >= rows || j < 0 || j >= columns) {
throw new IllegalArgumentException("cannot get element ("
+ i + ", " + j + ") from a "
+ rows + 'x' + columns
+ " matrix");
}
return data[i * columns + j];
}
/** Set a matrix element.
* @param i row index, from 0 to rows - 1
* @param j column index, from 0 to cols - 1
* @param value value of the element
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #getElement
*/
public void setElement(int i, int j, double value) {
if (i < 0 || i >= rows || j < 0 || j >= columns) {
throw new IllegalArgumentException("cannot set element ("
+ i + ", " + j + ") in a "
+ rows + 'x' + columns
+ " matrix");
}
data[i * columns + j] = value;
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It returns a new
* matrix and does not modify the instance.
* @param m matrix to add
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix add(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot add a "
+ m.rows + 'x' + m.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
double[] resultData = new double[rows * columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
// external loop through the rows
for (int i = 0; i < rows; ++i) {
// compute the indices of the internal loop
NonNullRange r = NonNullRange.reunion(getRangeForRow(i),
m.getRangeForRow(i));
// assign the zeros before the non null range
int j = 0;
while (j < r.begin) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
// compute the possibly non null elements
while (j < r.end) {
// compute the current element
resultData[resultIndex] = data[resultIndex] + m.data[resultIndex];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < columns) {
resultData[resultIndex++] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(rows, columns, resultData,
lowerElements, upperElements);
}
/** Substract a matrix from the instance.
* This method substracts a matrix from the instance. It returns a new
* matrix and does not modify the instance.
* @param m matrix to substract
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix sub(Matrix m) {
// validity check
if ((rows != m.rows) || (columns != m.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ m.rows + 'x' + m.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
double[] resultData = new double[rows * columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
// external loop through the rows
for (int i = 0; i < rows; ++i) {
// compute the indices of the internal loop
NonNullRange r = NonNullRange.reunion(getRangeForRow(i),
m.getRangeForRow(i));
// assign the zeros before the non null range
int j = 0;
while (j < r.begin) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
// compute the possibly non null elements
while (j < r.end) {
// compute the current element
resultData[resultIndex] = data[resultIndex] - m.data[resultIndex];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < columns) {
resultData[resultIndex++] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(rows, columns, resultData,
lowerElements, upperElements);
}
/** Multiply the instance by a matrix.
* This method multiplies the instance by a matrix. It returns a new
* matrix and does not modify the instance.
* @param m matrix by which to multiply
* @return a new matrix containing the result
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public Matrix mul(Matrix m) {
// validity check
if (columns != m.rows) {
throw new IllegalArgumentException("cannot multiply a "
+ rows + 'x' + columns
+ " matrix by a "
+ m.rows + 'x' + m.columns
+ " matrix");
}
double[] resultData = new double[rows * m.columns];
int resultIndex = 0;
int lowerElements = 0;
int upperElements = 0;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < m.columns; ++j) {
double value = 0.0;
// compute the tighter possible indices of the internal loop
NonNullRange r = NonNullRange.intersection(getRangeForRow(i),
m.getRangeForColumn(j));
if (r.begin < r.end) {
int k = r.begin;
int idx = i * columns + k;
int midx = k * m.columns + j;
while (k++ < r.end) {
value += data[idx++] * m.data[midx];
midx += m.columns;
}
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
}
// store the element value
resultData[resultIndex++] = value;
}
}
return MatrixFactory.buildMatrix(rows, m.columns, resultData,
lowerElements, upperElements);
}
/** Multiply the instance by a scalar.
* This method multiplies the instance by a scalar. It returns a new
* matrix and does not modify the instance.
* @param a scalar by which to multiply
* @return a new matrix containing the result
* @see #selfMul(double)
*/
public Matrix mul(double a) {
Matrix copy = duplicate();
copy.selfMul(a);
return copy;
}
/** Multiply the instance by a scalar.
* This method multiplies the instance by a scalar.
* It does modify the instance.
* @param a scalar by which to multiply
* @see #mul(double)
*/
public void selfMul(double a) {
for (int i = 0; i < rows; ++i) {
NonNullRange r = getRangeForRow(i);
for (int j = r.begin, index = i * columns + r.begin; j < r.end; ++j) {
data[index++] *= a;
}
}
}
/** Compute the transpose of the instance.
* This method transposes the instance. It returns a new
* matrix and does not modify the instance.
* @return a new matrix containing the result
*/
public Matrix getTranspose() {
double[] resultData = new double[columns * rows];
int resultIndex = 0;
int upperElements = 0;
int lowerElements = 0;
for (int i = 0; i < columns; ++i) {
// compute the indices of the internal loop
NonNullRange range = getRangeForColumn(i);
int j = 0;
int index = i;
// assign the zeros before the non null range
while (j < range.begin) {
resultData[resultIndex++] = 0.0;
index += columns;
++j;
}
// compute the possibly non null elements
while (j < range.end) {
resultData[resultIndex] = data[index];
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
index += columns;
++resultIndex;
++j;
}
// assign the zeros after the non null range
while (j < rows) {
resultData[resultIndex] = 0.0;
++resultIndex;
++j;
}
}
return MatrixFactory.buildMatrix(columns, rows, resultData,
lowerElements, upperElements);
}
/** Set a range to the non null part covered by a row.
* @param i index of the row
* @return range of non nul elements in the specified row
* @see #getRangeForColumn
*/
protected abstract NonNullRange getRangeForRow(int i);
/** Set a range to the non null part covered by a column.
* @param j index of the column
* @return range of non nul elements in the specified column
* @see #getRangeForRow
*/
protected abstract NonNullRange getRangeForColumn(int j);
public String toString() {
String separator = System.getProperty("line.separator");
StringBuffer buf = new StringBuffer();
for (int index = 0; index < rows * columns; ++index) {
if (index > 0) {
if (index % columns == 0) {
buf.append(separator);
} else {
buf.append(' ');
}
}
buf.append(Double.toString(data[index]));
}
return buf.toString();
}
/** number of rows of the matrix. */
protected final int rows;
/** number of columns of the matrix. */
protected final int columns;
/** array of the matrix elements.
* the elements are stored in a one dimensional array, row after row
*/
protected final double[] data;
}

View File

@ -0,0 +1,86 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
/** This class is a factory for the linear algebra package.
* <p>This class is devoted to building the right type of matrix
* according to the structure of the non null elements.</p>
* <p>This is a utility class, no instance of this class should be
* built, so the constructor is explicitly made private.</p>
* @version $Id: MatrixFactory.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class MatrixFactory {
/** Simple constructor.
* Since the class is a utility class with only static methods, the
* constructor is made private to prevent creating instances of this
* class.
*/
private MatrixFactory() {
}
/** Build a matrix of the right subtype.
* Build the right subtype of matrix according to the structure of
* the non null elements of the instance. Note that the information
* provided does not allow to build instances of the {@link
* SymetricalMatrix} class. When the data corresponding to a
* symetrical matrix is given, this method can only build an
* instance of the {@link GeneralSquareMatrix} class.
* @param rows number of row of the matrix
* @param columns number of columns of the matrix
* @param data table of the matrix elements (stored row after row)
* @param lowerElements number of non null elements in the lower triangle
* @param upperElements number of non null elements in the upper triangle
* @return a matrix containing the instance
*/
public static Matrix buildMatrix(int rows, int columns, double[] data,
int lowerElements, int upperElements) {
if (rows == columns) {
if (lowerElements == 0 && upperElements == 0) {
return new DiagonalMatrix(rows, data);
} else if (lowerElements == 0) {
return new UpperTriangularMatrix(rows, data);
} else if (upperElements == 0) {
return new LowerTriangularMatrix(rows, data);
} else {
return new GeneralSquareMatrix(rows, data);
}
}
return new GeneralMatrix(rows, columns, data);
}
/** Build a matrix of the right subtype.
* Build the right subtype of matrix according to the dimensions.
* @param rows number of row of the matrix
* @param columns number of columns of the matrix
* @param data table of the matrix elements (stored row after row)
* @return a matrix containing the instance
*/
public static Matrix buildMatrix(int rows, int columns, double[] data) {
if (rows == columns) {
return new GeneralSquareMatrix(rows, data);
}
return new GeneralMatrix(rows, columns, data);
}
}

View File

@ -0,0 +1,103 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class represents range of non null elements for rows or
* columns of matrices.
* <p>This class is used to reduce the computation loops by avoiding
* using elements that are known to be zeros. For full matrices, the
* range simply spans from 0 to the order of the matrix. For lower and
* upper triangular matrices, its width will depend on the index of
* the row or column considered. For diagonal matrices, the range is
* reduced to one index.</p>
* <p>The indices provided by the class correspond to the elements
* that are non-null only according to the <emph>structure</emph> of
* the matrix. The real value of the element is not
* considered. Consider for example the following lower triangular
* matrix :</p>
* <pre>
* 1 0 0 0
* 2 8 0 0
* 0 5 3 0
* 3 2 4 4
* </pre>
* <p>The third rows begins with zero, but this is not a consequence
* of the lower triangular structure, it is only a
* coincidence. Therefore, the range (in row/columns count)
* corresponding to third row will span from 0 to 2, not from 1 to 2.</p>
* @version $Id: NonNullRange.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class NonNullRange
implements Serializable, Cloneable {
/** Index in row/column count of the first non-null element. */
public final int begin;
/** Index in row/column count after the last non-null element. */
public final int end;
/** Simple constructor.
* @param begin index in row/column count of the first non-null element
* @param end index in row/column count after the last non-null element
*/
public NonNullRange(int begin, int end)
{
this.begin = begin;
this.end = end;
}
/** Copy constructor.
* @param range range to copy.
*/
public NonNullRange(NonNullRange range) {
begin = range.begin;
end = range.end;
}
/** Build the intersection of two ranges.
* @param first first range to consider
* @param second second range to consider
*/
public static NonNullRange intersection(NonNullRange first,
NonNullRange second) {
return new NonNullRange(Math.max(first.begin, second.begin),
Math.min(first.end, second.end));
}
/** Build the reunion of two ranges.
* @param first first range to consider
* @param second second range to consider
*/
public static NonNullRange reunion(NonNullRange first,
NonNullRange second) {
return new NonNullRange(Math.min(first.begin, second.begin),
Math.max(first.end, second.end));
}
private static final long serialVersionUID = 8175301560126132666L;
}

View File

@ -0,0 +1,41 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import org.spaceroots.mantissa.MantissaException;
/** This class represent exceptions thrown by some matrix operations.
* @version $Id: SingularMatrixException.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class SingularMatrixException
extends MantissaException {
/** Simple constructor.
* Build an exception with a default message
*/
public SingularMatrixException() {
super("singular matrix");
}
private static final long serialVersionUID = 7531357987468317564L;
}

View File

@ -0,0 +1,102 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class factor all services common to square matrices of linear algebra.
* <p>This class is the base class of all square matrix
* implementations. It extends the {@link Matrix} class with methods
* specific to square matrices.</p>
* @version $Id: SquareMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class SquareMatrix
extends Matrix
implements Serializable, Cloneable {
/** Simple constructor.
* Build a matrix with null elements.
* @param order order of the matrix
*/
protected SquareMatrix(int order) {
super(order, order);
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
protected SquareMatrix(int order, double[] data) {
super(order, order, data);
}
/** Copy constructor.
* @param m matrix to copy
*/
protected SquareMatrix(SquareMatrix m) {
super(m);
}
/** Get the determinant of the matrix.
* @param epsilon threshold on matrix elements below which the
* matrix is considered singular (this is used by the derived
* classes that use a factorization to compute the determinant)
* @return the determinant of the matrix
*/
public abstract double getDeterminant(double epsilon);
/** Invert the instance.
* @param epsilon threshold on matrix elements below which the
* matrix is considered singular
* @return the inverse matrix of the instance
* @exception SingularMatrixException if the matrix is singular
*/
public SquareMatrix getInverse(double epsilon)
throws SingularMatrixException {
return solve(new DiagonalMatrix (columns), epsilon);
}
/** Solve the <tt>A.X = B</tt> equation.
* @param b second term of the equation
* @param epsilon threshold on matrix elements below which the
* matrix is considered singular
* @return a matrix X such that <tt>A.X = B</tt>, where A is the instance
* @exception SingularMatrixException if the matrix is singular
*/
public abstract Matrix solve(Matrix b, double epsilon)
throws SingularMatrixException;
/** Solve the <tt>A.X = B</tt> equation.
* @param b second term of the equation
* @param epsilon threshold on matrix elements below which the
* matrix is considered singular
* @return a matrix X such that <tt>A.X = B</tt>, where A is the instance
* @exception SingularMatrixException if the matrix is singular
*/
public SquareMatrix solve(SquareMatrix b, double epsilon)
throws SingularMatrixException {
return (SquareMatrix) solve((Matrix) b, epsilon);
}
}

View File

@ -0,0 +1,226 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class implements symetrical matrices of linear algebra.
* @version $Id: SymetricalMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class SymetricalMatrix
extends GeneralSquareMatrix
implements Serializable, Cloneable {
/** Simple constructor.
* This constructor builds a symetrical matrix of specified order, all
* elements beeing zeros.
* @param order order of the matrix
*/
public SymetricalMatrix(int order) {
super(order);
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public SymetricalMatrix(int order, double[] data) {
super(order, data);
}
/** Copy constructor.
* @param s square matrix to copy
*/
public SymetricalMatrix(SymetricalMatrix s) {
super(s);
}
/** Build the symetrical matrix resulting from the product w.A.At.
* @param w multiplicative factor (weight)
* @param a base vector used to compute the symetrical contribution
*/
public SymetricalMatrix(double w, double[] a) {
super(a.length, new double[a.length * a.length]);
for (int i = 0; i < a.length; ++i) {
int indexU = i * (columns + 1);
int indexL = indexU;
double factor = w * a[i];
data[indexU] = factor * a[i];
for (int j = i + 1; j < columns; ++j) {
++indexU;
indexL += columns;
data[indexU] = factor * a[j];
data[indexL] = data[indexU];
}
}
}
public Matrix duplicate() {
return new SymetricalMatrix(this);
}
/** Set a matrix element.
* On symetrical matrices, setting separately elements outside of
* the diagonal is forbidden, so this method throws an
* ArrayIndexOutOfBoundsException in this case. The {@link
* #setElementAndSymetricalElement} can be used to set both elements
* simultaneously.
* @param i row index, from 0 to rows - 1
* @param j column index, from 0 to cols - 1
* @param value value of the element
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #setElementAndSymetricalElement
* @see Matrix#getElement
*/
public void setElement(int i, int j, double value) {
if (i != j) {
throw new ArrayIndexOutOfBoundsException("cannot separately set"
+ " elements out of diagonal"
+ " in a symetrical matrix");
}
super.setElement(i, j, value);
}
/** Set both a matrix element and its symetrical element.
* @param i row index of first element (column index of second
* element), from 0 to order - 1
* @param j column index of first element (row index of second
* element), from 0 to order - 1
* @param value value of the elements
* @exception ArrayIndexOutOfBoundsException if the indices are wrong
* @see #setElement
* @see Matrix#getElement
*/
public void setElementAndSymetricalElement(int i, int j, double value) {
super.setElement(i, j, value);
if (i != j) {
super.setElement(j, i, value);
}
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It does modify the instance.
* @param s symetrical matrix to add
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAdd(SymetricalMatrix s) {
// validity check
if ((rows != s.rows) || (columns != s.columns)) {
throw new IllegalArgumentException("cannot add a "
+ s.rows + 'x' + s.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
// addition loop
for (int i = 0; i < rows; ++i) {
int indexU = i * (columns + 1);
int indexL = indexU;
data[indexU] += s.data[indexU];
for (int j = i + 1; j < columns; ++j) {
++indexU;
indexL += columns;
data[indexU] += s.data[indexU];
data[indexL] = data[indexU];
}
}
}
/** Substract a matrix from the instance.
* This method substracts a matrix from the instance. It does modify the instance.
* @param s symetrical matrix to substract
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfSub(SymetricalMatrix s) {
// validity check
if ((rows != s.rows) || (columns != s.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ s.rows + 'x' + s.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
// substraction loop
for (int i = 0; i < rows; ++i) {
int indexU = i * (columns + 1);
int indexL = indexU;
data[indexU] -= s.data[indexU];
for (int j = i + 1; j < columns; ++j) {
++indexU;
indexL += columns;
data[indexU] -= s.data[indexU];
data[indexL] = data[indexU];
}
}
}
/** Add the symetrical matrix resulting from the product w.A.At to the instance.
* This method can be used to build progressively the matrices of
* least square problems. The instance is modified.
* @param w multiplicative factor (weight)
* @param a base vector used to compute the symetrical contribution
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAddWAAt(double w, double[] a) {
if (rows != a.length) {
throw new IllegalArgumentException("cannot add a "
+ a.length + 'x' + a.length
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
for (int i = 0; i < rows; ++i) {
int indexU = i * (columns + 1);
int indexL = indexU;
double factor = w * a[i];
data[indexU] += factor * a[i];
for (int j = i + 1; j < columns; ++j) {
++indexU;
indexL += columns;
data[indexU] += factor * a[j];
data[indexL] = data[indexU];
}
}
}
private static final long serialVersionUID = -2083829252075519221L;
}

View File

@ -0,0 +1,219 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.linalg;
import java.io.Serializable;
/** This class implements upper triangular matrices of linear algebra.
* @version $Id: UpperTriangularMatrix.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class UpperTriangularMatrix
extends SquareMatrix
implements Serializable, Cloneable {
/** Simple constructor.
* This constructor builds a upper triangular matrix of specified order, all
* elements being zeros.
* @param order order of the matrix
*/
public UpperTriangularMatrix(int order) {
super(order);
}
/** Simple constructor.
* Build a matrix with specified elements.
* @param order order of the matrix
* @param data table of the matrix elements (stored row after row)
*/
public UpperTriangularMatrix(int order, double[] data) {
super(order, data);
}
/** Copy constructor.
* @param u upper triangular matrix to copy
*/
public UpperTriangularMatrix(UpperTriangularMatrix u) {
super(u);
}
public Matrix duplicate() {
return new UpperTriangularMatrix(this);
}
public void setElement(int i, int j, double value) {
if (i > j) {
throw new ArrayIndexOutOfBoundsException("cannot set elements"
+ " below diagonal of a"
+ " upper triangular matrix");
}
super.setElement(i, j, value);
}
/** Add a matrix to the instance.
* This method adds a matrix to the instance. It does modify the instance.
* @param u upper triangular matrix to add
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfAdd(UpperTriangularMatrix u) {
// validity check
if ((rows != u.rows) || (columns != u.columns)) {
throw new IllegalArgumentException("cannot add a "
+ u.rows + 'x' + u.columns
+ " matrix to a "
+ rows + 'x' + columns
+ " matrix");
}
// addition loop
for (int i = 0; i < rows; ++i) {
for (int index = i * (columns + 1); index < (i + 1) * columns; ++index) {
data[index] += u.data[index];
}
}
}
/** Substract a matrix from the instance.
* This method substract a matrix from the instance. It does modify the instance.
* @param u upper triangular matrix to substract
* @exception IllegalArgumentException if there is a dimension mismatch
*/
public void selfSub(UpperTriangularMatrix u) {
// validity check
if ((rows != u.rows) || (columns != u.columns)) {
throw new IllegalArgumentException("cannot substract a "
+ u.rows + 'x' + u.columns
+ " matrix from a "
+ rows + 'x' + columns
+ " matrix");
}
// substraction loop
for (int i = 0; i < rows; ++i) {
for (int index = i * (columns + 1); index < (i + 1) * columns; ++index) {
data[index] -= u.data[index];
}
}
}
public double getDeterminant(double epsilon) {
double determinant = data[0];
for (int index = columns + 1; index < columns * columns; index += columns + 1) {
determinant *= data[index];
}
return determinant;
}
public Matrix solve(Matrix b, double epsilon)
throws SingularMatrixException {
// validity check
if (b.getRows() != rows) {
throw new IllegalArgumentException("dimension mismatch");
}
// prepare the data storage
int bRows = b.getRows();
int bCols = b.getColumns();
double[] resultData = new double[bRows * bCols];
int resultIndex = bRows * bCols - 1;
int lowerElements = 0;
int upperElements = 0;
int minJ = columns;
int maxJ = 0;
// solve the linear system
for (int i = rows - 1; i >= 0; --i) {
double diag = data[i * (columns + 1)];
if (Math.abs(diag) < epsilon) {
throw new SingularMatrixException();
}
double inv = 1.0 / diag;
NonNullRange range = b.getRangeForRow(i);
minJ = Math.min(minJ, range.begin);
maxJ = Math.max(maxJ, range.end);
int j = bCols - 1;
while (j >= maxJ) {
resultData[resultIndex] = 0.0;
--resultIndex;
--j;
}
// compute the possibly non null elements
int bIndex = i * bCols + maxJ - 1;
while (j >= minJ) {
// compute the current element
int index1 = (i + 1) * columns - 1;
int index2 = (bRows - 1) * bCols + j;
double value = b.data[bIndex];
while (index1 >= i * (columns + 1)) {
value -= data[index1] * resultData[index2];
--index1;
index2 -= bCols;
}
value *= inv;
resultData[resultIndex] = value;
// count the affected upper and lower elements
// (in order to deduce the shape of the resulting matrix)
if (j < i) {
++lowerElements;
} else if (i < j) {
++upperElements;
}
--bIndex;
--resultIndex;
--j;
}
while (j >= 0) {
resultData[resultIndex] = 0.0;
--resultIndex;
--j;
}
}
return MatrixFactory.buildMatrix(bRows, bCols, resultData,
lowerElements, upperElements);
}
public NonNullRange getRangeForRow(int i) {
return new NonNullRange (i, columns);
}
public NonNullRange getRangeForColumn(int j) {
return new NonNullRange (0, j + 1);
}
private static final long serialVersionUID = -197266611942032237L;
}

View File

@ -0,0 +1,68 @@
<?xml version = "1.0" encoding = "ISO-8859-1" ?>
<!DOCTYPE argo SYSTEM "argo.dtd" >
<argo>
<documentation>
<authorname></authorname>
<version></version>
<description>
</description>
</documentation>
<searchpath href="PROJECT_DIR" />
<member
type="pgml"
name="linalg_classdiagram1.pgml"
/>
<member
type="xmi"
name="linalg.xmi"
/>
<historyfile name="" />
<stats>
<stat name="clicksInToDoPane"
value="0" />
<stat name="dblClicksInToDoPane"
value="0" />
<stat name="longestToDoList"
value="48" />
<stat name="longestAdd"
value="0" />
<stat name="longestHot"
value="0" />
<stat name="numCriticsFired"
value="17738" />
<stat name="numNotValid"
value="4" />
<stat name="numCriticsApplied"
value="0" />
<stat name="toDoPerspectivesChanged"
value="1" />
<stat name="navPerspectivesChanged"
value="5" />
<stat name="clicksInNavPane"
value="3" />
<stat name="numFinds"
value="1" />
<stat name="numJumpToRelated"
value="0" />
<stat name="numDecisionModel"
value="0" />
<stat name="numGoalsModel"
value="0" />
<stat name="numCriticBrowser"
value="0" />
<stat name="numNavConfig"
value="0" />
<stat name="numHushes"
value="0" />
<stat name="numChecks"
value="0" />
<stat name="Num_Button_Clicks"
value="0" />
<stat name="Drags_To_New"
value="0" />
<stat name="Drags_To_Existing"
value="0" />
</stats>
</argo>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
<html>
<body>
This package provides classes to perform linear algebra computation.
<p>It is by no means a complete linear algebra system, it is
sufficient to solve least squares problems and linear equations
systems, but it lacks lots of functionalities and matrices types (for
example sparse matrices are not supported).</p>
<p>The class diagram for the public classes of this package is
displayed below. The user will mainly use directly the implementation
classes rather than the abstract {@link
org.spaceroots.mantissa.linalg.Matrix} class. The shape of the matrices
used is often known in the algorithms so there is little need to hide
this shape behind the general class.</p>
<img src="doc-files/org_spaceroots_mantissa_linalg_classes.png" />
@author L. Maisonobe
</body>
</html>

View File

@ -0,0 +1,433 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
/** This abstract class represents an interpolator over the last step
* during an ODE integration.
*
* <p>The various ODE integrators provide objects extending this class
* to the step handlers. The handlers can use these objects to
* retrieve the state vector at intermediate times between the
* previous and the current grid points (dense output).</p>
*
* @see FirstOrderIntegrator
* @see SecondOrderIntegrator
* @see StepHandler
*
* @version $Id: AbstractStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
public abstract class AbstractStepInterpolator
implements StepInterpolator, Cloneable {
/** previous time */
protected double previousTime;
/** current time */
protected double currentTime;
/** current time step */
protected double h;
/** current state */
protected double[] currentState;
/** interpolated time */
protected double interpolatedTime;
/** interpolated state */
protected double[] interpolatedState;
/** indicate if the step has been finalized or not. */
private boolean finalized;
/** integration direction. */
private boolean forward;
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link #reinitialize} method should be called before using the
* instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. As an example, the {@link
* RungeKuttaFehlbergIntegrator} uses the prototyping design pattern
* to create the step interpolators by cloning an uninitialized
* model and latter initializing the copy.
*/
protected AbstractStepInterpolator() {
previousTime = Double.NaN;
currentTime = Double.NaN;
h = Double.NaN;
interpolatedTime = Double.NaN;
currentState = null;
interpolatedState = null;
finalized = false;
this.forward = true;
}
/** Simple constructor.
* @param y reference to the integrator array holding the state at
* the end of the step
* @param forward integration direction indicator
*/
protected AbstractStepInterpolator(double[] y, boolean forward) {
previousTime = Double.NaN;
currentTime = Double.NaN;
h = Double.NaN;
interpolatedTime = Double.NaN;
currentState = y;
interpolatedState = new double[y.length];
finalized = false;
this.forward = forward;
}
/** Copy constructor.
* <p>The copied interpolator should have been finalized before the
* copy, otherwise the copy will not be able to perform correctly
* any derivative computation and will throw a {@link
* NullPointerException} later. Since we don't want this constructor
* to throw the exceptions finalization may involve and since we
* don't want this method to modify the state of the copied
* interpolator, finalization is <strong>not</strong> done
* automatically, it remains under user control.</p>
* <p>The copy is a deep copy: its arrays are separated from the
* original arrays of the instance.</p>
* @param interpolator interpolator to copy from.
*/
protected AbstractStepInterpolator(AbstractStepInterpolator interpolator) {
previousTime = interpolator.previousTime;
currentTime = interpolator.currentTime;
h = interpolator.h;
interpolatedTime = interpolator.interpolatedTime;
if (interpolator.currentState != null) {
int dimension = interpolator.currentState.length;
currentState = new double[dimension];
System.arraycopy(interpolator.currentState, 0, currentState, 0,
dimension);
interpolatedState = new double[dimension];
System.arraycopy(interpolator.interpolatedState, 0, interpolatedState, 0,
dimension);
} else {
currentState = null;
interpolatedState = null;
}
finalized = interpolator.finalized;
forward = interpolator.forward;
}
/** Reinitialize the instance
* @param y reference to the integrator array holding the state at
* the end of the step
* @param forward integration direction indicator
*/
protected void reinitialize(double[] y, boolean forward) {
previousTime = Double.NaN;
currentTime = Double.NaN;
h = Double.NaN;
interpolatedTime = Double.NaN;
currentState = y;
interpolatedState = new double[y.length];
finalized = false;
this.forward = forward;
}
/** Copy the instance.
* <p>The copied interpolator should have been finalized before the
* copy, otherwise the copy will not be able to perform correctly any
* interpolation and will throw a {@link NullPointerException}
* later. Since we don't want this constructor to throw the
* exceptions finalization may involve and since we don't want this
* method to modify the state of the copied interpolator,
* finalization is <strong>not</strong> done automatically, it
* remains under user control.</p>
* <p>The copy is a deep copy: its arrays are separated from the
* original arrays of the instance.</p>
* <p>This method has been redeclared as public instead of protected.</p>
* @return a copy of the instance.
*/
public abstract Object clone();
/** Shift one step forward.
* Copy the current time into the previous time, hence preparing the
* interpolator for future calls to {@link #storeTime storeTime}
*/
public void shift() {
previousTime = currentTime;
}
/** Store the current step time.
* @param t current time
*/
public void storeTime(double t) {
currentTime = t;
h = currentTime - previousTime;
interpolatedTime = t;
System.arraycopy(currentState, 0, interpolatedState, 0,
currentState.length);
// the step is not finalized anymore
finalized = false;
}
/**
* Get the previous grid point time.
* @return previous grid point time
*/
public double getPreviousTime() {
return previousTime;
}
/**
* Get the current grid point time.
* @return current grid point time
*/
public double getCurrentTime() {
return currentTime;
}
/**
* Get the time of the interpolated point.
* If {@link #setInterpolatedTime} has not been called, it returns
* the current grid point time.
* @return interpolation point time
*/
public double getInterpolatedTime() {
return interpolatedTime;
}
/**
* Set the time of the interpolated point.
* <p>Setting the time outside of the current step is now allowed
* (it was not allowed up to version 5.4 of Mantissa), but should be
* used with care since the accuracy of the interpolator will
* probably be very poor far from this step. This allowance has been
* added to simplify implementation of search algorithms near the
* step endpoints.</p>
* @param time time of the interpolated point
* @throws DerivativeException if this call induces an automatic
* step finalization that throws one
*/
public void setInterpolatedTime(double time)
throws DerivativeException {
interpolatedTime = time;
double oneMinusThetaH = currentTime - interpolatedTime;
computeInterpolatedState((h - oneMinusThetaH) / h, oneMinusThetaH);
}
/** Check if the natural integration direction is forward.
* <p>This method provides the integration direction as specified by the
* integrator itself, it avoid some nasty problems in degenerated
* cases like null steps due to cancellation at step initialization,
* step control or switching function triggering.</p>
* @return true if the integration variable (time) increases during
* integration
*/
public boolean isForward() {
return forward;
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected abstract void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException;
/**
* Get the state vector of the interpolated point.
* @return state vector at time {@link #getInterpolatedTime}
*/
public double[] getInterpolatedState() {
return interpolatedState;
}
/**
* Finalize the step.
* <p>Some Runge-Kutta-Fehlberg integrators need fewer functions
* evaluations than their counterpart step interpolators. These
* interpolators should perform the last evaluations they need by
* themselves only if they need them. This method triggers these
* extra evaluations. It can be called directly by the user step
* handler and it is called automatically if {@link
* #setInterpolatedTime} is called.</p>
* <p>Once this method has been called, <strong>no</strong> other
* evaluation will be performed on this step. If there is a need to
* have some side effects between the step handler and the
* differential equations (for example update some data in the
* equations once the step has been done), it is advised to call
* this method explicitly from the step handler before these side
* effects are set up. If the step handler induces no side effect,
* then this method can safely be ignored, it will be called
* transparently as needed.</p>
* <p><strong>Warning</strong>: since the step interpolator provided
* to the step handler as a parameter of the {@link
* StepHandler#handleStep handleStep} is valid only for the duration
* of the {@link StepHandler#handleStep handleStep} call, one cannot
* simply store a reference and reuse it later. One should first
* finalize the instance, then copy this finalized instance into a
* new object that can be kept.</p>
* <p>This method calls the protected {@link #doFinalize doFinalize}
* method if it has never been called during this step and set a
* flag indicating that it has been called once. It is the {@link
* #doFinalize doFinalize} method which should perform the
* evaluations. This wrapping prevents from calling {@link
* #doFinalize doFinalize} several times and hence evaluating the
* differential equations too often. Therefore, subclasses are not
* allowed not reimplement it, they should rather reimplement {@link
* #doFinalize doFinalize}.</p>
* @throws DerivativeException this exception is propagated to the
* caller if the underlying user function triggers one
*/
public final void finalizeStep()
throws DerivativeException {
if (! finalized) {
doFinalize();
finalized = true;
}
}
/**
* Really finalize the step.
* The default implementation of this method does nothing.
* @throws DerivativeException this exception is propagated to the
* caller if the underlying user function triggers one
*/
protected void doFinalize()
throws DerivativeException {
}
public abstract void writeExternal(ObjectOutput out)
throws IOException;
public abstract void readExternal(ObjectInput in)
throws IOException;
/** Save the base state of the instance.
* This method performs step finalization if it has not been done
* before.
* @param out stream where to save the state
* @exception IOException in case of write error
*/
protected void writeBaseExternal(ObjectOutput out)
throws IOException {
out.writeInt(currentState.length);
out.writeDouble(previousTime);
out.writeDouble(currentTime);
out.writeDouble(h);
out.writeBoolean(forward);
for (int i = 0; i < currentState.length; ++i) {
out.writeDouble(currentState[i]);
}
out.writeDouble(interpolatedTime);
// we do not store the interpolated state,
// it will be recomputed as needed after reading
// finalize the step (and don't bother saving the now true flag)
try {
finalizeStep();
} catch (DerivativeException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
}
/** Read the base state of the instance.
* This method does <strong>neither</strong> set the interpolated
* time nor state. It is up to the derived class to reset it
* properly calling the {@link #setInterpolatedTime} method later,
* once all rest of the object state has been set up properly.
* @param in stream where to read the state from
* @return interpolated time be set later by the caller
* @exception IOException in case of read error
*/
protected double readBaseExternal(ObjectInput in)
throws IOException {
int dimension = in.readInt();
previousTime = in.readDouble();
currentTime = in.readDouble();
h = in.readDouble();
forward = in.readBoolean();
currentState = new double[dimension];
for (int i = 0; i < currentState.length; ++i) {
currentState[i] = in.readDouble();
}
// we do NOT handle the interpolated time and state here
interpolatedTime = Double.NaN;
interpolatedState = new double[dimension];
finalized = true;
return in.readDouble();
}
}

View File

@ -0,0 +1,317 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This abstract class holds the common part of all adaptive
* stepsize integrators for Ordinary Differential Equations.
* <p>These algorithms perform integration with stepsize control, which
* means the user does not specify the integration step but rather a
* tolerance on error. The error threshold is computed as
* <pre>
* threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
* </pre>
* where absTol_i is the absolute tolerance for component i of the
* state vector and relTol_i is the relative tolerance for the same
* component. The user can also use only two scalar values absTol and
* relTol which will be used for all components.</p>
* <p>If the estimated error for ym+1 is such that
* <pre>
* sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
* </pre>
* (where n is the state vector dimension) then the step is accepted,
* otherwise the step is rejected and a new attempt is made with a new
* stepsize.</p>
* @version $Id: AdaptiveStepsizeIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class AdaptiveStepsizeIntegrator
implements FirstOrderIntegrator {
/** Build an integrator with the given stepsize bounds.
* The default step handler does nothing.
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param scalAbsoluteTolerance allowed absolute error
* @param scalRelativeTolerance allowed relative error
*/
public AdaptiveStepsizeIntegrator(double minStep, double maxStep,
double scalAbsoluteTolerance,
double scalRelativeTolerance) {
this.minStep = minStep;
this.maxStep = maxStep;
this.initialStep = -1.0;
this.scalAbsoluteTolerance = scalAbsoluteTolerance;
this.scalRelativeTolerance = scalRelativeTolerance;
this.vecAbsoluteTolerance = null;
this.vecRelativeTolerance = null;
// set the default step handler
handler = DummyStepHandler.getInstance();
switchesHandler = new SwitchingFunctionsHandler();
}
/** Build an integrator with the given stepsize bounds.
* The default step handler does nothing.
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param vecAbsoluteTolerance allowed absolute error
* @param vecRelativeTolerance allowed relative error
*/
public AdaptiveStepsizeIntegrator(double minStep, double maxStep,
double[] vecAbsoluteTolerance,
double[] vecRelativeTolerance) {
this.minStep = minStep;
this.maxStep = maxStep;
this.initialStep = -1.0;
this.scalAbsoluteTolerance = 0;
this.scalRelativeTolerance = 0;
this.vecAbsoluteTolerance = vecAbsoluteTolerance;
this.vecRelativeTolerance = vecRelativeTolerance;
// set the default step handler
handler = DummyStepHandler.getInstance();
switchesHandler = new SwitchingFunctionsHandler();
}
/** Set the initial step size.
* <p>This method allows the user to specify an initial positive
* step size instead of letting the integrator guess it by
* itself. If this method is not called before integration is
* started, the initial step size will be estimated by the
* integrator.</p>
* @param initialStepSize initial step size to use (must be positive even
* for backward integration ; providing a negative value or a value
* outside of the min/max step interval will lead the integrator to
* ignore the value and compute the initial step size by itself)
*/
public void setInitialStepSize(double initialStepSize) {
if ((initialStepSize < minStep) || (initialStepSize > maxStep)) {
initialStep = -1.0;
} else {
initialStep = initialStepSize;
}
}
/** Set the step handler for this integrator.
* The handler will be called by the integrator for each accepted
* step.
* @param handler handler for the accepted steps
*/
public void setStepHandler (StepHandler handler) {
this.handler = handler;
}
/** Get the step handler for this integrator.
* @return the step handler for this integrator
*/
public StepHandler getStepHandler() {
return handler;
}
/** Add a switching function to the integrator.
* @param function switching function
* @param maxCheckInterval maximal time interval between switching
* function checks (this interval prevents missing sign changes in
* case the integration steps becomes very large)
* @param convergence convergence threshold in the event time search
*/
public void addSwitchingFunction(SwitchingFunction function,
double maxCheckInterval,
double convergence) {
switchesHandler.add(function, maxCheckInterval, convergence);
}
/** Initialize the integration step.
* @param equations differential equations set
* @param forward forward integration indicator
* @param order order of the method
* @param scale scaling vector for the state vector
* @param t0 start time
* @param y0 state vector at t0
* @param yDot0 first time derivative of y0
* @param y1 work array for a state vector
* @param yDot1 work array for the first time derivative of y1
* @return first integration step
* @exception DerivativeException this exception is propagated to
* the caller if the underlying user function triggers one
*/
public double initializeStep(FirstOrderDifferentialEquations equations,
boolean forward, int order, double[] scale,
double t0, double[] y0, double[] yDot0,
double[] y1, double[] yDot1)
throws DerivativeException {
if (initialStep > 0) {
// use the user provided value
return forward ? initialStep : -initialStep;
}
// very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
// this guess will be used to perform an Euler step
double ratio;
double yOnScale2 = 0;
double yDotOnScale2 = 0;
for (int j = 0; j < y0.length; ++j) {
ratio = y0[j] / scale[j];
yOnScale2 += ratio * ratio;
ratio = yDot0[j] / scale[j];
yDotOnScale2 += ratio * ratio;
}
double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10))
? 1.0e-6 : (0.01 * Math.sqrt(yOnScale2 / yDotOnScale2));
if (! forward) {
h = -h;
}
// perform an Euler step using the preceding rough guess
for (int j = 0; j < y0.length; ++j) {
y1[j] = y0[j] + h * yDot0[j];
}
equations.computeDerivatives(t0 + h, y1, yDot1);
// estimate the second derivative of the solution
double yDDotOnScale = 0;
for (int j = 0; j < y0.length; ++j) {
ratio = (yDot1[j] - yDot0[j]) / scale[j];
yDDotOnScale += ratio * ratio;
}
yDDotOnScale = Math.sqrt(yDDotOnScale) / h;
// step size is computed such that
// h^order * max (||y'/tol||, ||y''/tol||) = 0.01
double maxInv2 = Math.max(Math.sqrt(yDotOnScale2), yDDotOnScale);
double h1 = (maxInv2 < 1.0e-15)
? Math.max(1.0e-6, 0.001 * Math.abs(h))
: Math.pow(0.01 / maxInv2, 1.0 / order);
h = Math.min(100.0 * Math.abs(h), h1);
h = Math.max(h, 1.0e-12 * Math.abs(t0)); // avoids cancellation when computing t1 - t0
if (h < getMinStep()) {
h = getMinStep();
}
if (h > getMaxStep()) {
h = getMaxStep();
}
if (! forward) {
h = -h;
}
return h;
}
/** Filter the integration step.
* @param h signed step
* @param acceptSmall if true, steps smaller than the minimal value
* are silently increased up to this value, if false such small
* steps generate an exception
* @return a bounded integration step (h if no bound is reach, or a bounded value)
* @exception IntegratorException if the step is too small and acceptSmall is false
*/
protected double filterStep(double h, boolean acceptSmall)
throws IntegratorException {
if (Math.abs(h) < minStep) {
if (acceptSmall) {
h = (h < 0) ? -minStep : minStep;
} else {
throw new IntegratorException("minimal step size ({0}) reached,"
+ " integration needs {1}",
new String[] {
Double.toString(minStep),
Double.toString(Math.abs(h))
});
}
}
if (h > maxStep) {
h = maxStep;
} else if (h < -maxStep) {
h = -maxStep;
}
return h;
}
public abstract void integrate (FirstOrderDifferentialEquations equations,
double t0, double[] y0,
double t, double[] y)
throws DerivativeException, IntegratorException;
/** Get the minimal step.
* @return minimal step
*/
public double getMinStep() {
return minStep;
}
/** Get the maximal step.
* @return maximal step
*/
public double getMaxStep() {
return maxStep;
}
/** Minimal step. */
private double minStep;
/** Maximal step. */
private double maxStep;
/** User supplied initial step. */
private double initialStep;
/** Allowed absolute scalar error. */
protected double scalAbsoluteTolerance;
/** Allowed relative scalar error. */
protected double scalRelativeTolerance;
/** Allowed absolute vectorial error. */
protected double[] vecAbsoluteTolerance;
/** Allowed relative vectorial error. */
protected double[] vecRelativeTolerance;
/** Step handler. */
protected StepHandler handler;
/** Switching functions handler. */
protected SwitchingFunctionsHandler switchesHandler;
}

View File

@ -0,0 +1,82 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the classical fourth order Runge-Kutta
* integrator for Ordinary Differential Equations (it is the most
* often used Runge-Kutta method).
* <p>This method is an explicit Runge-Kutta method, its Butcher-array
* is the following one :
* <pre>
* 0 | 0 0 0 0
* 1/2 | 1/2 0 0 0
* 1/2 | 0 1/2 0 0
* 1 | 0 0 1 0
* |--------------------
* | 1/6 1/3 1/3 1/6
* </pre>
* </p>
* @see EulerIntegrator
* @see GillIntegrator
* @see MidpointIntegrator
* @see ThreeEighthesIntegrator
* @version $Id: ClassicalRungeKuttaIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ClassicalRungeKuttaIntegrator
extends RungeKuttaIntegrator {
private static final String methodName = new String("classical Runge-Kutta");
private static final double[] c = {
1.0 / 2.0, 1.0 / 2.0, 1.0
};
private static final double[][] a = {
{ 1.0 / 2.0 },
{ 0.0, 1.0 / 2.0 },
{ 0.0, 0.0, 1.0 }
};
private static final double[] b = {
1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0
};
/** Simple constructor.
* Build a fourth-order Runge-Kutta integrator with the given
* step.
* @param step integration step
*/
public ClassicalRungeKuttaIntegrator(double step) {
super(false, c, a, b, new ClassicalRungeKuttaStepInterpolator(), step);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
}

View File

@ -0,0 +1,113 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a step interpolator for the classical fourth
* order Runge-Kutta integrator.
* <p>This interpolator allows to compute dense output inside the last
* step computed. The interpolation equation is consistent with the
* integration scheme :
* <pre>
* y(t_n + theta h) = y (t_n + h)
* + (1 - theta) (h/6) [ (-4 theta^2 + 5 theta - 1) y'_1
* +(4 theta^2 - 2 theta - 2) (y'_2 + y'_3)
* -(4 theta^2 + theta + 1) y'_4
* ]
* </pre>
* where theta belongs to [0 ; 1] and where y'_1 to y'_4 are the four
* evaluations of the derivatives already computed during the
* step.</p>
* @see ClassicalRungeKuttaIntegrator
* @version $Id: ClassicalRungeKuttaStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class ClassicalRungeKuttaStepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link RungeKuttaStepInterpolator#reinitialize} method should be
* called before using the instance in order to initialize the
* internal arrays. This constructor is used only in order to delay
* the initialization in some cases. The {@link RungeKuttaIntegrator}
* class uses the prototyping design pattern to create the step
* interpolators by cloning an uninitialized model and latter initializing
* the copy.
*/
public ClassicalRungeKuttaStepInterpolator() {
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public ClassicalRungeKuttaStepInterpolator(ClassicalRungeKuttaStepInterpolator interpolator) {
super(interpolator);
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new ClassicalRungeKuttaStepInterpolator(this);
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
double fourTheta = 4 * theta;
double s = oneMinusThetaH / 6.0;
double coeff1 = s * ((-fourTheta + 5) * theta - 1);
double coeff23 = s * (( fourTheta - 2) * theta - 2);
double coeff4 = s * ((-fourTheta - 1) * theta - 1);
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
+ coeff1 * yDotK[0][i]
+ coeff23 * (yDotK[1][i] + yDotK[2][i])
+ coeff4 * yDotK[3][i];
}
}
private static final long serialVersionUID = -6576285612589783992L;
}

View File

@ -0,0 +1,375 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.Serializable;
/**
* This class stores all information provided by an ODE integrator
* during the integration process and build a continuous model of the
* solution from this.
* <p>This class act as a step handler from the integrator point of
* view. It is called iteratively during the integration process and
* stores a copy of all steps information in a sorted collection for
* later use. Once the integration process is over, the user can use
* the {@link #setInterpolatedTime setInterpolatedTime} and {@link
* #getInterpolatedState getInterpolatedState} to retrieve this
* information at any time. It is important to wait for the
* integration to be over before attempting to call {@link
* #setInterpolatedTime setInterpolatedTime} because some internal
* variables are set only once the last step has been handled.</p>
* <p>This is useful for example if the main loop of the user
* application should remain independant from the integration process
* or if one needs to mimic the behaviour of an analytical model
* despite a numerical model is used (i.e. one needs the ability to
* get the model value at any time or to navigate through the
* data).</p>
* <p>If problem modelization is done with several separate
* integration phases for contiguous intervals, the same
* ContinuousOutputModel can be used as step handler for all
* integration phases as long as they are performed in order and in
* the same direction. As an example, one can extrapolate the
* trajectory of a satellite with one model (i.e. one set of
* differential equations) up to the beginning of a maneuver, use
* another more complex model including thrusters modelization and
* accurate attitude control during the maneuver, and revert to the
* first model after the end of the maneuver. If the same continuous
* output model handles the steps of all integration phases, the user
* do not need to bother when the maneuver begins or ends, he has all
* the data available in a transparent manner.</p>
* <p>An important feature of this class is that it implements the
* <code>Serializable</code> interface. This means that the result of
* an integration can be serialized and reused later (if stored into a
* persistent medium like a filesystem or a database) or elsewhere (if
* sent to another application). Only the result of the integration is
* stored, there is no reference to the integrated problem by
* itself.</p>
* <p>One should be aware that the amount of data stored in a
* ContinuousOutputModel instance can be important if the state vector
* is large, if the integration interval is long or if the steps are
* small (which can result from small tolerance settings in {@link
* AdaptiveStepsizeIntegrator adaptive step size integrators}).</p>
* @see StepHandler
* @see StepInterpolator
* @version $Id: ContinuousOutputModel.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class ContinuousOutputModel
implements StepHandler, Serializable {
/** Simple constructor.
* Build an empty continuous output model.
*/
public ContinuousOutputModel() {
steps = new ArrayList();
reset();
}
/** Append another model at the end of the instance.
* @param model model to add at the end of the instance
* @exception IllegalArgumentException if the model to append is not
* compatible with the instance (dimension of the state vector,
* propagation direction, hole between the dates)
*/
public void append(ContinuousOutputModel model) {
if (model.steps.size() == 0) {
return;
}
if (steps.size() == 0) {
initialTime = model.initialTime;
forward = model.forward;
} else {
if (getInterpolatedState().length != model.getInterpolatedState().length) {
throw new IllegalArgumentException("state vector dimension mismatch");
}
if (forward ^ model.forward) {
throw new IllegalArgumentException("propagation direction mismatch");
}
StepInterpolator lastInterpolator = (StepInterpolator) steps.get(index);
double current = lastInterpolator.getCurrentTime();
double previous = lastInterpolator.getPreviousTime();
double step = current - previous;
double gap = model.getInitialTime() - current;
if (Math.abs(gap) > 1.0e-3 * Math.abs(step)) {
throw new IllegalArgumentException("hole between time ranges");
}
}
for (Iterator iter = model.steps.iterator(); iter.hasNext(); ) {
AbstractStepInterpolator ai = (AbstractStepInterpolator) iter.next();
steps.add(ai.clone());
}
index = steps.size() - 1;
finalTime = ((StepInterpolator) steps.get(index)).getCurrentTime();
}
/** Determines whether this handler needs dense output.
* <p>The essence of this class is to provide dense output over all
* steps, hence it requires the internal steps to provide themselves
* dense output. The method therefore returns always true.</p>
* @return always true
*/
public boolean requiresDenseOutput() {
return true;
}
/** Reset the step handler.
* Initialize the internal data as required before the first step is
* handled.
*/
public void reset() {
initialTime = Double.NaN;
finalTime = Double.NaN;
forward = true;
index = 0;
steps.clear();
}
/** Handle the last accepted step.
* A copy of the information provided by the last step is stored in
* the instance for later use.
* @param interpolator interpolator for the last accepted step.
* @param isLast true if the step is the last one
* @throws DerivativeException this exception is propagated to the
* caller if the underlying user function triggers one
*/
public void handleStep(StepInterpolator interpolator, boolean isLast)
throws DerivativeException {
AbstractStepInterpolator ai = (AbstractStepInterpolator) interpolator;
if (steps.size() == 0) {
initialTime = interpolator.getPreviousTime();
forward = interpolator.isForward();
}
ai.finalizeStep();
steps.add(ai.clone());
if (isLast) {
finalTime = ai.getCurrentTime();
index = steps.size() - 1;
}
}
/**
* Get the initial integration time.
* @return initial integration time
*/
public double getInitialTime() {
return initialTime;
}
/**
* Get the final integration time.
* @return final integration time
*/
public double getFinalTime() {
return finalTime;
}
/**
* Get the time of the interpolated point.
* If {@link #setInterpolatedTime} has not been called, it returns
* the final integration time.
* @return interpolation point time
*/
public double getInterpolatedTime() {
return ((StepInterpolator) steps.get(index)).getInterpolatedTime();
}
/** Set the time of the interpolated point.
* <p>This method should <strong>not</strong> be called before the
* integration is over because some internal variables are set only
* once the last step has been handled.</p>
* <p>Setting the time outside of the integration interval is now
* allowed (it was not allowed up to version 5.9 of Mantissa), but
* should be used with care since the accuracy of the interpolator
* will probably be very poor far from this interval. This allowance
* has been added to simplify implementation of search algorithms
* near the interval endpoints.</p>
* @param time time of the interpolated point
*/
public void setInterpolatedTime(double time) {
try {
// initialize the search with the complete steps table
int iMin = 0;
StepInterpolator sMin = (StepInterpolator) steps.get(iMin);
double tMin = 0.5 * (sMin.getPreviousTime() + sMin.getCurrentTime());
int iMax = steps.size() - 1;
StepInterpolator sMax = (StepInterpolator) steps.get(iMax);
double tMax = 0.5 * (sMax.getPreviousTime() + sMax.getCurrentTime());
// handle points outside of the integration interval
// or in the first and last step
if (locatePoint(time, sMin) <= 0) {
index = iMin;
sMin.setInterpolatedTime(time);
return;
}
if (locatePoint(time, sMax) >= 0) {
index = iMax;
sMax.setInterpolatedTime(time);
return;
}
// reduction of the table slice size
while (iMax - iMin > 5) {
// use the last estimated index as the splitting index
StepInterpolator si = (StepInterpolator) steps.get(index);
int location = locatePoint(time, si);
if (location < 0) {
iMax = index;
tMax = 0.5 * (si.getPreviousTime() + si.getCurrentTime());
} else if (location > 0) {
iMin = index;
tMin = 0.5 * (si.getPreviousTime() + si.getCurrentTime());
} else {
// we have found the target step, no need to continue searching
si.setInterpolatedTime(time);
return;
}
// compute a new estimate of the index in the reduced table slice
int iMed = (iMin + iMax) / 2;
StepInterpolator sMed = (StepInterpolator) steps.get(iMed);
double tMed = 0.5 * (sMed.getPreviousTime() + sMed.getCurrentTime());
if ((Math.abs(tMed - tMin) < 1e-6) || (Math.abs(tMax - tMed) < 1e-6)) {
// too close to the bounds, we estimate using a simple dichotomy
index = iMed;
} else {
// estimate the index using a reverse quadratic polynom
// (reverse means we have i = P(t), thus allowing to simply
// compute index = P(time) rather than solving a quadratic equation)
double d12 = tMax - tMed;
double d23 = tMed - tMin;
double d13 = tMax - tMin;
double dt1 = time - tMax;
double dt2 = time - tMed;
double dt3 = time - tMin;
double iLagrange = ( (dt2 * dt3 * d23) * iMax
- (dt1 * dt3 * d13) * iMed
+ (dt1 * dt2 * d12) * iMin)
/ (d12 * d23 * d13);
index = (int) Math.rint(iLagrange);
}
// force the next size reduction to be at least one tenth
int low = Math.max(iMin + 1, (9 * iMin + iMax) / 10);
int high = Math.min(iMax - 1, (iMin + 9 * iMax) / 10);
if (index < low) {
index = low;
} else if (index > high) {
index = high;
}
}
// now the table slice is very small, we perform an iterative search
index = iMin;
while ((index <= iMax)
&& (locatePoint(time, (StepInterpolator) steps.get(index)) > 0)) {
++index;
}
StepInterpolator si = (StepInterpolator) steps.get(index);
si.setInterpolatedTime(time);
} catch (DerivativeException de) {
throw new RuntimeException("unexpected DerivativeException caught", de);
}
}
/**
* Get the state vector of the interpolated point.
* @return state vector at time {@link #getInterpolatedTime}
*/
public double[] getInterpolatedState() {
return ((StepInterpolator) steps.get(index)).getInterpolatedState();
}
/** Compare a step interval and a double.
* @param time point to locate
* @param interval step interval
* @return -1 if the double is before the interval, 0 if it is in
* the interval, and +1 if it is after the interval, according to
* the interval direction
*/
private int locatePoint(double time, StepInterpolator interval) {
if (forward) {
if (time < interval.getPreviousTime()) {
return -1;
} else if (time > interval.getCurrentTime()) {
return +1;
} else {
return 0;
}
}
if (time > interval.getPreviousTime()) {
return -1;
} else if (time < interval.getCurrentTime()) {
return +1;
} else {
return 0;
}
}
/** Initial integration time. */
private double initialTime;
/** Final integration time. */
private double finalTime;
/** Integration direction indicator. */
private boolean forward;
/** Current interpolator index. */
private int index;
/** Steps table. */
private ArrayList steps;
private static final long serialVersionUID = 2259286184268533249L;
}

View File

@ -0,0 +1,50 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import org.spaceroots.mantissa.MantissaException;
/**
* This exception is made available to users to report
* the error conditions that are trigegred while computing
* the differential equations.
* @author Luc Maisonobe
* @version $Id: DerivativeException.java 1705 2006-09-17 19:57:39Z luc $
*/
public class DerivativeException
extends MantissaException {
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public DerivativeException(String specifier, String[] parts) {
super(specifier, parts);
}
/** Build an instance from an underlying cause.
* @param cause cause for the exception
*/
public DerivativeException(Throwable cause) {
super(cause);
}
private static final long serialVersionUID = -4100440615830558122L;
}

View File

@ -0,0 +1,153 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the 5(4) Dormand-Prince integrator for Ordinary
* Differential Equations.
* <p>This integrator is an embedded Runge-Kutta-Fehlberg integrator
* of order 5(4) used in local extrapolation mode (i.e. the solution
* is computed using the high order formula) with stepsize control
* (and automatic step initialization) and continuous output. This
* method uses 7 functions evaluations per step. However, since this
* is an <i>fsal</i>, the last evaluation of one step is the same as
* the first evaluation of the next step and hence can be avoided. So
* the cost is really 6 functions evaluations per step.</p>
* <p>This method has been published (whithout the continuous output
* that was added by Shampine in 1986) in the following article :
* <pre>
* A family of embedded Runge-Kutta formulae
* J. R. Dormand and P. J. Prince
* Journal of Computational and Applied Mathematics
* volume 6, no 1, 1980, pp. 19-26
* </pre></p>
* @version $Id: DormandPrince54Integrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class DormandPrince54Integrator
extends RungeKuttaFehlbergIntegrator {
private static final String methodName = new String("Dormand-Prince 5(4)");
private static final double[] c = {
1.0/5.0, 3.0/10.0, 4.0/5.0, 8.0/9.0, 1.0, 1.0
};
private static final double[][] a = {
{1.0/5.0},
{3.0/40.0, 9.0/40.0},
{44.0/45.0, -56.0/15.0, 32.0/9.0},
{19372.0/6561.0, -25360.0/2187.0, 64448.0/6561.0, -212.0/729.0},
{9017.0/3168.0, -355.0/33.0, 46732.0/5247.0, 49.0/176.0, -5103.0/18656.0},
{35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0}
};
private static final double[] b = {
35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0, 0.0
};
private static final double e1 = 71.0 / 57600.0;
private static final double e3 = -71.0 / 16695.0;
private static final double e4 = 71.0 / 1920.0;
private static final double e5 = -17253.0 / 339200.0;
private static final double e6 = 22.0 / 525.0;
private static final double e7 = -1.0 / 40.0;
/** Simple constructor.
* Build a fifth order Dormand-Prince integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param scalAbsoluteTolerance allowed absolute error
* @param scalRelativeTolerance allowed relative error
*/
public DormandPrince54Integrator(double minStep, double maxStep,
double scalAbsoluteTolerance,
double scalRelativeTolerance) {
super(true, c, a, b, new DormandPrince54StepInterpolator(),
minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
}
/** Simple constructor.
* Build a fifth order Dormand-Prince integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param vecAbsoluteTolerance allowed absolute error
* @param vecRelativeTolerance allowed relative error
*/
public DormandPrince54Integrator(double minStep, double maxStep,
double[] vecAbsoluteTolerance,
double[] vecRelativeTolerance) {
super(true, c, a, b, new DormandPrince54StepInterpolator(),
minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
/** Get the order of the method.
* @return order of the method
*/
public int getOrder() {
return 5;
}
/** Compute the error ratio.
* @param yDotK derivatives computed during the first stages
* @param y0 estimate of the step at the start of the step
* @param y1 estimate of the step at the end of the step
* @param h current step
* @return error ratio, greater than 1 if step should be rejected
*/
protected double estimateError(double[][] yDotK,
double[] y0, double[] y1,
double h) {
double error = 0;
for (int j = 0; j < y0.length; ++j) {
double errSum = e1 * yDotK[0][j] + e3 * yDotK[2][j]
+ e4 * yDotK[3][j] + e5 * yDotK[4][j]
+ e6 * yDotK[5][j] + e7 * yDotK[6][j];
double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j]));
double tol = (vecAbsoluteTolerance == null)
? (scalAbsoluteTolerance + scalRelativeTolerance * yScale)
: (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
double ratio = h * errSum / tol;
error += ratio * ratio;
}
return Math.sqrt(error / y0.length);
}
}

View File

@ -0,0 +1,205 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class represents an interpolator over the last step during an
* ODE integration for the 5(4) Dormand-Prince integrator.
*
* @see DormandPrince54Integrator
*
* @version $Id: DormandPrince54StepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
class DormandPrince54StepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link #reinitialize} method should be called before using the
* instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaFehlbergIntegrator} uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public DormandPrince54StepInterpolator() {
super();
v1 = null;
v2 = null;
v3 = null;
v4 = null;
vectorsInitialized = false;
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public DormandPrince54StepInterpolator(DormandPrince54StepInterpolator interpolator) {
super(interpolator);
if (interpolator.v1 == null) {
v1 = null;
v2 = null;
v3 = null;
v4 = null;
vectorsInitialized = false;
} else {
int dimension = interpolator.v1.length;
v1 = new double[dimension];
v2 = new double[dimension];
v3 = new double[dimension];
v4 = new double[dimension];
System.arraycopy(interpolator.v1, 0, v1, 0, dimension);
System.arraycopy(interpolator.v2, 0, v2, 0, dimension);
System.arraycopy(interpolator.v3, 0, v3, 0, dimension);
System.arraycopy(interpolator.v4, 0, v4, 0, dimension);
vectorsInitialized = interpolator.vectorsInitialized;
}
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new DormandPrince54StepInterpolator(this);
}
/** Reinitialize the instance
* @param equations set of differential equations being integrated
* @param y reference to the integrator array holding the state at
* the end of the step
* @param yDotK reference to the integrator array holding all the
* intermediate slopes
* @param forward integration direction indicator
*/
public void reinitialize(FirstOrderDifferentialEquations equations,
double[] y, double[][] yDotK, boolean forward) {
super.reinitialize(equations, y, yDotK, forward);
v1 = null;
v2 = null;
v3 = null;
v4 = null;
vectorsInitialized = false;
}
/** Store the current step time.
* @param t current time
*/
public void storeTime(double t) {
super.storeTime(t);
vectorsInitialized = false;
}
/** Compute the state at the interpolated time.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
if (! vectorsInitialized) {
if (v1 == null) {
v1 = new double[interpolatedState.length];
v2 = new double[interpolatedState.length];
v3 = new double[interpolatedState.length];
v4 = new double[interpolatedState.length];
}
// no step finalization is needed for this interpolator
// we need to compute the interpolation vectors for this time step
for (int i = 0; i < interpolatedState.length; ++i) {
v1[i] = h * (a70 * yDotK[0][i] + a72 * yDotK[2][i] + a73 * yDotK[3][i]
+ a74 * yDotK[4][i] + a75 * yDotK[5][i]);
v2[i] = h * yDotK[0][i] - v1[i];
v3[i] = v1[i] - v2[i] - h * yDotK[6][i];
v4[i] = h * (d0 * yDotK[0][i] + d2 * yDotK[2][i] + d3 * yDotK[3][i]
+ d4 * yDotK[4][i] + d5 * yDotK[5][i] + d6 * yDotK[6][i]);
}
vectorsInitialized = true;
}
// interpolate
double eta = oneMinusThetaH / h;
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
- eta * (v1[i]
- theta * (v2[i]
+ theta * (v3[i]
+ eta * v4[i])));
}
}
/** First vector for interpolation. */
private double[] v1;
/** Second vector for interpolation. */
private double[] v2;
/** Third vector for interpolation. */
private double[] v3;
/** Fourth vector for interpolation. */
private double[] v4;
/** Initialization indicator for the interpolation vectors. */
private boolean vectorsInitialized;
// last row of the Butcher-array internal weights, note that a71 is null
private static final double a70 = 35.0 / 384.0;
private static final double a72 = 500.0 / 1113.0;
private static final double a73 = 125.0 / 192.0;
private static final double a74 = -2187.0 / 6784.0;
private static final double a75 = 11.0 / 84.0;
// dense output of Shampine (1986), note that d1 is null
private static final double d0 = -12715105075.0 / 11282082432.0;
private static final double d2 = 87487479700.0 / 32700410799.0;
private static final double d3 = -10690763975.0 / 1880347072.0;
private static final double d4 = 701980252875.0 / 199316789632.0;
private static final double d5 = -1453857185.0 / 822651844.0;
private static final double d6 = 69997945.0 / 29380423.0;
private static final long serialVersionUID = 4104157279605906956L;
}

View File

@ -0,0 +1,258 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
* Differential Equations.
* <p>This integrator is an embedded Runge-Kutta-Fehlberg integrator
* of order 8(5,3) used in local extrapolation mode (i.e. the solution
* is computed using the high order formula) with stepsize control
* (and automatic step initialization) and continuous output. This
* method uses 12 functions evaluations per step for integration and 4
* evaluations for interpolation. However, since the first
* interpolation evaluation is the same as the first integration
* evaluation of the next step, we have included it in the integrator
* rather than in the interpolator and specified the method was an
* <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
* really 12 evaluations per step even if no interpolation is done,
* and the overcost of interpolation is only 3 evaluations.</p>
* <p>This method is based on an 8(6) method by Dormand and Prince
* (i.e. order 8 for the integration and order 6 for error estimation)
* modified by Hairer and Wanner to use a 5th order error estimator
* with 3rd order correction. This modification was introduced because
* the original method failed in some cases (wrong steps can be
* accepted when step size is too large, for example in the
* Brusselator problem) and also had <i>severe difficulties when
* applied to problems with discontinuities</i>. This modification is
* explained in the second edition of the first volume (Nonstiff
* Problems) of the reference book by Hairer, Norsett and Wanner:
* <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
* ISBN 3-540-56670-8).</p>
* @version $Id: DormandPrince853Integrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class DormandPrince853Integrator
extends RungeKuttaFehlbergIntegrator {
private static final String methodName = new String("Dormand-Prince 8 (5, 3)");
private static final double sqrt6 = Math.sqrt(6.0);
private static final double[] c = {
(12.0 - 2.0 * sqrt6) / 135.0, (6.0 - sqrt6) / 45.0, (6.0 - sqrt6) / 30.0,
(6.0 + sqrt6) / 30.0, 1.0/3.0, 1.0/4.0, 4.0/13.0, 127.0/195.0, 3.0/5.0,
6.0/7.0, 1.0, 1.0
};
private static final double[][] a = {
// k2
{(12.0 - 2.0 * sqrt6) / 135.0},
// k3
{(6.0 - sqrt6) / 180.0, (6.0 - sqrt6) / 60.0},
// k4
{(6.0 - sqrt6) / 120.0, 0.0, (6.0 - sqrt6) / 40.0},
// k5
{(462.0 + 107.0 * sqrt6) / 3000.0, 0.0,
(-402.0 - 197.0 * sqrt6) / 1000.0, (168.0 + 73.0 * sqrt6) / 375.0},
// k6
{1.0 / 27.0, 0.0, 0.0, (16.0 + sqrt6) / 108.0, (16.0 - sqrt6) / 108.0},
// k7
{19.0 / 512.0, 0.0, 0.0, (118.0 + 23.0 * sqrt6) / 1024.0,
(118.0 - 23.0 * sqrt6) / 1024.0, -9.0 / 512.0},
// k8
{13772.0 / 371293.0, 0.0, 0.0, (51544.0 + 4784.0 * sqrt6) / 371293.0,
(51544.0 - 4784.0 * sqrt6) / 371293.0, -5688.0 / 371293.0, 3072.0 / 371293.0},
// k9
{58656157643.0 / 93983540625.0, 0.0, 0.0,
(-1324889724104.0 - 318801444819.0 * sqrt6) / 626556937500.0,
(-1324889724104.0 + 318801444819.0 * sqrt6) / 626556937500.0,
96044563816.0 / 3480871875.0, 5682451879168.0 / 281950621875.0,
-165125654.0 / 3796875.0},
// k10
{8909899.0 / 18653125.0, 0.0, 0.0,
(-4521408.0 - 1137963.0 * sqrt6) / 2937500.0,
(-4521408.0 + 1137963.0 * sqrt6) / 2937500.0,
96663078.0 / 4553125.0, 2107245056.0 / 137915625.0,
-4913652016.0 / 147609375.0, -78894270.0 / 3880452869.0},
// k11
{-20401265806.0 / 21769653311.0, 0.0, 0.0,
(354216.0 + 94326.0 * sqrt6) / 112847.0,
(354216.0 - 94326.0 * sqrt6) / 112847.0,
-43306765128.0 / 5313852383.0, -20866708358144.0 / 1126708119789.0,
14886003438020.0 / 654632330667.0, 35290686222309375.0 / 14152473387134411.0,
-1477884375.0 / 485066827.0},
// k12
{39815761.0 / 17514443.0, 0.0, 0.0,
(-3457480.0 - 960905.0 * sqrt6) / 551636.0,
(-3457480.0 + 960905.0 * sqrt6) / 551636.0,
-844554132.0 / 47026969.0, 8444996352.0 / 302158619.0,
-2509602342.0 / 877790785.0, -28388795297996250.0 / 3199510091356783.0,
226716250.0 / 18341897.0, 1371316744.0 / 2131383595.0},
// k13 should be for interpolation only, but since it is the same
// stage as the first evaluation of the next step, we perform it
// here at no cost by specifying this is an fsal method
{104257.0/1920240.0, 0.0, 0.0, 0.0, 0.0, 3399327.0/763840.0,
66578432.0/35198415.0, -1674902723.0/288716400.0,
54980371265625.0/176692375811392.0, -734375.0/4826304.0,
171414593.0/851261400.0, 137909.0/3084480.0}
};
private static final double[] b = {
104257.0/1920240.0,
0.0,
0.0,
0.0,
0.0,
3399327.0/763840.0,
66578432.0/35198415.0,
-1674902723.0/288716400.0,
54980371265625.0/176692375811392.0,
-734375.0/4826304.0,
171414593.0/851261400.0,
137909.0/3084480.0,
0.0
};
private static final double e1_01 = 116092271.0 / 8848465920.0;
private static final double e1_06 = -1871647.0 / 1527680.0;
private static final double e1_07 = -69799717.0 / 140793660.0;
private static final double e1_08 = 1230164450203.0 / 739113984000.0;
private static final double e1_09 = -1980813971228885.0 / 5654156025964544.0;
private static final double e1_10 = 464500805.0 / 1389975552.0;
private static final double e1_11 = 1606764981773.0 / 19613062656000.0;
private static final double e1_12 = -137909.0 / 6168960.0;
private static final double e2_01 = -364463.0 / 1920240.0;
private static final double e2_06 = 3399327.0 / 763840.0;
private static final double e2_07 = 66578432.0 / 35198415.0;
private static final double e2_08 = -1674902723.0 / 288716400.0;
private static final double e2_09 = -74684743568175.0 / 176692375811392.0;
private static final double e2_10 = -734375.0 / 4826304.0;
private static final double e2_11 = 171414593.0 / 851261400.0;
private static final double e2_12 = 69869.0 / 3084480.0;
/** Simple constructor.
* Build an eighth order Dormand-Prince integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param scalAbsoluteTolerance allowed absolute error
* @param scalRelativeTolerance allowed relative error
*/
public DormandPrince853Integrator(double minStep, double maxStep,
double scalAbsoluteTolerance,
double scalRelativeTolerance) {
super(true, c, a, b,
new DormandPrince853StepInterpolator(),
minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
}
/** Simple constructor.
* Build an eighth order Dormand-Prince integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param vecAbsoluteTolerance allowed absolute error
* @param vecRelativeTolerance allowed relative error
*/
public DormandPrince853Integrator(double minStep, double maxStep,
double[] vecAbsoluteTolerance,
double[] vecRelativeTolerance) {
super(true, c, a, b,
new DormandPrince853StepInterpolator(),
minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
/** Get the order of the method.
* @return order of the method
*/
public int getOrder() {
return 8;
}
/** Compute the error ratio.
* @param yDotK derivatives computed during the first stages
* @param y0 estimate of the step at the start of the step
* @param y1 estimate of the step at the end of the step
* @param h current step
* @return error ratio, greater than 1 if step should be rejected
*/
protected double estimateError(double[][] yDotK,
double[] y0, double[] y1,
double h) {
double error1 = 0;
double error2 = 0;
for (int j = 0; j < y0.length; ++j) {
double errSum1 = e1_01 * yDotK[0][j] + e1_06 * yDotK[5][j]
+ e1_07 * yDotK[6][j] + e1_08 * yDotK[7][j]
+ e1_09 * yDotK[8][j] + e1_10 * yDotK[9][j]
+ e1_11 * yDotK[10][j] + e1_12 * yDotK[11][j];
double errSum2 = e2_01 * yDotK[0][j] + e2_06 * yDotK[5][j]
+ e2_07 * yDotK[6][j] + e2_08 * yDotK[7][j]
+ e2_09 * yDotK[8][j] + e2_10 * yDotK[9][j]
+ e2_11 * yDotK[10][j] + e2_12 * yDotK[11][j];
double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j]));
double tol = (vecAbsoluteTolerance == null)
? (scalAbsoluteTolerance + scalRelativeTolerance * yScale)
: (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
double ratio1 = errSum1 / tol;
error1 += ratio1 * ratio1;
double ratio2 = errSum2 / tol;
error2 += ratio2 * ratio2;
}
double den = error1 + 0.01 * error2;
if (den <= 0.0) {
den = 1.0;
}
return Math.abs(h) * error1 / Math.sqrt(y0.length * den);
}
}

View File

@ -0,0 +1,415 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import java.io.ObjectOutput;
import java.io.ObjectInput;
import java.io.IOException;
/**
* This class represents an interpolator over the last step during an
* ODE integration for the 8(5,3) Dormand-Prince integrator.
*
* @see DormandPrince853Integrator
*
* @version $Id: DormandPrince853StepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
class DormandPrince853StepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link #reinitialize} method should be called before using the
* instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaFehlbergIntegrator} uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public DormandPrince853StepInterpolator() {
super();
yDotKLast = null;
yTmp = null;
v = null;
vectorsInitialized = false;
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public DormandPrince853StepInterpolator(DormandPrince853StepInterpolator interpolator) {
super(interpolator);
if (interpolator.currentState == null) {
yDotKLast = null;
v = null;
vectorsInitialized = false;
} else {
int dimension = interpolator.currentState.length;
yDotKLast = new double[3][];
for (int k = 0; k < yDotKLast.length; ++k) {
yDotKLast[k] = new double[dimension];
System.arraycopy(interpolator.yDotKLast[k], 0, yDotKLast[k], 0,
dimension);
}
v = new double[7][];
for (int k = 0; k < v.length; ++k) {
v[k] = new double[dimension];
System.arraycopy(interpolator.v[k], 0, v[k], 0, dimension);
}
vectorsInitialized = interpolator.vectorsInitialized;
}
// the step has been finalized, we don't need this anymore
yTmp = null;
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new DormandPrince853StepInterpolator(this);
}
/** Reinitialize the instance
* Some Runge-Kutta-Fehlberg integrators need fewer functions
* evaluations than their counterpart step interpolators. So the
* interpolator should perform the last evaluations they need by
* themselves. The {@link RungeKuttaFehlbergIntegrator
* RungeKuttaFehlbergIntegrator} abstract class calls this method in
* order to let the step interpolator perform the evaluations it
* needs. These evaluations will be performed during the call to
* <code>doFinalize</code> if any, i.e. only if the step handler
* either calls the {@link AbstractStepInterpolator#finalizeStep
* finalizeStep} method or the {@link
* AbstractStepInterpolator#getInterpolatedState getInterpolatedState}
* method (for an interpolator which needs a finalization) or if it clones
* the step interpolator.
* @param equations set of differential equations being integrated
* @param y reference to the integrator array holding the state at
* the end of the step
* @param yDotK reference to the integrator array holding all the
* intermediate slopes
* @param forward integration direction indicator
*/
public void reinitialize(FirstOrderDifferentialEquations equations,
double[] y, double[][] yDotK, boolean forward) {
super.reinitialize(equations, y, yDotK, forward);
int dimension = currentState.length;
yDotKLast = new double[3][];
for (int k = 0; k < yDotKLast.length; ++k) {
yDotKLast[k] = new double[dimension];
}
yTmp = new double[dimension];
v = new double[7][];
for (int k = 0; k < v.length; ++k) {
v[k] = new double[dimension];
}
vectorsInitialized = false;
}
/** Store the current step time.
* @param t current time
*/
public void storeTime(double t) {
super.storeTime(t);
vectorsInitialized = false;
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
if (! vectorsInitialized) {
if (v == null) {
v = new double[7][];
for (int k = 0; k < 7; ++k) {
v[k] = new double[interpolatedState.length];
}
}
// perform the last evaluations if they have not been done yet
finalizeStep();
// compute the interpolation vectors for this time step
for (int i = 0; i < interpolatedState.length; ++i) {
v[0][i] = h * (b_01 * yDotK[0][i] + b_06 * yDotK[5][i] + b_07 * yDotK[6][i]
+ b_08 * yDotK[7][i] + b_09 * yDotK[8][i] + b_10 * yDotK[9][i]
+ b_11 * yDotK[10][i] + b_12 * yDotK[11][i]);
v[1][i] = h * yDotK[0][i] - v[0][i];
v[2][i] = v[0][i] - v[1][i] - h * yDotK[12][i];
for (int k = 0; k < d.length; ++k) {
v[k+3][i] = h * (d[k][0] * yDotK[0][i] + d[k][1] * yDotK[5][i] + d[k][2] * yDotK[6][i]
+ d[k][3] * yDotK[7][i] + d[k][4] * yDotK[8][i] + d[k][5] * yDotK[9][i]
+ d[k][6] * yDotK[10][i] + d[k][7] * yDotK[11][i] + d[k][8] * yDotK[12][i]
+ d[k][9] * yDotKLast[0][i]
+ d[k][10] * yDotKLast[1][i]
+ d[k][11] * yDotKLast[2][i]);
}
}
vectorsInitialized = true;
}
double eta = oneMinusThetaH / h;
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
- eta * (v[0][i]
- theta * (v[1][i]
+ theta * (v[2][i]
+ eta * (v[3][i]
+ theta * (v[4][i]
+ eta * (v[5][i]
+ theta * (v[6][i])))))));
}
}
/**
* Really finalize the step.
* Perform the last 3 functions evaluations (k14, k15, k16)
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void doFinalize()
throws DerivativeException {
double s;
// k14
for (int j = 0; j < currentState.length; ++j) {
s = k14_01 * yDotK[0][j] + k14_06 * yDotK[5][j] + k14_07 * yDotK[6][j]
+ k14_08 * yDotK[7][j] + k14_09 * yDotK[8][j] + k14_10 * yDotK[9][j]
+ k14_11 * yDotK[10][j] + k14_12 * yDotK[11][j] + k14_13 * yDotK[12][j];
yTmp[j] = currentState[j] + h * s;
}
equations.computeDerivatives(previousTime + c14 * h, yTmp, yDotKLast[0]);
// k15
for (int j = 0; j < currentState.length; ++j) {
s = k15_01 * yDotK[0][j] + k15_06 * yDotK[5][j] + k15_07 * yDotK[6][j]
+ k15_08 * yDotK[7][j] + k15_09 * yDotK[8][j] + k15_10 * yDotK[9][j]
+ k15_11 * yDotK[10][j] + k15_12 * yDotK[11][j] + k15_13 * yDotK[12][j]
+ k15_14 * yDotKLast[0][j];
yTmp[j] = currentState[j] + h * s;
}
equations.computeDerivatives(previousTime + c15 * h, yTmp, yDotKLast[1]);
// k16
for (int j = 0; j < currentState.length; ++j) {
s = k16_01 * yDotK[0][j] + k16_06 * yDotK[5][j] + k16_07 * yDotK[6][j]
+ k16_08 * yDotK[7][j] + k16_09 * yDotK[8][j] + k16_10 * yDotK[9][j]
+ k16_11 * yDotK[10][j] + k16_12 * yDotK[11][j] + k16_13 * yDotK[12][j]
+ k16_14 * yDotKLast[0][j] + k16_15 * yDotKLast[1][j];
yTmp[j] = currentState[j] + h * s;
}
equations.computeDerivatives(previousTime + c16 * h, yTmp, yDotKLast[2]);
}
/** Save the state of the instance.
* @param out stream where to save the state
* @exception IOException in case of write error
*/
public void writeExternal(ObjectOutput out)
throws IOException {
try {
// save the local attributes
finalizeStep();
} catch (DerivativeException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
out.writeInt(currentState.length);
for (int i = 0; i < currentState.length; ++i) {
out.writeDouble(yDotKLast[0][i]);
out.writeDouble(yDotKLast[1][i]);
out.writeDouble(yDotKLast[2][i]);
}
// save the state of the base class
super.writeExternal(out);
}
/** Read the state of the instance.
* @param in stream where to read the state from
* @exception IOException in case of read error
*/
public void readExternal(ObjectInput in)
throws IOException {
// read the local attributes
yDotKLast = new double[3][];
int dimension = in.readInt();
yDotKLast[0] = new double[dimension];
yDotKLast[1] = new double[dimension];
yDotKLast[2] = new double[dimension];
for (int i = 0; i < dimension; ++i) {
yDotKLast[0][i] = in.readDouble();
yDotKLast[1][i] = in.readDouble();
yDotKLast[2][i] = in.readDouble();
}
// read the base state
super.readExternal(in);
}
/** Last evaluations. */
private double[][] yDotKLast;
/** Temporary state vector. */
private double[] yTmp;
/** Vectors for interpolation. */
private double[][] v;
/** Initialization indicator for the interpolation vectors. */
private boolean vectorsInitialized;
// external weights of the integrator,
// note that b_02 through b_05 are null
private static double b_01 = 104257.0 / 1920240.0;
private static double b_06 = 3399327.0 / 763840.0;
private static double b_07 = 66578432.0 / 35198415.0;
private static double b_08 = -1674902723.0 / 288716400.0;
private static double b_09 = 54980371265625.0 / 176692375811392.0;
private static double b_10 = -734375.0 / 4826304.0;
private static double b_11 = 171414593.0 / 851261400.0;
private static double b_12 = 137909.0 / 3084480.0;
// k14 for interpolation only
private static double c14 = 1.0 / 10.0;
private static double k14_01 = 13481885573.0 / 240030000000.0 - b_01;
private static double k14_06 = 0.0 - b_06;
private static double k14_07 = 139418837528.0 / 549975234375.0 - b_07;
private static double k14_08 = -11108320068443.0 / 45111937500000.0 - b_08;
private static double k14_09 = -1769651421925959.0 / 14249385146080000.0 - b_09;
private static double k14_10 = 57799439.0 / 377055000.0 - b_10;
private static double k14_11 = 793322643029.0 / 96734250000000.0 - b_11;
private static double k14_12 = 1458939311.0 / 192780000000.0 - b_12;
private static double k14_13 = -4149.0 / 500000.0;
// k15 for interpolation only
private static double c15 = 1.0 / 5.0;
private static double k15_01 = 1595561272731.0 / 50120273500000.0 - b_01;
private static double k15_06 = 975183916491.0 / 34457688031250.0 - b_06;
private static double k15_07 = 38492013932672.0 / 718912673015625.0 - b_07;
private static double k15_08 = -1114881286517557.0 / 20298710767500000.0 - b_08;
private static double k15_09 = 0.0 - b_09;
private static double k15_10 = 0.0 - b_10;
private static double k15_11 = -2538710946863.0 / 23431227861250000.0 - b_11;
private static double k15_12 = 8824659001.0 / 23066716781250.0 - b_12;
private static double k15_13 = -11518334563.0 / 33831184612500.0;
private static double k15_14 = 1912306948.0 / 13532473845.0;
// k16 for interpolation only
private static double c16 = 7.0 / 9.0;
private static double k16_01 = -13613986967.0 / 31741908048.0 - b_01;
private static double k16_06 = -4755612631.0 / 1012344804.0 - b_06;
private static double k16_07 = 42939257944576.0 / 5588559685701.0 - b_07;
private static double k16_08 = 77881972900277.0 / 19140370552944.0 - b_08;
private static double k16_09 = 22719829234375.0 / 63689648654052.0 - b_09;
private static double k16_10 = 0.0 - b_10;
private static double k16_11 = 0.0 - b_11;
private static double k16_12 = 0.0 - b_12;
private static double k16_13 = -1199007803.0 / 857031517296.0;
private static double k16_14 = 157882067000.0 / 53564469831.0;
private static double k16_15 = -290468882375.0 / 31741908048.0;
// interpolation weights
// (beware that only the non-null values are in the table)
private static double[][] d = {
{ -17751989329.0 / 2106076560.0, 4272954039.0 / 7539864640.0,
-118476319744.0 / 38604839385.0, 755123450731.0 / 316657731600.0,
3692384461234828125.0 / 1744130441634250432.0, -4612609375.0 / 5293382976.0,
2091772278379.0 / 933644586600.0, 2136624137.0 / 3382989120.0,
-126493.0 / 1421424.0, 98350000.0 / 5419179.0,
-18878125.0 / 2053168.0, -1944542619.0 / 438351368.0},
{ 32941697297.0 / 3159114840.0, 456696183123.0 / 1884966160.0,
19132610714624.0 / 115814518155.0, -177904688592943.0 / 474986597400.0,
-4821139941836765625.0 / 218016305204281304.0, 30702015625.0 / 3970037232.0,
-85916079474274.0 / 2800933759800.0, -5919468007.0 / 634310460.0,
2479159.0 / 157936.0, -18750000.0 / 602131.0,
-19203125.0 / 2053168.0, 15700361463.0 / 438351368.0},
{ 12627015655.0 / 631822968.0, -72955222965.0 / 188496616.0,
-13145744952320.0 / 69488710893.0, 30084216194513.0 / 56998391688.0,
-296858761006640625.0 / 25648977082856624.0, 569140625.0 / 82709109.0,
-18684190637.0 / 18672891732.0, 69644045.0 / 89549712.0,
-11847025.0 / 4264272.0, -978650000.0 / 16257537.0,
519371875.0 / 6159504.0, 5256837225.0 / 438351368.0},
{ -450944925.0 / 17550638.0, -14532122925.0 / 94248308.0,
-595876966400.0 / 2573655959.0, 188748653015.0 / 527762886.0,
2545485458115234375.0 / 27252038150535163.0, -1376953125.0 / 36759604.0,
53995596795.0 / 518691437.0, 210311225.0 / 7047894.0,
-1718875.0 / 39484.0, 58000000.0 / 602131.0,
-1546875.0 / 39484.0, -1262172375.0 / 8429834.0}
};
private static final long serialVersionUID = 4165537490327432186L;
}

View File

@ -0,0 +1,92 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class is a step handler that do nothing.
* <p>This class is provided as a convenience for users who are only
* interested in the final state of an integration and not in the
* intermediate steps. Its handleStep method does nothing.</p>
* <p>Since this class has no internal state, it is implemented using
* the Singleton design pattern. This means that only one instance is
* ever created, which can be retrieved using the getInstance
* method. This explains why there is no public constructor.</p>
* @see StepHandler
* @version $Id: DummyStepHandler.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class DummyStepHandler
implements StepHandler {
/** Private constructor.
* The constructor is private to prevent users from creating
* instances (Singleton design-pattern).
*/
private DummyStepHandler() {
}
/** Get the only instance.
* @return the only instance
*/
public static DummyStepHandler getInstance() {
if (instance == null) {
instance = new DummyStepHandler();
}
return instance;
}
/** Determines whether this handler needs dense output.
* Since this handler does nothing, it does not require dense output.
* @return always false
*/
public boolean requiresDenseOutput() {
return false;
}
/** Reset the step handler.
* Initialize the internal data as required before the first step is
* handled.
*/
public void reset() {
}
/**
* Handle the last accepted step.
* This method does nothing in this class.
* @param interpolator interpolator for the last accepted step. For
* efficiency purposes, the various integrators reuse the same
* object on each call, so if the instance wants to keep it across
* all calls (for example to provide at the end of the integration a
* continuous model valid throughout the integration range), it
* should build a local copy using the clone method and store this
* copy.
* @param isLast true if the step is the last one
*/
public void handleStep(StepInterpolator interpolator, boolean isLast) {
}
/** The only instance. */
private static DummyStepHandler instance = null;
}

View File

@ -0,0 +1,134 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
/** This class is a step interpolator that does nothing.
*
* <p>This class is used when the {@link StepHandler "step handler"}
* set up by the user does not need step interpolation. It does not
* recompute the state when {@link AbstractStepInterpolator#setInterpolatedTime
* setInterpolatedTime} is called. This implies the interpolated state
* is always the state at the end of the current step.</p>
*
* @see StepHandler
*
* @version $Id: DummyStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
public class DummyStepInterpolator
extends AbstractStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#reinitialize} method should be called
* before using the instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. As an example, the {@link
* RungeKuttaFehlbergIntegrator} uses the prototyping design pattern
* to create the step interpolators by cloning an uninitialized
* model and latter initializing the copy.
*/
protected DummyStepInterpolator() {
super();
}
/** Simple constructor.
* @param y reference to the integrator array holding the state at
* the end of the step
* @param forward integration direction indicator
*/
protected DummyStepInterpolator(double[] y, boolean forward) {
super(y, forward);
}
/** Copy constructor.
* <p>The copied interpolator should have been finalized before the
* copy, otherwise the copy will not be able to perform correctly
* any interpolation and will throw a {@link NullPointerException}
* later. Since we don't want this constructor to throw the
* exceptions finalization may involve and since we don't want this
* method to modify the state of the copied interpolator,
* finalization is <strong>not</strong> done automatically, it
* remains under user control.</p>
* <p>The copy is a deep copy: its arrays are separated from the
* original arrays of the instance.</p>
* @param interpolator interpolator to copy from.
*/
protected DummyStepInterpolator(DummyStepInterpolator interpolator) {
super(interpolator);
}
/** Copy the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance.
*/
public Object clone() {
return new DummyStepInterpolator(this);
}
/** Compute the state at the interpolated time.
* In this class, this method does nothing: the interpolated state
* is always the state at the end of the current step.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta, double oneMinusThetaH)
throws DerivativeException {
}
public void writeExternal(ObjectOutput out)
throws IOException {
// save the state of the base class
writeBaseExternal(out);
}
public void readExternal(ObjectInput in)
throws IOException {
// read the base class
double t = readBaseExternal(in);
try {
// we can now set the interpolated time and state
setInterpolatedTime(t);
} catch (DerivativeException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
}
private static final long serialVersionUID = 1708010296707839488L;
}

View File

@ -0,0 +1,80 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a simple Euler integrator for Ordinary
* Differential Equations.
* <p>The Euler algorithm is the simplest one that can be used to
* integrate ordinary differential equations. It is a simple inversion
* of the forward difference expression :
* <code>f'=(f(t+h)-f(t))/h</code> which leads to
* <code>f(t+h)=f(t)+hf'</code>. The interpolation scheme used for
* dense output is the linear scheme already used for integration.</p>
* <p>This algorithm looks cheap because it needs only one function
* evaluation per step. However, as it uses linear estimates, it needs
* very small steps to achieve high accuracy, and small steps lead to
* numerical errors and instabilities.</p>
* <p>This algorithm is almost never used and has been included in
* this package only as a comparison reference for more useful
* integrators.</p>
* @see MidpointIntegrator
* @see ClassicalRungeKuttaIntegrator
* @see GillIntegrator
* @see ThreeEighthesIntegrator
* @version $Id: EulerIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class EulerIntegrator
extends RungeKuttaIntegrator {
private static final String methodName = new String("Euler");
private static final double[] c = {
};
private static final double[][] a = {
};
private static final double[] b = {
1.0
};
/** Simple constructor.
* Build an Euler integrator with the given step.
* @param step integration step
*/
public EulerIntegrator(double step) {
super(false, c, a, b, new EulerStepInterpolator(), step);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
}

View File

@ -0,0 +1,97 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a linear interpolator for step.
* <p>This interpolator allow to compute dense output inside the last
* step computed. The interpolation equation is consistent with the
* integration scheme :
* <pre>
* y(t_n + theta h) = y (t_n + h) - (1-theta) h y'
* </pre>
* where theta belongs to [0 ; 1] and where y' is the evaluation of
* the derivatives already computed during the step.</p>
* @see EulerIntegrator
* @version $Id: EulerStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class EulerStepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#reinitialize} method should be called
* before using the instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaIntegrator} class uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public EulerStepInterpolator() {
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public EulerStepInterpolator(EulerStepInterpolator interpolator) {
super(interpolator);
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new EulerStepInterpolator(this);
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i] - oneMinusThetaH * yDotK[0][i];
}
}
private static final long serialVersionUID = -7179861704951334960L;
}

View File

@ -0,0 +1,108 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/** This class converts second order differential equations to first
* order ones.
* <p>This class is a wrapper around a {@link
* SecondOrderDifferentialEquations} which allow to use a {@link
* FirstOrderIntegrator} to integrate it.</p>
* <p>The transformation is done by changing the n dimension state
* vector to a 2n dimension vector, where the first n components are
* the initial state variables and the n last components are their
* first time derivative. The first time derivative of this state
* vector then really contains both the first and second time
* derivative of the initial state vector, which can be handled by the
* underlying second order equations set.</p>
* <p>One should be aware that the data is duplicated during the
* transformation process and that for each call to {@link
* #computeDerivatives computeDerivatives}, this wrapper does copy 4n
* scalars : 2n before the call to {@link
* SecondOrderDifferentialEquations#computeSecondDerivatives
* computeSecondDerivatives} in order to dispatch the y state vector
* into z and zDot, and 2n after the call to gather zDot and zDDot
* into yDot. Since the underlying problem by itself perhaps also
* needs to copy data and dispatch the arrays into domain objects,
* this has an impact on both memory and CPU usage. The only way to
* avoid this duplication is to perform the transformation at the
* problem level, i.e. to implement the problem as a first order one
* and then avoid using this class.</p>
* @see FirstOrderIntegrator
* @see FirstOrderDifferentialEquations
* @see SecondOrderDifferentialEquations
* @version $Id: FirstOrderConverter.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class FirstOrderConverter
implements FirstOrderDifferentialEquations {
/** Simple constructor.
* Build a converter around a second order equations set.
* @param equations second order equations set to convert
*/
public FirstOrderConverter (SecondOrderDifferentialEquations equations) {
this.equations = equations;
dimension = equations.getDimension();
z = new double[dimension];
zDot = new double[dimension];
zDDot = new double[dimension];
}
public int getDimension() {
return 2 * dimension;
}
public void computeDerivatives(double t, double[] y, double[] yDot)
throws DerivativeException {
// split the state vector in two
System.arraycopy(y, 0, z, 0, dimension);
System.arraycopy(y, dimension, zDot, 0, dimension);
// apply the underlying equations set
equations.computeSecondDerivatives(t, z, zDot, zDDot);
// build the result state derivative
System.arraycopy(zDot, 0, yDot, 0, dimension);
System.arraycopy(zDDot, 0, yDot, dimension, dimension);
}
/** Underlying second order equations set. */
private SecondOrderDifferentialEquations equations;
/** second order problem dimension. */
private int dimension;
/** state vector. */
private double[] z;
/** first time derivative of the state vector. */
private double[] zDot;
/** second time derivative of the state vector. */
private double[] zDDot;
}

View File

@ -0,0 +1,66 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/** This interface represents a first order differential equations set.
*
* <p>This interface should be implemented by all real first order
* differential equation problems before they can be handled by the
* integrators {@link FirstOrderIntegrator#integrate} method.</p>
*
* <p>A first order differential equations problem, as seen by an
* integrator is the time derivative <code>dY/dt</code> of a state
* vector <code>Y</code>, both being one dimensional arrays. From the
* integrator point of view, this derivative depends only on the
* current time <code>t</code> and on the state vector
* <code>Y</code>.</p>
*
* <p>For real problems, the derivative depends also on parameters
* that do not belong to the state vector (dynamical model constants
* for example). These constants are completely outside of the scope
* of this interface, the classes that implement it are allowed to
* handle them as they want.</p>
*
* @see FirstOrderIntegrator
* @see FirstOrderConverter
* @see SecondOrderDifferentialEquations
* @see org.spaceroots.mantissa.utilities.ArraySliceMappable
*
* @version $Id: FirstOrderDifferentialEquations.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
public interface FirstOrderDifferentialEquations {
/** Get the dimension of the problem.
* @return dimension of the problem
*/
public int getDimension();
/** Get the current time derivative of the state vector.
* @param t current value of the independant <I>time</I> variable
* @param y array containing the current value of the state vector
* @param yDot placeholder array where to put the time derivative of the state vector
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
public void computeDerivatives(double t, double[] y, double[] yDot)
throws DerivativeException;
}

View File

@ -0,0 +1,84 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/** This interface represents a first order integrator for
* differential equations.
* <p>The classes which are devoted to solve first order differential
* equations should implement this interface. The problems which can
* be handled should implement the {@link
* FirstOrderDifferentialEquations} interface.</p>
* @see FirstOrderDifferentialEquations
* @see StepHandler
* @see SwitchingFunction
* @version $Id: FirstOrderIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface FirstOrderIntegrator {
/** Get the name of the method.
* @return name of the method
*/
public String getName();
/** Set the step handler for this integrator.
* The handler will be called by the integrator for each accepted
* step.
* @param handler handler for the accepted steps
*/
public void setStepHandler (StepHandler handler);
/** Get the step handler for this integrator.
* @return the step handler for this integrator
*/
public StepHandler getStepHandler();
/** Add a switching function to the integrator.
* @param function switching function
* @param maxCheckInterval maximal time interval between switching
* function checks (this interval prevents missing sign changes in
* case the integration steps becomes very large)
* @param convergence convergence threshold in the event time search
*/
public void addSwitchingFunction(SwitchingFunction function,
double maxCheckInterval,
double convergence);
/** Integrate the differential equations up to the given time
* @param equations differential equations to integrate
* @param t0 initial time
* @param y0 initial value of the state vector at t0
* @param t target time for the integration
* (can be set to a value smaller thant <code>t0</code> for backward integration)
* @param y placeholder where to put the state vector at each successful
* step (and hence at the end of integration), can be the same object as y0
* @throws IntegratorException if the integrator cannot perform integration
* @throws DerivativeException this exception is propagated to the caller if
* the underlying user function triggers one
*/
public void integrate (FirstOrderDifferentialEquations equations,
double t0, double[] y0,
double t, double[] y)
throws DerivativeException, IntegratorException;
}

View File

@ -0,0 +1,57 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This interface represents a handler that should be called after
* each successful fixed step.
* <p>This interface should be implemented by anyone who is interested
* in getting the solution of an ordinary differential equation at
* fixed time steps. Objects implementing this interface should be
* wrapped within an instance of {@link StepNormalizer} that itself
* is used as the general {@link StepHandler} by the integrator. The
* {@link StepNormalizer} object is called according to the integrator
* internal algorithms and it calls objects implementing this
* interface as necessary at fixed time steps.</p>
* @see StepHandler
* @see StepNormalizer
* @version $Id: FixedStepHandler.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public interface FixedStepHandler {
/**
* Handle the last accepted step
* @param t time of the current step
* @param y state vector at t. For efficiency purposes, the {@link
* StepNormalizer} class reuse the same array on each call, so if
* the instance wants to keep it across all calls (for example to
* provide at the end of the integration a complete array of all
* steps), it should build a local copy store this copy.
* @param isLast true if the step is the last one
*/
public void handleStep(double t, double[] y, boolean isLast);
}

View File

@ -0,0 +1,82 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the Gill fourth order Runge-Kutta
* integrator for Ordinary Differential Equations .
* <p>This method is an explicit Runge-Kutta method, its Butcher-array
* is the following one :
* <pre>
* 0 | 0 0 0 0
* 1/2 | 1/2 0 0 0
* 1/2 | (q-1)/2 (2-q)/2 0 0
* 1 | 0 -q/2 (2+q)/2 0
* |-------------------------------
* | 1/6 (2-q)/6 (2+q)/6 1/6
* </pre>
* where q = sqrt(2)</p>
* @see EulerIntegrator
* @see ClassicalRungeKuttaIntegrator
* @see MidpointIntegrator
* @see ThreeEighthesIntegrator
* @version $Id: GillIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class GillIntegrator
extends RungeKuttaIntegrator {
private static final String methodName = new String("Gill");
private static final double sqrt2 = Math.sqrt(2.0);
private static final double[] c = {
1.0 / 2.0, 1.0 / 2.0, 1.0
};
private static final double[][] a = {
{ 1.0 / 2.0 },
{ (sqrt2 - 1.0) / 2.0, (2.0 - sqrt2) / 2.0 },
{ 0.0, -sqrt2 / 2.0, (2.0 + sqrt2) / 2.0 }
};
private static final double[] b = {
1.0 / 6.0, (2.0 - sqrt2) / 6.0, (2.0 + sqrt2) / 6.0, 1.0 / 6.0
};
/** Simple constructor.
* Build a fourth-order Gill integrator with the given step.
* @param step integration step
*/
public GillIntegrator(double step) {
super(false, c, a, b, new GillStepInterpolator(), step);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
}

View File

@ -0,0 +1,119 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a step interpolator for the Gill fourth
* order Runge-Kutta integrator.
* <p>This interpolator allows to compute dense output inside the last
* step computed. The interpolation equation is consistent with the
* integration scheme :
* <pre>
* y(t_n + theta h) = y (t_n + h)
* - (1 - theta) (h/6) [ (1 - theta) (1 - 4 theta) y'_1
* + (1 - theta) (1 + 2 theta) ((2-q) y'_2 + (2+q) y'_3)
* + (1 + theta + 4 theta^2) y'_4
* ]
* </pre>
* where theta belongs to [0 ; 1], q = sqrt(2) and where y'_1 to y'_4
* are the four evaluations of the derivatives already computed during
* the step.</p>
* @see GillIntegrator
* @version $Id: GillStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class GillStepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#reinitialize} method should be called
* before using the instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaIntegrator} class uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public GillStepInterpolator() {
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public GillStepInterpolator(GillStepInterpolator interpolator) {
super(interpolator);
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new GillStepInterpolator(this);
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
double fourTheta = 4 * theta;
double s = oneMinusThetaH / 6.0;
double soMt = s * (1 - theta);
double c23 = soMt * (1 + 2 * theta);
double coeff1 = soMt * (1 - fourTheta);
double coeff2 = c23 * tMq;
double coeff3 = c23 * tPq;
double coeff4 = s * (1 + theta * (1 + fourTheta));
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
- coeff1 * yDotK[0][i] - coeff2 * yDotK[1][i]
- coeff3 * yDotK[2][i] - coeff4 * yDotK[3][i];
}
}
/** First Gill coefficient. */
private static final double tMq = 2 - Math.sqrt(2.0);
/** Second Gill coefficient. */
private static final double tPq = 2 + Math.sqrt(2.0);
private static final long serialVersionUID = -107804074496313322L;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,404 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
/**
* This class implements an interpolator for the Gragg-Bulirsch-Stoer
* integrator.
* <p>This interpolator compute dense output inside the last step
* produced by a Gragg-Bulirsch-Stoer integrator.</p>
* <p>
* This implementation is basically a reimplementation in Java of the
* <a
* href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
* fortran code by E. Hairer and G. Wanner. The redistribution policy
* for this code is available <a
* href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
* convenience, it is reproduced below.</p>
* </p>
* <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
* <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
* <tr><td>Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
* <ul>
* <li>Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.</li>
* <li>Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.</li>
* </ul></td></tr>
* <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
* </table>
* @see GraggBulirschStoerIntegrator
* @version $Id: GraggBulirschStoerStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author E. Hairer and G. Wanner (fortran version)
* @author L. Maisonobe (Java port)
*/
class GraggBulirschStoerStepInterpolator
extends AbstractStepInterpolator {
/** Slope at the beginning of the step. */
private double[] y0Dot;
/** State at the end of the step. */
private double[] y1;
/** Slope at the end of the step. */
private double[] y1Dot;
/** Derivatives at the middle of the step.
* element 0 is state at midpoint, element 1 is first derivative ...
*/
private double[][] yMidDots;
/** Interpolation polynoms. */
private double[][] polynoms;
/** Error coefficients for the interpolation. */
private double[] errfac;
/** Degree of the interpolation polynoms. */
private int currentDegree;
/** Reallocate the internal tables.
* Reallocate the internal tables in order to be able to handle
* interpolation polynoms up to the given degree
* @param maxDegree maximal degree to handle
*/
private void resetTables(int maxDegree) {
if (maxDegree < 0) {
polynoms = null;
errfac = null;
currentDegree = -1;
} else {
double[][] newPols = new double[maxDegree + 1][];
if (polynoms != null) {
System.arraycopy(polynoms, 0, newPols, 0, polynoms.length);
for (int i = polynoms.length; i < newPols.length; ++i) {
newPols[i] = new double[currentState.length];
}
} else {
for (int i = 0; i < newPols.length; ++i) {
newPols[i] = new double[currentState.length];
}
}
polynoms = newPols;
// initialize the error factors array for interpolation
if (maxDegree <= 4) {
errfac = null;
} else {
errfac = new double[maxDegree - 4];
for (int i = 0; i < errfac.length; ++i) {
int ip5 = i + 5;
errfac[i] = 1.0 / (ip5 * ip5);
double e = 0.5 * Math.sqrt (((double) (i + 1)) / ip5);
for (int j = 0; j <= i; ++j) {
errfac[i] *= e / (j + 1);
}
}
}
currentDegree = 0;
}
}
/** Simple constructor.
* This constructor should not be used directly, it is only intended
* for the serialization process.
*/
public GraggBulirschStoerStepInterpolator() {
y0Dot = null;
y1 = null;
y1Dot = null;
yMidDots = null;
resetTables(-1);
}
/** Simple constructor.
* @param y reference to the integrator array holding the current state
* @param y0Dot reference to the integrator array holding the slope
* at the beginning of the step
* @param y1 reference to the integrator array holding the state at
* the end of the step
* @param y1Dot reference to the integrator array holding the slope
* at theend of the step
* @param yMidDots reference to the integrator array holding the
* derivatives at the middle point of the step
* @param forward integration direction indicator
*/
public GraggBulirschStoerStepInterpolator(double[] y, double[] y0Dot,
double[] y1, double[] y1Dot,
double[][] yMidDots,
boolean forward) {
super(y, forward);
this.y0Dot = y0Dot;
this.y1 = y1;
this.y1Dot = y1Dot;
this.yMidDots = yMidDots;
resetTables(yMidDots.length + 4);
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public GraggBulirschStoerStepInterpolator
(GraggBulirschStoerStepInterpolator interpolator) {
super(interpolator);
int dimension = currentState.length;
// the interpolator has been finalized,
// the following arrays are not needed anymore
y0Dot = null;
y1 = null;
y1Dot = null;
yMidDots = null;
// copy the interpolation polynoms (up to the current degree only)
if (interpolator.polynoms == null) {
polynoms = null;
currentDegree = -1;
} else {
resetTables(interpolator.currentDegree);
for (int i = 0; i < polynoms.length; ++i) {
polynoms[i] = new double[dimension];
System.arraycopy(interpolator.polynoms[i], 0,
polynoms[i], 0, dimension);
}
currentDegree = interpolator.currentDegree;
}
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new GraggBulirschStoerStepInterpolator(this);
}
/** Compute the interpolation coefficients for dense output.
* @param mu degree of the interpolation polynom
* @param h current step
*/
public void computeCoefficients(int mu, double h) {
if ((polynoms == null) || (polynoms.length <= (mu + 4))) {
resetTables(mu + 4);
}
currentDegree = mu + 4;
for (int i = 0; i < currentState.length; ++i) {
double yp0 = h * y0Dot[i];
double yp1 = h * y1Dot[i];
double ydiff = y1[i] - currentState[i];
double aspl = ydiff - yp1;
double bspl = yp0 - ydiff;
polynoms[0][i] = currentState[i];
polynoms[1][i] = ydiff;
polynoms[2][i] = aspl;
polynoms[3][i] = bspl;
if (mu < 0) {
return;
}
// compute the remaining coefficients
double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
polynoms[4][i] = 16 * (yMidDots[0][i] - ph0);
if (mu > 0) {
double ph1 = ydiff + 0.25 * (aspl - bspl);
polynoms[5][i] = 16 * (yMidDots[1][i] - ph1);
if (mu > 1) {
double ph2 = yp1 - yp0;
polynoms[6][i] = 16 * (yMidDots[2][i] - ph2 + polynoms[4][i]);
if (mu > 2) {
double ph3 = 6 * (bspl - aspl);
polynoms[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynoms[5][i]);
for (int j = 4; j <= mu; ++j) {
double fac1 = 0.5 * j * (j - 1);
double fac2 = 2 * fac1 * (j - 2) * (j - 3);
polynoms[j+4][i] = 16 * (yMidDots[j][i]
+ fac1 * polynoms[j+2][i]
- fac2 * polynoms[j][i]);
}
}
}
}
}
}
/** Estimate interpolation error.
* @param scale scaling array
* @return estimate of the interpolation error
*/
public double estimateError(double[] scale) {
double error = 0;
if (currentDegree >= 5) {
for (int i = 0; i < currentState.length; ++i) {
double e = polynoms[currentDegree][i] / scale[i];
error += e * e;
}
error = Math.sqrt(error / currentState.length) * errfac[currentDegree-5];
}
return error;
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
int dimension = currentState.length;
double oneMinusTheta = 1.0 - theta;
double theta05 = theta - 0.5;
double t4 = theta * oneMinusTheta;
t4 = t4 * t4;
for (int i = 0; i < dimension; ++i) {
interpolatedState[i] = polynoms[0][i]
+ theta * (polynoms[1][i]
+ oneMinusTheta * (polynoms[2][i] * theta
+ polynoms[3][i] * oneMinusTheta));
if (currentDegree > 3) {
double c = polynoms[currentDegree][i];
for (int j = currentDegree - 1; j > 3; --j) {
c = polynoms[j][i] + c * theta05 / (j - 3);
}
interpolatedState[i] += t4 * c;
}
}
}
/** Save the state of the instance.
* @param out stream where to save the state
* @exception IOException in case of write error
*/
public void writeExternal(ObjectOutput out)
throws IOException {
int dimension = currentState.length;
// save the state of the base class
writeBaseExternal(out);
// save the local attributes (but not the temporary vectors)
out.writeInt(currentDegree);
for (int k = 0; k <= currentDegree; ++k) {
for (int l = 0; l < dimension; ++l) {
out.writeDouble(polynoms[k][l]);
}
}
}
/** Read the state of the instance.
* @param in stream where to read the state from
* @exception IOException in case of read error
*/
public void readExternal(ObjectInput in)
throws IOException {
// read the base class
double t = readBaseExternal(in);
int dimension = currentState.length;
// read the local attributes
int degree = in.readInt();
resetTables(degree);
currentDegree = degree;
for (int k = 0; k <= currentDegree; ++k) {
for (int l = 0; l < dimension; ++l) {
polynoms[k][l] = in.readDouble();
}
}
try {
// we can now set the interpolated time and state
setInterpolatedTime(t);
} catch (DerivativeException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
}
private static final long serialVersionUID = 7320613236731409847L;
}

View File

@ -0,0 +1,139 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the 5(4) Higham and Hall integrator for
* Ordinary Differential Equations.
* <p>This integrator is an embedded Runge-Kutta-Fehlberg integrator
* of order 5(4) used in local extrapolation mode (i.e. the solution
* is computed using the high order formula) with stepsize control
* (and automatic step initialization) and continuous output. This
* method uses 7 functions evaluations per step.</p>
* @version $Id: HighamHall54Integrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class HighamHall54Integrator
extends RungeKuttaFehlbergIntegrator {
private static final String methodName = new String("Higham-Hall 5(4)");
private static final double[] c = {
2.0/9.0, 1.0/3.0, 1.0/2.0, 3.0/5.0, 1.0, 1.0
};
private static final double[][] a = {
{2.0/9.0},
{1.0/12.0, 1.0/4.0},
{1.0/8.0, 0.0, 3.0/8.0},
{91.0/500.0, -27.0/100.0, 78.0/125.0, 8.0/125.0},
{-11.0/20.0, 27.0/20.0, 12.0/5.0, -36.0/5.0, 5.0},
{1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0}
};
private static final double[] b = {
1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0, 0.0
};
private static final double[] e = {
-1.0/20.0, 0.0, 81.0/160.0, -6.0/5.0, 25.0/32.0, 1.0/16.0, -1.0/10.0
};
/** Simple constructor.
* Build a fifth order Higham and Hall integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param scalAbsoluteTolerance allowed absolute error
* @param scalRelativeTolerance allowed relative error
*/
public HighamHall54Integrator(double minStep, double maxStep,
double scalAbsoluteTolerance,
double scalRelativeTolerance) {
super(false, c, a, b, new HighamHall54StepInterpolator(),
minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
}
/** Simple constructor.
* Build a fifth order Higham and Hall integrator with the given step bounds
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param vecAbsoluteTolerance allowed absolute error
* @param vecRelativeTolerance allowed relative error
*/
public HighamHall54Integrator(double minStep, double maxStep,
double[] vecAbsoluteTolerance,
double[] vecRelativeTolerance) {
super(false, c, a, b, new HighamHall54StepInterpolator(),
minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
/** Get the order of the method.
* @return order of the method
*/
public int getOrder() {
return 5;
}
/** Compute the error ratio.
* @param yDotK derivatives computed during the first stages
* @param y0 estimate of the step at the start of the step
* @param y1 estimate of the step at the end of the step
* @param h current step
* @return error ratio, greater than 1 if step should be rejected
*/
protected double estimateError(double[][] yDotK,
double[] y0, double[] y1,
double h) {
double error = 0;
for (int j = 0; j < y0.length; ++j) {
double errSum = e[0] * yDotK[0][j];
for (int l = 1; l < e.length; ++l) {
errSum += e[l] * yDotK[l][j];
}
double yScale = Math.max(Math.abs(y0[j]), Math.abs(y1[j]));
double tol = (vecAbsoluteTolerance == null)
? (scalAbsoluteTolerance + scalRelativeTolerance * yScale)
: (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
double ratio = h * errSum / tol;
error += ratio * ratio;
}
return Math.sqrt(error / y0.length);
}
}

View File

@ -0,0 +1,96 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class represents an interpolator over the last step during an
* ODE integration for the 5(4) Higham and Hall integrator.
*
* @see HighamHall54Integrator
*
* @version $Id: HighamHall54StepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*
*/
class HighamHall54StepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#reinitialize} method should be called
* before using the instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaFehlbergIntegrator} uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public HighamHall54StepInterpolator() {
super();
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public HighamHall54StepInterpolator(HighamHall54StepInterpolator interpolator) {
super(interpolator);
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new HighamHall54StepInterpolator(this);
}
/** Compute the state at the interpolated time.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
double theta2 = theta * theta;
double b0 = h * (-1.0/12.0 + theta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 + theta * -5.0/2.0))));
double b2 = h * (-27.0/32.0 + theta2 * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0)));
double b3 = h * (4.0/3.0 + theta2 * (-22.0 + theta * (152.0/3.0 + theta * -30.0)));
double b4 = h * (-125.0/96.0 + theta2 * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0)));
double b5 = h * (-5.0/48.0 + theta2 * (-5.0/16.0 + theta * 5.0/12.0));
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
+ b0 * yDotK[0][i] + b2 * yDotK[2][i] + b3 * yDotK[3][i]
+ b4 * yDotK[4][i] + b5 * yDotK[5][i];
}
}
private static final long serialVersionUID = -3583240427587318654L;
}

View File

@ -0,0 +1,42 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
import org.spaceroots.mantissa.MantissaException;
/**
* This exception is made available to users to report
* the error conditions that are triggered during integration
* @author Luc Maisonobe
* @version $Id: IntegratorException.java 1705 2006-09-17 19:57:39Z luc $
*/
public class IntegratorException
extends MantissaException {
/** Simple constructor.
* Build an exception by translating and formating a message
* @param specifier format specifier (to be translated)
* @param parts to insert in the format (no translation)
*/
public IntegratorException(String specifier, String[] parts) {
super(specifier, parts);
}
private static final long serialVersionUID = -1390328069787882608L;
}

View File

@ -0,0 +1,75 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a second order Runge-Kutta integrator for
* Ordinary Differential Equations.
* <p>This method is an explicit Runge-Kutta method, its Butcher-array
* is the following one :
* <pre>
* 0 | 0 0
* 1/2 | 1/2 0
* |----------
* | 0 1
* </pre>
* </p>
* @see EulerIntegrator
* @see ClassicalRungeKuttaIntegrator
* @see GillIntegrator
* @version $Id: MidpointIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public class MidpointIntegrator
extends RungeKuttaIntegrator {
private static final String methodName = new String("midpoint");
private static final double[] c = {
1.0 / 2.0
};
private static final double[][] a = {
{ 1.0 / 2.0 }
};
private static final double[] b = {
0.0, 1.0
};
/** Simple constructor.
* Build a midpoint integrator with the given step.
* @param step integration step
*/
public MidpointIntegrator(double step) {
super(false, c, a, b, new MidpointStepInterpolator(), step);
}
/** Get the name of the method.
* @return name of the method
*/
public String getName() {
return methodName;
}
}

View File

@ -0,0 +1,103 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements a step interpolator for second order
* Runge-Kutta integrator.
* <p>This interpolator allow to compute dense output inside the last
* step computed. The interpolation equation is consistent with the
* integration scheme :
* <pre>
* y(t_n + theta h) = y (t_n + h) + (1-theta) h [theta y'_1 - (1+theta) y'_2]
* </pre>
* where theta belongs to [0 ; 1] and where y'_1 and y'_2 are the two
* evaluations of the derivatives already computed during the
* step.</p>
* @see MidpointIntegrator
* @version $Id: MidpointStepInterpolator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
class MidpointStepInterpolator
extends RungeKuttaStepInterpolator {
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#reinitialize} method should be called
* before using the instance in order to initialize the internal arrays. This
* constructor is used only in order to delay the initialization in
* some cases. The {@link RungeKuttaIntegrator} class uses the
* prototyping design pattern to create the step interpolators by
* cloning an uninitialized model and latter initializing the copy.
*/
public MidpointStepInterpolator() {
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public MidpointStepInterpolator(MidpointStepInterpolator interpolator) {
super(interpolator);
}
/**
* Clone the instance.
* the copy is a deep copy: its arrays are separated from the
* original arrays of the instance
* @return a copy of the instance
*/
public Object clone() {
return new MidpointStepInterpolator(this);
}
/** Compute the state at the interpolated time.
* This is the main processing method that should be implemented by
* the derived classes to perform the interpolation.
* @param theta normalized interpolation abscissa within the step
* (theta is zero at the previous time step and one at the current time step)
* @param oneMinusThetaH time gap between the interpolated time and
* the current time
* @throws DerivativeException this exception is propagated to the caller if the
* underlying user function triggers one
*/
protected void computeInterpolatedState(double theta,
double oneMinusThetaH)
throws DerivativeException {
double coeff1 = oneMinusThetaH * theta;
double coeff2 = oneMinusThetaH * (1.0 + theta);
for (int i = 0; i < interpolatedState.length; ++i) {
interpolatedState[i] = currentState[i]
+ coeff1 * yDotK[0][i] - coeff2 * yDotK[1][i];
}
}
private static final long serialVersionUID = -865524111506042509L;
}

View File

@ -0,0 +1,397 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the common part of all Runge-Kutta-Fehlberg
* integrators for Ordinary Differential Equations.
* <p>These methods are embedded explicit Runge-Kutta methods with two
* sets of coefficients allowing to estimate the error, their Butcher
* arrays are as follows :
* <pre>
* 0 |
* c2 | a21
* c3 | a31 a32
* ... | ...
* cs | as1 as2 ... ass-1
* |--------------------------
* | b1 b2 ... bs-1 bs
* | b'1 b'2 ... b's-1 b's
* </pre>
* </p>
* <p>In fact, we rather use the array defined by ej = bj - b'j to
* compute directly the error rather than computing two estimates and
* then comparing them.</p>
* <p>Some methods are qualified as <i>fsal</i> (first same as last)
* methods. This means the last evaluation of the derivatives in one
* step is the same as the first in the next step. Then, this
* evaluation can be reused from one step to the next one and the cost
* of such a method is really s-1 evaluations despite the method still
* has s stages. This behaviour is true only for successful steps, if
* the step is rejected after the error estimation phase, no
* evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
* asi = bi for all i.</p>
* @version $Id: RungeKuttaFehlbergIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class RungeKuttaFehlbergIntegrator
extends AdaptiveStepsizeIntegrator {
/** Build a Runge-Kutta integrator with the given Butcher array.
* @param fsal indicate that the method is an <i>fsal</i>
* @param c time steps from Butcher array (without the first zero)
* @param a internal weights from Butcher array (without the first empty row)
* @param b external weights for the high order method from Butcher array
* @param prototype prototype of the step interpolator to use
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param scalAbsoluteTolerance allowed absolute error
* @param scalRelativeTolerance allowed relative error
*/
protected RungeKuttaFehlbergIntegrator(boolean fsal,
double[] c, double[][] a, double[] b,
RungeKuttaStepInterpolator prototype,
double minStep, double maxStep,
double scalAbsoluteTolerance,
double scalRelativeTolerance) {
super(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
this.fsal = fsal;
this.c = c;
this.a = a;
this.b = b;
this.prototype = prototype;
exp = -1.0 / getOrder();
this.safety = 0.9;
// set the default values of the algorithm control parameters
setMinReduction(0.2);
setMaxGrowth(10.0);
}
/** Build a Runge-Kutta integrator with the given Butcher array.
* @param fsal indicate that the method is an <i>fsal</i>
* @param c time steps from Butcher array (without the first zero)
* @param a internal weights from Butcher array (without the first empty row)
* @param b external weights for the high order method from Butcher array
* @param prototype prototype of the step interpolator to use
* @param minStep minimal step (must be positive even for backward
* integration), the last step can be smaller than this
* @param maxStep maximal step (must be positive even for backward
* integration)
* @param vecAbsoluteTolerance allowed absolute error
* @param vecRelativeTolerance allowed relative error
*/
protected RungeKuttaFehlbergIntegrator(boolean fsal,
double[] c, double[][] a, double[] b,
RungeKuttaStepInterpolator prototype,
double minStep, double maxStep,
double[] vecAbsoluteTolerance,
double[] vecRelativeTolerance) {
super(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
this.fsal = fsal;
this.c = c;
this.a = a;
this.b = b;
this.prototype = prototype;
exp = -1.0 / getOrder();
this.safety = 0.9;
// set the default values of the algorithm control parameters
setMinReduction(0.2);
setMaxGrowth(10.0);
}
/** Get the name of the method.
* @return name of the method
*/
public abstract String getName();
/** Get the order of the method.
* @return order of the method
*/
public abstract int getOrder();
/** Get the safety factor for stepsize control.
* @return safety factor
*/
public double getSafety() {
return safety;
}
/** Set the safety factor for stepsize control.
* @param safety safety factor
*/
public void setSafety(double safety) {
this.safety = safety;
}
public void integrate(FirstOrderDifferentialEquations equations,
double t0, double[] y0,
double t, double[] y)
throws DerivativeException, IntegratorException {
// sanity check
if (equations.getDimension() != y0.length) {
throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0},"
+ " state vector has dimension {1}",
new String[] {
Integer.toString(equations.getDimension()),
Integer.toString(y0.length)
});
}
if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) {
throw new IntegratorException("too small integration interval: length = {0}",
new String[] {
Double.toString(Math.abs(t - t0))
});
}
boolean forward = (t > t0);
// create some internal working arrays
int stages = c.length + 1;
if (y != y0) {
System.arraycopy(y0, 0, y, 0, y0.length);
}
double[][] yDotK = new double[stages][];
for (int i = 0; i < stages; ++i) {
yDotK [i] = new double[y0.length];
}
double[] yTmp = new double[y0.length];
// set up an interpolator sharing the integrator arrays
AbstractStepInterpolator interpolator;
if (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())) {
RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.clone();
rki.reinitialize(equations, yTmp, yDotK, forward);
interpolator = rki;
} else {
interpolator = new DummyStepInterpolator(yTmp, forward);
}
interpolator.storeTime(t0);
double currentT = t0;
double hNew = 0;
boolean firstTime = true;
boolean lastStep;
handler.reset();
do {
interpolator.shift();
double h = 0;
double error = 0;
for (boolean loop = true; loop;) {
if (firstTime || !fsal) {
// first stage
equations.computeDerivatives(currentT, y, yDotK[0]);
}
if (firstTime) {
double[] scale;
if (vecAbsoluteTolerance != null) {
scale = vecAbsoluteTolerance;
} else {
scale = new double[y0.length];
for (int i = 0; i < scale.length; ++i) {
scale[i] = scalAbsoluteTolerance;
}
}
hNew = initializeStep(equations, forward, getOrder(), scale,
currentT, y, yDotK[0], yTmp, yDotK[1]);
firstTime = false;
}
h = hNew;
// step adjustment near bounds
if ((forward && (currentT + h > t))
|| ((! forward) && (currentT + h < t))) {
h = t - currentT;
}
// next stages
for (int k = 1; k < stages; ++k) {
for (int j = 0; j < y0.length; ++j) {
double sum = a[k-1][0] * yDotK[0][j];
for (int l = 1; l < k; ++l) {
sum += a[k-1][l] * yDotK[l][j];
}
yTmp[j] = y[j] + h * sum;
}
equations.computeDerivatives(currentT + c[k-1] * h, yTmp, yDotK[k]);
}
// estimate the state at the end of the step
for (int j = 0; j < y0.length; ++j) {
double sum = b[0] * yDotK[0][j];
for (int l = 1; l < stages; ++l) {
sum += b[l] * yDotK[l][j];
}
yTmp[j] = y[j] + h * sum;
}
// estimate the error at the end of the step
error = estimateError(yDotK, y, yTmp, h);
if (error <= 1.0) {
// Switching functions handling
interpolator.storeTime(currentT + h);
if (switchesHandler.evaluateStep(interpolator)) {
// reject the step to match exactly the next switch time
hNew = switchesHandler.getEventTime() - currentT;
} else {
// accept the step
loop = false;
}
} else {
// reject the step and attempt to reduce error by stepsize control
double factor = Math.min(maxGrowth,
Math.max(minReduction,
safety * Math.pow(error, exp)));
hNew = filterStep(h * factor, false);
}
}
// the step has been accepted
currentT += h;
System.arraycopy(yTmp, 0, y, 0, y0.length);
switchesHandler.stepAccepted(currentT, y);
if (switchesHandler.stop()) {
lastStep = true;
} else {
lastStep = forward ? (currentT >= t) : (currentT <= t);
}
// provide the step data to the step handler
interpolator.storeTime(currentT);
handler.handleStep(interpolator, lastStep);
if (fsal) {
// save the last evaluation for the next step
System.arraycopy(yDotK[stages - 1], 0, yDotK[0], 0, y0.length);
}
switchesHandler.reset(currentT, y);
if (! lastStep) {
// stepsize control for next step
double factor = Math.min(maxGrowth,
Math.max(minReduction,
safety * Math.pow(error, exp)));
double scaledH = h * factor;
double nextT = currentT + scaledH;
boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
hNew = filterStep(scaledH, nextIsLast);
}
} while (! lastStep);
}
/** Get the minimal reduction factor for stepsize control.
* @return minimal reduction factor
*/
public double getMinReduction() {
return minReduction;
}
/** Set the minimal reduction factor for stepsize control.
* @param minReduction minimal reduction factor
*/
public void setMinReduction(double minReduction) {
this.minReduction = minReduction;
}
/** Get the maximal growth factor for stepsize control.
* @return maximal growth factor
*/
public double getMaxGrowth() {
return maxGrowth;
}
/** Set the maximal growth factor for stepsize control.
* @param maxGrowth maximal growth factor
*/
public void setMaxGrowth(double maxGrowth) {
this.maxGrowth = maxGrowth;
}
/** Compute the error ratio.
* @param yDotK derivatives computed during the first stages
* @param y0 estimate of the step at the start of the step
* @param y1 estimate of the step at the end of the step
* @param h current step
* @return error ratio, greater than 1 if step should be rejected
*/
protected abstract double estimateError(double[][] yDotK,
double[] y0, double[] y1,
double h);
/** Indicator for <i>fsal</i> methods. */
private boolean fsal;
/** Time steps from Butcher array (without the first zero). */
private double[] c;
/** Internal weights from Butcher array (without the first empty row). */
private double[][] a;
/** External weights for the high order method from Butcher array. */
private double[] b;
/** Prototype of the step interpolator. */
private RungeKuttaStepInterpolator prototype;
/** Stepsize control exponent. */
private double exp;
/** Safety factor for stepsize control. */
private double safety;
/** Minimal reduction factor for stepsize control. */
private double minReduction;
/** Maximal growth factor for stepsize control. */
private double maxGrowth;
}

View File

@ -0,0 +1,275 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.spaceroots.mantissa.ode;
/**
* This class implements the common part of all fixed step Runge-Kutta
* integrators for Ordinary Differential Equations.
* <p>These methods are explicit Runge-Kutta methods, their Butcher
* arrays are as follows :
* <pre>
* 0 |
* c2 | a21
* c3 | a31 a32
* ... | ...
* cs | as1 as2 ... ass-1
* |--------------------------
* | b1 b2 ... bs-1 bs
* </pre>
* </p>
* <p>Some methods are qualified as <i>fsal</i> (first same as last)
* methods. This means the last evaluation of the derivatives in one
* step is the same as the first in the next step. Then, this
* evaluation can be reused from one step to the next one and the cost
* of such a method is really s-1 evaluations despite the method still
* has s stages. This behaviour is true only for successful steps, if
* the step is rejected after the error estimation phase, no
* evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
* asi = bi for all i.</p>
* @see EulerIntegrator
* @see ClassicalRungeKuttaIntegrator
* @see GillIntegrator
* @see MidpointIntegrator
* @version $Id: RungeKuttaIntegrator.java 1705 2006-09-17 19:57:39Z luc $
* @author L. Maisonobe
*/
public abstract class RungeKuttaIntegrator
implements FirstOrderIntegrator {
/** Simple constructor.
* Build a Runge-Kutta integrator with the given
* step. The default step handler does nothing.
* @param fsal indicate that the method is an <i>fsal</i>
* @param c time steps from Butcher array (without the first zero)
* @param a internal weights from Butcher array (without the first empty row)
* @param b external weights for the high order method from Butcher array
* @param prototype prototype of the step interpolator to use
* @param step integration step
*/
protected RungeKuttaIntegrator(boolean fsal,
double[] c, double[][] a, double[] b,
RungeKuttaStepInterpolator prototype,
double step) {
this.fsal = fsal;
this.c = c;
this.a = a;
this.b = b;
this.prototype = prototype;
this.step = step;
handler = DummyStepHandler.getInstance();
switchesHandler = new SwitchingFunctionsHandler();
}
/** Get the name of the method.
* @return name of the method
*/
public abstract String getName();
/** Set the step handler for this integrator.
* The handler will be called by the integrator for each accepted
* step.
* @param handler handler for the accepted steps
*/
public void setStepHandler (StepHandler handler) {
this.handler = handler;
}
/** Get the step handler for this integrator.
* @return the step handler for this integrator
*/
public StepHandler getStepHandler() {
return handler;
}
/** Add a switching function to the integrator.
* @param function switching function
* @param maxCheckInterval maximal time interval between switching
* function checks (this interval prevents missing sign changes in
* case the integration steps becomes very large)
* @param convergence convergence threshold in the event time search
*/
public void addSwitchingFunction(SwitchingFunction function,
double maxCheckInterval,
double convergence) {
switchesHandler.add(function, maxCheckInterval, convergence);
}
public void integrate(FirstOrderDifferentialEquations equations,
double t0, double[] y0,
double t, double[] y)
throws DerivativeException, IntegratorException {
// sanity check
if (equations.getDimension() != y0.length) {
throw new IntegratorException("dimensions mismatch: ODE problem has dimension {0},"
+ " state vector has dimension {1}",
new String[] {
Integer.toString(equations.getDimension()),
Integer.toString(y0.length)
});
}
if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) {
throw new IntegratorException("too small integration interval: length = {0}",
new String[] {
Double.toString(Math.abs(t - t0))
});
}
boolean forward = (t > t0);
// create some internal working arrays
int stages = c.length + 1;
if (y != y0) {
System.arraycopy(y0, 0, y, 0, y0.length);
}
double[][] yDotK = new double[stages][];
for (int i = 0; i < stages; ++i) {
yDotK [i] = new double[y0.length];
}
double[] yTmp = new double[y0.length];
// set up an interpolator sharing the integrator arrays
AbstractStepInterpolator interpolator;
if (handler.requiresDenseOutput() || (! switchesHandler.isEmpty())) {
RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.clone();
rki.reinitialize(equations, yTmp, yDotK, forward);
interpolator = rki;
} else {
interpolator = new DummyStepInterpolator(yTmp, forward);
}
interpolator.storeTime(t0);
// recompute the step
double currentT = t0;
long nbStep = Math.max(1l, Math.abs(Math.round((t - t0) / step)));
double h = (t - t0) / nbStep;
boolean firstTime = true;
boolean lastStep = false;
handler.reset();
for (long i = 0; ! lastStep; ++i) {
interpolator.shift();
boolean needUpdate = false;
for (boolean loop = true; loop;) {
if (firstTime || !fsal) {
// first stage
equations.computeDerivatives(currentT, y, yDotK[0]);
firstTime = false;
}
// next stages
for (int k = 1; k < stages; ++k) {
for (int j = 0; j < y0.length; ++j) {
double sum = a[k-1][0] * yDotK[0][j];
for (int l = 1; l < k; ++l) {
sum += a[k-1][l] * yDotK[l][j];
}
yTmp[j] = y[j] + h * sum;
}
equations.computeDerivatives(currentT + c[k-1] * h, yTmp, yDotK[k]);
}
// estimate the state at the end of the step
for (int j = 0; j < y0.length; ++j) {
double sum = b[0] * yDotK[0][j];
for (int l = 1; l < stages; ++l) {
sum += b[l] * yDotK[l][j];
}
yTmp[j] = y[j] + h * sum;
}
// Switching functions handling
interpolator.storeTime(currentT + h);
if (switchesHandler.evaluateStep(interpolator)) {
needUpdate = true;
h = switchesHandler.getEventTime() - currentT;
} else {
loop = false;
}
}
// the step has been accepted
currentT += h;
System.arraycopy(yTmp, 0, y, 0, y0.length);
switchesHandler.stepAccepted(currentT, y);
if (switchesHandler.stop()) {
lastStep = true;
} else {
lastStep = (i == (nbStep - 1));
}
// provide the step data to the step handler
interpolator.storeTime(currentT);
handler.handleStep(interpolator, lastStep);
if (fsal) {
// save the last evaluation for the next step
System.arraycopy(yDotK[stages - 1], 0, yDotK[0], 0, y0.length);
}
switchesHandler.reset(currentT, y);
if (needUpdate) {
// a switching function has changed the step
// we need to recompute stepsize
nbStep = Math.max(1l, Math.abs(Math.round((t - currentT) / step)));
h = (t - currentT) / nbStep;
i = -1;
}
}
}
/** Indicator for <i>fsal</i> methods. */
private boolean fsal;
/** Time steps from Butcher array (without the first zero). */
private double[] c;
/** Internal weights from Butcher array (without the first empty row). */
private double[][] a;
/** External weights for the high order method from Butcher array. */
private double[] b;
/** Prototype of the step interpolator. */
private RungeKuttaStepInterpolator prototype;
/** Integration step. */
private double step;
/** Step handler. */
private StepHandler handler;
/** Switching functions handler. */
protected SwitchingFunctionsHandler switchesHandler;
}

Some files were not shown because too many files have changed in this diff Show More