Skip to content

Commit

Permalink
Implement SIR
Browse files Browse the repository at this point in the history
  • Loading branch information
jschueller committed May 6, 2024
1 parent b40bd78 commit 99bf0ca
Show file tree
Hide file tree
Showing 16 changed files with 503 additions and 73 deletions.
2 changes: 2 additions & 0 deletions lib/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
ot_add_current_dir_to_include_dirs ()

ot_add_source_file (SlicedInverseRegression.cxx)
ot_add_source_file (SlicedInverseRegressionResult.cxx)

ot_install_header_file (SlicedInverseRegression.hxx)
ot_install_header_file (SlicedInverseRegressionResult.hxx)


include_directories (${INTERNAL_INCLUDE_DIRS})
Expand Down
135 changes: 126 additions & 9 deletions lib/src/SlicedInverseRegression.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
*/
#include "otsliced/SlicedInverseRegression.hxx"
#include <openturns/PersistentObjectFactory.hxx>
#include <openturns/SquareComplexMatrix.hxx>
#include <openturns/SpecFunc.hxx>

using namespace OT;

Expand All @@ -38,22 +40,102 @@ SlicedInverseRegression::SlicedInverseRegression()
// Nothing to do
}

SlicedInverseRegression::SlicedInverseRegression(const Sample & inputSample,
const Sample & outputSample)
: PersistentObject()
, inputSample_(inputSample)
, outputSample_(outputSample)
, modesNumber_(inputSample.getDimension())
{
if (outputSample.getDimension() != 1)
throw InvalidArgumentException(HERE) << "Supervision variable must be of dimension 1";
}

/* Virtual constructor method */
SlicedInverseRegression * SlicedInverseRegression::clone() const
{
return new SlicedInverseRegression(*this);
}

/* example of a func that return a point squared. */
Point SlicedInverseRegression::square(Point& p) const
{
void SlicedInverseRegression::run()
{
const UnsignedInteger size = inputSample_.getSize();
const UnsignedInteger inputDimension = inputSample_.getDimension();
const Indices supervisionIndices = outputSample_.argsort();
Collection<Indices> list_chunk;
UnsignedInteger offset = 0;
Indices chunk_population;
for (UnsignedInteger i = 0; i < sliceNumber_; ++ i)
{
const UnsignedInteger localSize = std::min(size / sliceNumber_, size - offset);
Indices chunk(localSize);
std::copy(supervisionIndices.begin() + offset, supervisionIndices.begin() + offset + localSize, chunk.begin());
list_chunk.add(chunk);
chunk_population.add(localSize);
offset += localSize;
}
const Point mean(inputSample_.computeMean());
Sample X_centered(inputSample_);
X_centered -= mean;
Matrix Q(size, inputDimension);
for (UnsignedInteger i = 0; i < size; ++ i)
for(UnsignedInteger j = 0; j < inputDimension; ++ j)
Q(i, j) = inputSample_(i, j);
Matrix R;
#if OPENTURNS_VERSION >= 102300
Q.computeQRInPlace(R);
#else
Q.computeQR(R, false, false); // full, keepintact
#endif
Sample Z(size, inputDimension);
for (UnsignedInteger i = 0; i < size; ++ i)
for(UnsignedInteger j = 0; j < inputDimension; ++ j)
Z(i, j) = Q(i, j) * std::sqrt(1.0 * size);
Matrix zMeans(sliceNumber_, inputDimension);
for(UnsignedInteger j = 0; j < sliceNumber_; ++ j)
{
const Point zMean(Z.select(list_chunk[j]).computeMean() * std::sqrt(1.0 * chunk_population[j]));
for(UnsignedInteger i = 0; i < inputDimension; ++ i)
zMeans(j, i) = zMean[i];
}
SymmetricMatrix M((zMeans.transpose() * zMeans / size).getImplementation());
SquareMatrix eigenVectors;
#if OPENTURNS_VERSION >= 102300
const Point eigenValues = M.computeEVInPlace(eigenVectors);
#else
const Point eigenValues = M.computeEV(eigenVectors, false); // keepIntact
#endif
SquareMatrix eigenVectorsRev(inputDimension);
Point eigenValuesRev(inputDimension);
for(UnsignedInteger j = 0; j < inputDimension; ++ j)
{
eigenValuesRev[j] = eigenValues[inputDimension - 1 - j];
for(UnsignedInteger i = 0; i < inputDimension; ++ i)
eigenVectorsRev(i, j) = eigenVectors(i, inputDimension - 1 - j);
}
TriangularMatrix R2((R * std::sqrt(1.0 * size)).getImplementation(), false);
Matrix directions = R2.solveLinearSystemInPlace(eigenVectorsRev);

Point p_out(p.getSize());
for(UnsignedInteger i = 0; i < p.getSize(); ++ i)
// prune directions and associated eigenvalues
directions.getImplementation()->resize(inputDimension, modesNumber_);
eigenValuesRev.resize(modesNumber_);

// normalize directions
for(UnsignedInteger j = 0; j < modesNumber_; ++ j)
{
p_out[i] = p[i] * p[i];
Scalar normJ = 0.0;
for(UnsignedInteger i = 0; i < inputDimension; ++ i)
normJ += directions(i, j) * directions(i, j);
normJ = 1.0 / std::sqrt(normJ);
for(UnsignedInteger i = 0; i < inputDimension; ++ i)
directions(i, j) *= normJ;
}
return p_out;
result_ = SlicedInverseRegressionResult(directions, mean, eigenValuesRev);
}

SlicedInverseRegressionResult SlicedInverseRegression::getResult() const
{
return result_;
}

/* String converter */
Expand All @@ -64,16 +146,51 @@ String SlicedInverseRegression::__repr__() const
return oss;
}


/* Slice number accessor */
UnsignedInteger SlicedInverseRegression::getSliceNumber() const
{
return sliceNumber_;
}

void SlicedInverseRegression::setSliceNumber(const UnsignedInteger sliceNumber)
{
sliceNumber_ = sliceNumber;
}

/* Modes number accessor */
void SlicedInverseRegression::setModesNumber(const UnsignedInteger modesNumber)
{
if (modesNumber_ > inputSample_.getDimension())
throw InvalidArgumentException(HERE) << "Cannot use more than " <<inputSample_.getDimension() << " modes";
modesNumber_ = modesNumber;
}

UnsignedInteger SlicedInverseRegression::getModesNumber() const
{
return modesNumber_;
}

/* Method save() stores the object through the StorageManager */
void SlicedInverseRegression::save(Advocate & adv) const
{
PersistentObject::save( adv );
PersistentObject::save(adv);
adv.saveAttribute( "inputSample_", inputSample_ );
adv.saveAttribute( "outputSample_", outputSample_ );
adv.saveAttribute( "sliceNumber_", sliceNumber_ );
adv.saveAttribute( "modesNumber", modesNumber_ );
adv.saveAttribute( "result_", result_ );
}

/* Method load() reloads the object from the StorageManager */
void SlicedInverseRegression::load(Advocate & adv)
{
PersistentObject::load( adv );
PersistentObject::load(adv);
adv.loadAttribute( "inputSample_", inputSample_ );
adv.loadAttribute( "outputSample_", outputSample_ );
adv.loadAttribute( "sliceNumber_", sliceNumber_ );
adv.loadAttribute( "modesNumber", modesNumber_ );
adv.loadAttribute( "result_", result_ );
}


Expand Down
107 changes: 107 additions & 0 deletions lib/src/SlicedInverseRegressionResult.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// -*- C++ -*-
/**
* @brief SlicedInverseRegressionResult
*
* Copyright 2005-2024 Airbus-EDF-IMACS-ONERA-Phimeca
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "otsliced/SlicedInverseRegressionResult.hxx"

#include <openturns/PersistentObjectFactory.hxx>
#include <openturns/LinearFunction.hxx>

using namespace OT;

namespace OTSLICED
{

CLASSNAMEINIT(SlicedInverseRegressionResult);

static Factory<SlicedInverseRegressionResult> Factory_SlicedInverseRegressionResult;


/* Default constructor */
SlicedInverseRegressionResult::SlicedInverseRegressionResult()
: PersistentObject()
{
// Nothing to do
}

SlicedInverseRegressionResult::SlicedInverseRegressionResult(const OT::Matrix & directions,
const OT::Point & center, const OT::Point & eigenValues)
: PersistentObject()
, directions_(directions)
, center_(center)
, eigenValues_(eigenValues)
{
}

/* Virtual constructor method */
SlicedInverseRegressionResult * SlicedInverseRegressionResult::clone() const
{
return new SlicedInverseRegressionResult(*this);
}

/* String converter */
String SlicedInverseRegressionResult::__repr__() const
{
OSS oss;
oss << "class=" << SlicedInverseRegressionResult::GetClassName();
return oss;
}

Function SlicedInverseRegressionResult::getTransformation() const
{
return LinearFunction(center_, Point(center_.getDimension()), directions_);
}

Function SlicedInverseRegressionResult::getInverseTransformation() const
{
Matrix inv(directions_.solveLinearSystem(IdentityMatrix(center_.getDimension())));
return LinearFunction(-center_, Point(center_.getDimension()), inv);
}

Matrix SlicedInverseRegressionResult::getDirections() const
{
return directions_;
}

Point SlicedInverseRegressionResult::getEigenvalues() const
{
return eigenValues_;
}

/* Method save() stores the object through the StorageManager */
void SlicedInverseRegressionResult::save(Advocate & adv) const
{
PersistentObject::save(adv);
adv.saveAttribute( "directions_", directions_ );
adv.saveAttribute( "center_", center_ );
adv.saveAttribute( "eigenValues_", eigenValues_ );
}

/* Method load() reloads the object from the StorageManager */
void SlicedInverseRegressionResult::load(Advocate & adv)
{
PersistentObject::load(adv);
adv.loadAttribute( "directions_", directions_ );
adv.loadAttribute( "center_", center_ );
adv.loadAttribute( "eigenValues_", eigenValues_ );
}


} /* namespace OTSLICED */
32 changes: 25 additions & 7 deletions lib/src/otsliced/SlicedInverseRegression.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@
#ifndef OTSLICED_SLICEDINVERSEREGRESSION_HXX
#define OTSLICED_SLICEDINVERSEREGRESSION_HXX

#include <openturns/PersistentObject.hxx>
#include <openturns/StorageManager.hxx>
#include <openturns/Point.hxx>
#include "otsliced/otslicedprivate.hxx"
#include "otsliced/SlicedInverseRegressionResult.hxx"

#include <openturns/Sample.hxx>

namespace OTSLICED
{

/**
* @class SlicedInverseRegression
*
* SlicedInverseRegression is some slicedinverseregression type to illustrate how to add some classes in OpenTURNS
* SIR dimension reduction
*/
class OTSLICED_API SlicedInverseRegression
: public OT::PersistentObject
Expand All @@ -43,11 +42,23 @@ public:
/** Default constructor */
SlicedInverseRegression();

SlicedInverseRegression(const OT::Sample & inputSample,
const OT::Sample & outputSample);

/** Virtual constructor method */
SlicedInverseRegression * clone() const override;

/** example of a func that return a point squared. **/
OT::Point square(OT::Point& p) const;
/** Slice number accessor */
void setSliceNumber(const OT::UnsignedInteger sliceNumber);
OT::UnsignedInteger getSliceNumber() const;

/** Modes number accessor */
void setModesNumber(const OT::UnsignedInteger modesNumber);
OT::UnsignedInteger getModesNumber() const;

void run();

SlicedInverseRegressionResult getResult() const;

/** String converter */
OT::String __repr__() const override;
Expand All @@ -59,6 +70,13 @@ public:
void load(OT::Advocate & adv) override;

private:
OT::Sample inputSample_;
OT::Sample outputSample_;

OT::UnsignedInteger modesNumber_ = 0;
OT::UnsignedInteger sliceNumber_ = 10;

SlicedInverseRegressionResult result_;

}; /* class SlicedInverseRegression */

Expand Down
Loading

0 comments on commit 99bf0ca

Please sign in to comment.