/**************************************************************************** Copyright (c) 2003, Landmark Graphics and others. All rights reserved. This program and accompanying materials are made available under the terms of the Common Public License - v1.0, which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html ****************************************************************************/ package com.lgc.wsh.opt; import java.io.*; import java.util.logging.Level; import java.util.logging.Logger; import com.lgc.wsh.util.Almost; import com.lgc.wsh.util.LoggerStream; /** Implements convenience methods for Vect. @author W.S. Harlan */ public class VectUtil { private static final Logger LOG = Logger.getLogger(VectUtil.class.getName()); /** Scale a vector by a scalar constant. @param v Vector to scale. @param scalar Factor to scale the vector. */ public static void scale(Vect v, double scalar) { v.add(scalar, 0., v); } /** Set the magnitude of this vector to zero, so that this.dot(this) == 0. @param v Vector to zero */ public static void zero(Vect v) { scale(v, 0.); } /** Copy the state of one vector onto another. @param to Vector whose state should be initialized with the state of from. @param from Vector whose state should be copied. */ public static void copy(Vect to, VectConst from) { to.add(0., 1., from); } /** Clone a vector and initialized to zero, so that out.dot(out) == 0. @param v Vect to clone @return A cloned copy of the vector set to zero magnitude. */ public static Vect cloneZero(VectConst v) { Vect result = v.clone(); zero(result); return result; } static final Almost ALMOST_DOT = new Almost(0.000015); /** See if two vectors are the same. Useful for test code. @param v1 First vector @param v2 Second vector @return true if vectors appear to be the same, within floating precision. */ public static boolean areSame(VectConst v1, VectConst v2) { double aa = v1.dot(v1); double ab = v1.dot(v2); double bb = v2.dot(v2); // LOG.info("aa="+aa+" ab="+ab+" bb="+bb); return ALMOST_DOT.equal(aa,bb) && ALMOST_DOT.equal(aa,ab) && ALMOST_DOT.equal(ab,bb) ; } /** Exercise all methods of Vect. @param vect An instance of a Vect to test. Should be initialized to random non-zero values. A vector of zero magnitude will fail. */ public static void test(VectConst vect) { double originalDot = vect.dot(vect); ass (!Almost.FLOAT.zero(originalDot), "cannot test a zero vector"); Vect t = VectUtil.cloneZero(vect); ass (Almost.FLOAT.zero(t.dot(t)), "cloneZero() did not work"); VectUtil.copy(t, vect); double check = t.dot(vect)/vect.dot(vect); ass(Almost.FLOAT.equal(check, 1.), "not 1. check="+check); VectUtil.scale(t, 0.5); check = t.dot(vect)/vect.dot(vect); ass(Almost.FLOAT.equal(check, 0.5), "not 0.5 check="+check); t.add(1., 1.,vect); check = t.dot(vect)/vect.dot(vect); ass(Almost.FLOAT.equal(check, 1.5), "not 1.5 check="+check); t.add(2., -5., vect); check = t.dot(vect)/vect.dot(vect); ass(Almost.FLOAT.equal(check, -2.), "not -2, check="+check); t.project(0., 1., vect); t.project(1.75, -0.75, vect); ass (VectUtil.areSame(t, vect), "project failed"); t.dispose(); ass (Almost.FLOAT.equal(originalDot, vect.dot(vect)), "exercise of clone damaged original"); t = vect.clone(); t.multiplyInverseCovariance(); double mag1 = vect.dot(t); t.dispose(); double mag2 = vect.magnitude(); ass (Almost.FLOAT.equal(mag1, mag2), "magnitude() inconsistent with " +"multiplyInverseCovariance() and dot(): "+ mag1+"!="+mag2); ass(mag1 > 0, "inverse covariance gave zero magnitude"); ass(mag2 > 0, "magnitude was zero when dot product was not zero"); // simple test of constrain t = vect.clone(); t.constrain(); double mag3 = t.magnitude(); ass(mag3 > 0, "constrain() gave zero magnitude"); t.dispose(); // make sure postCondition can be called t = vect.clone(); t.postCondition(); t.dispose(); // some will override toString method String vs = vect.toString(); assert vs != null && vs.length() > 0; // test serialization byte[] data = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); t = vect.clone(); oos.writeObject(t); oos.flush(); oos.close(); t.dispose(); t = null; data = baos.toByteArray(); } catch (IOException e) { e.printStackTrace(new LoggerStream(LOG, Level.SEVERE)); ass(false, "writing serialization failed "+e.getMessage()); } try { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); t = (Vect) (ois.readObject()); ass (VectUtil.areSame(t, vect), "Serialization did not preserve Vect " +t.dot(t)+"=="+t.dot(vect)+"=="+vect.dot(vect)); // check these are not sharing anything VectUtil.scale(t,0.5); double tt = t.dot(t); double tv = t.dot(vect); double vv = vect.dot(vect); ass (tt>0, "Scaling set serialized vect to zero magnitude"); ass (Almost.FLOAT.equal(tt*2, tv), "Serialized vector does not have independent magnitude tt="+tt+ " tv="+tv); ass (Almost.FLOAT.equal(tv*2, vv), "serialized vector does not have independent magnitude tv="+tv+ " vv="+vv); t.dispose(); } catch (IOException e) { e.printStackTrace(new LoggerStream(LOG, Level.SEVERE)); ass(false, "reading serialization failed "+e.getMessage()); } catch (ClassNotFoundException e) { e.printStackTrace(new LoggerStream(LOG, Level.SEVERE)); ass (false, "Can't find class just written "+e.getMessage()); } } /** Return the number of significant digits in the dot product when calculated with and without the transpose. @param data Nonzero sample data @param model A nonzero sample model. @param transform The transform to test. @return number of digits in precision. */ public static int getTransposePrecision(VectConst data, VectConst model, LinearTransform transform) { return getTransposePrecision(data,model, new LinearTransformWrapper(transform)); } /** Return the number of significant digits in the dot product when calculated with and without the transpose. @param data Nonzero sample data @param model A nonzero sample model. @param transform The transform to test. @return number of digits in precision. */ public static int getTransposePrecision(VectConst data, VectConst model, Transform transform) { int precision = 200; boolean dampOnlyPerturbation = true; // results in a bigger b VectUtil.test(data); VectUtil.test(model); TransformQuadratic tq = new TransformQuadratic (data, model, null, transform, dampOnlyPerturbation); precision = Math.min(precision,tq.getTransposePrecision()); return precision; } // Assertion that cannot be disabled. private static void ass(boolean condition, String requirement) { if (!condition) throw new IllegalStateException(requirement); } }