bool MeasureGroup_Impl::insert(int index, const Measure& measure) {
    if ((index < 0) || (index > int(numMeasures(false)))) {
      return false;
    }

    if (index == int(numMeasures(false))) {
      return push(measure);
    }

    // position does not matter for compatibility checking
    MeasureVector candidates = measures(false);
    candidates.push_back(measure);
    if (!measuresAreCompatible(candidates)) {
      return false;
    }

    auto it = m_measures.begin();
    for (int count = 0; count < index; ++count, ++it);
    it = m_measures.insert(it,measure);
    for (int i = index, n = int(m_measures.size()); i < n; ++i) {
      m_measures[i].onChange();
    }
    connectChild(m_measures[index],true);
    onChange(AnalysisObject_Impl::InvalidatesDataPoints);
    return true;
  }
TEST_F(AnalysisFixture, MeasureGroup_DeselectMeasures) {
  MeasureVector measures;

  // null, ruby, ruby
  measures.push_back(NullMeasure());
  openstudio::path rubyScriptPath = toPath(rubyLibDir()) / 
                                    toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
  measures.push_back(RubyMeasure(rubyScriptPath,
                                 FileReferenceType::OSM,
                                 FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                 FileReferenceType::OSM,
                                 FileReferenceType::OSM));

  MeasureGroup variable("Variable",measures);
  EXPECT_EQ(3u,variable.numMeasures(true));
  EXPECT_EQ(3u,variable.numMeasures(false));

  measures[1].setIsSelected(false);
  EXPECT_EQ(2u,variable.numMeasures(true));
  ASSERT_EQ(2u,variable.measures(true).size());
  EXPECT_TRUE(variable.measures(true)[1] == measures[2]);
  EXPECT_EQ(3u,variable.numMeasures(false));

  measures[0].setIsSelected(false);
  measures[2].setIsSelected(false);
  EXPECT_EQ(0u,variable.numMeasures(true));
  EXPECT_EQ(3u,variable.numMeasures(false));
}
  bool MeasureGroup_Impl::push(const Measure& measure) {
    MeasureVector candidates = measures(false);
    candidates.push_back(measure);
    if (!measuresAreCompatible(candidates)) {
      return false;
    }

    m_measures.push_back(measure);
    m_measures.back().onChange();
    connectChild(m_measures.back(),true);
    onChange(AnalysisObject_Impl::Benign);
    return true;
  }
  std::vector<Measure> MeasureGroup_Impl::measures(
      bool selectedMeasuresOnly) const
  {
    if (selectedMeasuresOnly) {
      MeasureVector result;
      for (const Measure& measure : m_measures) {
        if (measure.isSelected()) {
          result.push_back(measure);
        }
      }
      return result;
    }

    return m_measures;
  }
  bool MeasureGroup_Impl::fileTypesAreCompatible(
      const Measure& childMeasure,
      const FileReferenceType& proposedInputFileType,
      const FileReferenceType& proposedOutputFileType) const
  {
    // can only change file types if no null measures
    if (proposedInputFileType != proposedOutputFileType) {
      NullMeasureVector nullMeasures = subsetCastVector<NullMeasure>(m_measures);
      if (!nullMeasures.empty()) {
        return false;
      }
    }

    // check proposals against file types of other measures
    MeasureVector pv = measures(false);
    auto it = std::find(pv.begin(),pv.end(),childMeasure);
    OS_ASSERT(it != pv.end());
    pv.erase(it);

    std::pair<bool,OptionalFileReferenceType> inputFileTypeResult = detail::inputFileType(pv);
    OS_ASSERT(inputFileTypeResult.first);
    if (inputFileTypeResult.second && (proposedInputFileType != *(inputFileTypeResult.second))) {
      return false;
    }

    std::pair<bool,OptionalFileReferenceType> outputFileTypeResult = detail::outputFileType(pv);
    if (outputFileTypeResult.second && (proposedOutputFileType != *(outputFileTypeResult.second))) {
      return false;
    }

    // either proposals match current file types, or at least one current file type is null.
    // if matches current, should be a-okay.
    // otherwise, proposing a new file type on input or output side for this variable, so
    // ask problem if ok.
    if (OptionalAnalysisObject parent = this->parent()) {
      if (!inputFileTypeResult.second || !outputFileTypeResult.second) {
        if (!(parent->cast<WorkflowStep>().fileTypesAreCompatible(proposedInputFileType,
                                                                  proposedOutputFileType)))
        {
          return false;
        }
      }
    }
    return true;
  }
TEST_F(AnalysisFixture, MeasureGroup_Constructors) {
  MeasureVector measures;

  // At most one null measure is allowed.
  measures.push_back(NullMeasure());
  measures.push_back(NullMeasure());
  measures.push_back(NullMeasure());
  measures.push_back(NullMeasure());
  MeasureGroup variable("Variable",measures);
  EXPECT_EQ(1u,variable.numMeasures(false));

  // deserialization constructor
  UUID uuid = createUUID();
  UUID versionUUID = createUUID();
  measures = variable.measures(false);
  variable = MeasureGroup(uuid,versionUUID,"Variable","","",boost::none,measures);
  EXPECT_EQ("Variable",variable.name());
  EXPECT_TRUE(variable.uuid() == uuid);
  EXPECT_TRUE(variable.versionUUID() == versionUUID);
  EXPECT_TRUE(variable.measures(false) == measures);

  // Inconsistent file types in measures (should throw)
  measures.clear();
  openstudio::path rubyScriptPath = toPath(rubyLibDir()) / 
                                    toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::IDF));
  EXPECT_THROW(MeasureGroup("Variable 2",measures),std::exception);

  // Inconsistent file types in measures (should throw)
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  EXPECT_THROW(MeasureGroup("Variable",measures),std::exception);

}
TEST_F(ProjectFixture, RubyMeasureRecord_RubyScript) {
    // Measures
    MeasureVector measures;

    // Null Measure
    measures.push_back(NullMeasure());

    openstudio::path rubyLibDirPath = openstudio::toPath(rubyLibDir());
    openstudio::path perturbScript = rubyLibDirPath/openstudio::toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
    RubyMeasure rubyMeasure(perturbScript,
                            FileReferenceType::OSM,
                            FileReferenceType::OSM);
    rubyMeasure.addArgument("inputPath", "in.osm");
    rubyMeasure.addArgument("outputPath", "out.osm");
    rubyMeasure.addArgument("objectType", "OS:Material");
    rubyMeasure.addArgument("nameRegex", "I02 50mm insulation board");
    rubyMeasure.addArgument("field", "3");
    rubyMeasure.addArgument("value", "0.10");

    // RubyMeasure
    measures.push_back(rubyMeasure);

    // Variables
    VariableVector variables;

    variables.push_back(MeasureGroup("Wall Construction",measures));

    // Workflow
    openstudio::runmanager::Workflow workflow;

    // Problem
    Problem problem("Variable",variables,workflow);

    // Save to database
    {
        ProjectDatabase database = getCleanDatabase("RubyMeasureRecord_RubyScript");

        bool didStartTransaction = database.startTransaction();
        EXPECT_TRUE(didStartTransaction);

        // Problem Record
        ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

        database.save();
        if (didStartTransaction) {
            EXPECT_TRUE(database.commitTransaction());
        }

        // Variable Records
        InputVariableRecordVector measureGroupRecords = problemRecord.inputVariableRecords();
        EXPECT_EQ(1u,measureGroupRecords.size());

        // Discrete Variable Record
        MeasureGroupRecord measureGroupRecord = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
        EXPECT_EQ(2u,measureGroupRecord.measureRecordIds(true).size());
        EXPECT_EQ(2u,measureGroupRecord.measureRecords(true).size());
        RubyMeasureRecord rubyMeasureRecord(rubyMeasure,measureGroupRecord,0);
        EXPECT_EQ("MeasureRecords",rubyMeasureRecord.databaseTableName());
        ObjectRecordVector objectRecordVector = rubyMeasureRecord.children();
        EXPECT_EQ(6u,objectRecordVector.size()); // arguments
        objectRecordVector = rubyMeasureRecord.resources();
        EXPECT_EQ(1u,objectRecordVector.size()); // script
        FileReferenceRecord scriptRecord = rubyMeasureRecord.fileReferenceRecord();
        EXPECT_EQ("FileReferenceRecords",scriptRecord.databaseTableName());

        Measure measure = rubyMeasureRecord.measure();
        EXPECT_EQ(true,measure.isSelected());
        ASSERT_TRUE(measure.optionalCast<RubyMeasure>());
        RubyMeasure rubyMeasureCopy = measure.cast<RubyMeasure>();
        EXPECT_FALSE(rubyMeasureCopy.usesBCLMeasure());
        EXPECT_FALSE(rubyMeasureCopy.isUserScript());
        EXPECT_EQ(6u,rubyMeasureCopy.arguments().size());

        MeasureGroupRecord measureGroupRecordFromRuby = rubyMeasureRecord.measureGroupRecord().get();
        EXPECT_EQ(measureGroupRecord.databaseTableName(),measureGroupRecordFromRuby.databaseTableName());
        EXPECT_EQ(measureGroupRecord.id(),measureGroupRecordFromRuby.id());
    }

    // Reopen database
    {
        ProjectDatabase database = getExistingDatabase("RubyMeasureRecord_RubyScript");

        ProblemRecordVector problemRecords = ProblemRecord::getProblemRecords(database);
        ASSERT_FALSE(problemRecords.empty());
        EXPECT_EQ(1u,problemRecords.size());
        ProblemRecord problemRecord = problemRecords[0];

        // COPY-PASTED FROM ABOVE

        // Variable Records
        InputVariableRecordVector measureGroupRecords = problemRecord.inputVariableRecords();
        EXPECT_EQ(1u,measureGroupRecords.size());

        // Discrete Variable Record
        MeasureGroupRecord measureGroupRecord = measureGroupRecords.at(0).cast<MeasureGroupRecord>();
        EXPECT_EQ(2u,measureGroupRecord.measureRecordIds(true).size());
        EXPECT_EQ(2u,measureGroupRecord.measureRecords(true).size());
        RubyMeasureRecord rubyMeasureRecord(rubyMeasure,measureGroupRecord,0);
        EXPECT_EQ("MeasureRecords",rubyMeasureRecord.databaseTableName());
        ObjectRecordVector objectRecordVector = rubyMeasureRecord.children();
        EXPECT_EQ(6u,objectRecordVector.size()); // arguments
        objectRecordVector = rubyMeasureRecord.resources();
        EXPECT_EQ(1u,objectRecordVector.size()); // script
        FileReferenceRecord scriptRecord = rubyMeasureRecord.fileReferenceRecord();
        EXPECT_EQ("FileReferenceRecords",scriptRecord.databaseTableName());

        Measure measure = rubyMeasureRecord.measure();
        EXPECT_EQ(true,measure.isSelected());
        ASSERT_TRUE(measure.optionalCast<RubyMeasure>());
        RubyMeasure rubyMeasureCopy = measure.cast<RubyMeasure>();
        EXPECT_FALSE(rubyMeasureCopy.usesBCLMeasure());
        EXPECT_FALSE(rubyMeasureCopy.isUserScript());
        EXPECT_EQ(6u,rubyMeasureCopy.arguments().size());

        MeasureGroupRecord measureGroupRecordFromRuby = rubyMeasureRecord.measureGroupRecord().get();
        EXPECT_EQ(measureGroupRecord.databaseTableName(),measureGroupRecordFromRuby.databaseTableName());
        EXPECT_EQ(measureGroupRecord.id(),measureGroupRecordFromRuby.id());
    }
}
TEST_F(ProjectFixture, RubyMeasureRecord_BCLMeasure) {
    // Construct problem with RubyMeasure that points to BCLMeasure
    Problem problem("Problem",VariableVector(),runmanager::Workflow());
    MeasureGroup dvar("Variable",MeasureVector());
    problem.push(dvar);
    openstudio::path measuresPath = resourcesPath() / toPath("/utilities/BCL/Measures");
    openstudio::path dir = measuresPath / toPath("SetWindowToWallRatioByFacade");
    BCLMeasure measure = BCLMeasure::load(dir).get();
    RubyMeasure rpert(measure);
    dvar.push(rpert);
    OSArgument arg = OSArgument::makeDoubleArgument("wwr");
    arg.setValue(0.4);
    rpert.setArgument(arg);
    arg = OSArgument::makeIntegerArgument("typo_arg");
    arg.setDefaultValue(1);
    rpert.setArgument(arg);

    // Serialize to database
    {
        ProjectDatabase database = getCleanDatabase("RubyMeasureRecord_BCLMeasure");

        bool didStartTransaction = database.startTransaction();
        EXPECT_TRUE(didStartTransaction);

        // Problem Record
        ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problem,database);

        database.save();
        if (didStartTransaction) {
            EXPECT_TRUE(database.commitTransaction());
        }
    }

    // Re-open database, de-serialize, verify that RubyMeasure is intact.
    openstudio::path tempDir1 = measuresPath / toPath(toString(createUUID()));
    {
        ProjectDatabase database = getExistingDatabase("RubyMeasureRecord_BCLMeasure");
        ASSERT_EQ(1u,ProblemRecord::getProblemRecords(database).size());
        ASSERT_EQ(1u,MeasureGroupRecord::getMeasureGroupRecords(database).size());
        EXPECT_EQ(1u,RubyMeasureRecord::getRubyMeasureRecords(database).size());

        MeasureRecordVector dprs = MeasureGroupRecord::getMeasureGroupRecords(database)[0].measureRecords(false);
        ASSERT_EQ(1u,dprs.size());
        ASSERT_TRUE(dprs[0].optionalCast<RubyMeasureRecord>());
        RubyMeasureRecord rpr = dprs[0].cast<RubyMeasureRecord>();
        RubyMeasure rp = rpr.rubyMeasure();
        EXPECT_TRUE(rp.usesBCLMeasure());
        EXPECT_TRUE(rp.measure());
        EXPECT_EQ(dir,rp.measureDirectory());
        EXPECT_EQ(measure.uuid(),rp.measureUUID());
        EXPECT_EQ(measure.versionUUID(),rp.measureVersionUUID());
        EXPECT_ANY_THROW(rp.perturbationScript());
        EXPECT_EQ(2u,rp.arguments().size());
        EXPECT_FALSE(rp.hasIncompleteArguments());

        // Update measure and save
        BCLMeasure newVersion = measure.clone(tempDir1).get();
        newVersion.setDescription("Window to wall ratio with sill height configurable.");
        newVersion.save();
        EXPECT_NE(measure.versionUUID(),newVersion.versionUUID());
        OSArgumentVector args;
        args.push_back(OSArgument::makeDoubleArgument("wwr"));
        args.push_back(OSArgument::makeDoubleArgument("sillHeight"));

        Problem problemCopy = ProblemRecord::getProblemRecords(database)[0].problem();
        problemCopy.updateMeasure(newVersion,args,false);

        bool didStartTransaction = database.startTransaction();
        EXPECT_TRUE(didStartTransaction);

        // Problem Record
        ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problemCopy,database);

        database.save();
        if (didStartTransaction) {
            EXPECT_TRUE(database.commitTransaction());
        }
    }

    // Re-open database, check that old argument records are gone, check that de-serialized object ok
    openstudio::path tempDir2 = measuresPath / toPath(toString(createUUID()));
    {
        ProjectDatabase database = getExistingDatabase("RubyMeasureRecord_BCLMeasure");
        ASSERT_EQ(1u,ProblemRecord::getProblemRecords(database).size());
        EXPECT_EQ(1u,MeasureGroupRecord::getMeasureGroupRecords(database).size());
        EXPECT_EQ(1u,RubyMeasureRecord::getRubyMeasureRecords(database).size());
        EXPECT_EQ(1u,FileReferenceRecord::getFileReferenceRecords(database).size());
        EXPECT_EQ(2u,OSArgumentRecord::getOSArgumentRecords(database).size());

        Problem problemCopy = ProblemRecord::getProblemRecords(database)[0].problem();
        InputVariableVector vars = problemCopy.variables();
        ASSERT_FALSE(vars.empty());
        ASSERT_TRUE(vars[0].optionalCast<MeasureGroup>());
        MeasureVector dps = vars[0].cast<MeasureGroup>().measures(false);
        ASSERT_FALSE(dps.empty());
        ASSERT_TRUE(dps[0].optionalCast<RubyMeasure>());
        RubyMeasure rp = dps[0].cast<RubyMeasure>();
        EXPECT_TRUE(rp.usesBCLMeasure());
        EXPECT_TRUE(rp.measure());
        EXPECT_EQ(tempDir1,rp.measureDirectory());
        EXPECT_EQ(measure.uuid(),rp.measureUUID());
        EXPECT_NE(measure.versionUUID(),rp.measureVersionUUID());
        EXPECT_ANY_THROW(rp.perturbationScript());
        ASSERT_EQ(2u,rp.arguments().size());
        EXPECT_TRUE(rp.hasIncompleteArguments());
        EXPECT_EQ("wwr",rp.arguments()[0].name());
        ASSERT_EQ(1u,rp.incompleteArguments().size());
        EXPECT_EQ("sillHeight",rp.incompleteArguments()[0].name());

        // Set to different measure
        BCLMeasure measure2 = measure.clone(tempDir2).get();
        measure2.changeUID();
        measure2.incrementVersionId();
        measure2.save();
        measure2 = BCLMeasure::load(tempDir2).get();
        EXPECT_NE(measure.uuid(),measure2.uuid());
        EXPECT_NE(measure.versionUUID(),measure2.versionUUID());
        rp.setMeasure(measure2);
        EXPECT_TRUE(rp.isDirty());
        EXPECT_TRUE(problemCopy.isDirty());

        bool didStartTransaction = database.startTransaction();
        EXPECT_TRUE(didStartTransaction);

        // Problem Record
        ProblemRecord problemRecord = ProblemRecord::factoryFromProblem(problemCopy,database);

        database.save();
        if (didStartTransaction) {
            EXPECT_TRUE(database.commitTransaction());
        }
    }

    // Re-open database, check that old measure and all argument records are gone
    {
        ProjectDatabase database = getExistingDatabase("RubyMeasureRecord_BCLMeasure");
        ASSERT_EQ(1u,ProblemRecord::getProblemRecords(database).size());
        EXPECT_EQ(1u,MeasureGroupRecord::getMeasureGroupRecords(database).size());
        EXPECT_EQ(1u,RubyMeasureRecord::getRubyMeasureRecords(database).size());
        EXPECT_EQ(1u,FileReferenceRecord::getFileReferenceRecords(database).size());
        EXPECT_EQ(0u,OSArgumentRecord::getOSArgumentRecords(database).size());

        Problem problemCopy = ProblemRecord::getProblemRecords(database)[0].problem();
        InputVariableVector vars = problemCopy.variables();
        ASSERT_FALSE(vars.empty());
        ASSERT_TRUE(vars[0].optionalCast<MeasureGroup>());
        MeasureVector dps = vars[0].cast<MeasureGroup>().measures(false);
        ASSERT_FALSE(dps.empty());
        ASSERT_TRUE(dps[0].optionalCast<RubyMeasure>());
        RubyMeasure rp = dps[0].cast<RubyMeasure>();
        EXPECT_TRUE(rp.usesBCLMeasure());
        EXPECT_TRUE(rp.measure());
        EXPECT_EQ(tempDir2,rp.measureDirectory());
        EXPECT_NE(measure.uuid(),rp.measureUUID());
        EXPECT_NE(measure.versionUUID(),rp.measureVersionUUID());
        EXPECT_ANY_THROW(rp.perturbationScript());
        ASSERT_EQ(0u,rp.arguments().size());
        EXPECT_FALSE(rp.hasIncompleteArguments());
    }

    boost::filesystem::remove_all(tempDir1);
    boost::filesystem::remove_all(tempDir2);
}
TEST_F(AnalysisFixture,DDACEAlgorithm_CompatibleProblemType) {
  // continuous problem with five variables
  VariableVector variables;
  BCLMeasure bclMeasure(resourcesPath() / toPath("utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade"));
  RubyMeasure measure(bclMeasure);
  variables.push_back(RubyContinuousVariable("Var 1",OSArgument::makeDoubleArgument("wwr1"),measure));
  variables.push_back(RubyContinuousVariable("Var 2",OSArgument::makeDoubleArgument("wwr2"),measure));
  variables.push_back(RubyContinuousVariable("Var 3",OSArgument::makeDoubleArgument("wwr3"),measure));
  variables.push_back(RubyContinuousVariable("Var 4",OSArgument::makeDoubleArgument("wwr4"),measure));
  variables.push_back(RubyContinuousVariable("Var 5",OSArgument::makeDoubleArgument("wwr5"),measure));
  Problem cProblem("Continuous Problem",variables,runmanager::Workflow());
  EXPECT_EQ(5,cProblem.numVariables());
  EXPECT_EQ(5,cProblem.numContinuousVariables());
  variables.clear();

  // mixed problem with three variables, ignorable discrete variable
  MeasureVector measures;
  variables.push_back(RubyContinuousVariable("Var 1",OSArgument::makeDoubleArgument("wwr1"),measure));
  measures.push_back(RubyMeasure(toPath("script.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Var 2",measures));
  measures.clear();
  variables.push_back(RubyContinuousVariable("Var 3",OSArgument::makeDoubleArgument("wwr3"),measure));
  Problem mProblem("Mixed Problem",variables,runmanager::Workflow());
  EXPECT_EQ(3,mProblem.numVariables());
  EXPECT_EQ(2,mProblem.numContinuousVariables());
  EXPECT_EQ(1,mProblem.numDiscreteVariables());
  EXPECT_EQ(1,mProblem.numStaticTransformations());
  variables.clear();

  // discrete problem
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(toPath("script1.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("wwr","0.2");
  measures.push_back(RubyMeasure(toPath("script1.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("wwr","0.4");
  variables.push_back(MeasureGroup("Var 1",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.0");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.3");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.5");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.3");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.0");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.5");
  measures.push_back(RubyMeasure(toPath("script2.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
  measures.back().cast<RubyMeasure>().addArgument("cop","3.5");
  measures.back().cast<RubyMeasure>().addArgument("fan_eff","0.5");
  variables.push_back(MeasureGroup("Var 2",measures));
  measures.clear();
  Problem dProblem("Discrete Problem",variables,runmanager::Workflow());
  EXPECT_EQ(2,dProblem.numVariables());
  EXPECT_EQ(2,dProblem.numDiscreteVariables());
  EXPECT_EQ(0,dProblem.numStaticTransformations());
  variables.clear();

  // box-behnken
  DDACEAlgorithmOptions options(DDACEAlgorithmType::box_behnken);
  DDACEAlgorithm algorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  options.setSamples(DDACEAlgorithmOptions::samplesForBoxBehnken(cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(81,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_EQ(5,DDACEAlgorithmOptions::samplesForBoxBehnken(mProblem));
  EXPECT_EQ(0,DDACEAlgorithmOptions::samplesForBoxBehnken(dProblem));

  // central-composite
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::central_composite);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  options.setSamples(DDACEAlgorithmOptions::samplesForCentralComposite(cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(43,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_EQ(9,DDACEAlgorithmOptions::samplesForCentralComposite(mProblem));
  EXPECT_EQ(0,DDACEAlgorithmOptions::samplesForCentralComposite(dProblem));

  // grid
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::grid);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_FALSE(algorithm.isCompatibleProblemType(dProblem));
  EXPECT_TRUE(options.setSamplesForGrid(3,cProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().symbols());
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(3,algorithm.ddaceAlgorithmOptions().symbols().get());
  EXPECT_EQ(243,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_TRUE(algorithm.ddaceAlgorithmOptions().setSamplesForGrid(5,mProblem));
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().symbols());
  ASSERT_TRUE(algorithm.ddaceAlgorithmOptions().samples());
  EXPECT_EQ(5,algorithm.ddaceAlgorithmOptions().symbols().get());
  EXPECT_EQ(25,algorithm.ddaceAlgorithmOptions().samples().get());
  EXPECT_FALSE(options.setSamplesForGrid(2,dProblem));

  // lhs
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::lhs);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // oa_lhs
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::oa_lhs);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // oas
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::oas);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));

  // random
  options = DDACEAlgorithmOptions(DDACEAlgorithmType::random);
  algorithm = DDACEAlgorithm(options);
  EXPECT_TRUE(algorithm.isCompatibleProblemType(cProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(mProblem));
  EXPECT_TRUE(algorithm.isCompatibleProblemType(dProblem));
}
示例#10
0
TEST_F(AnalysisFixture, Problem_Constructors) {
  VariableVector variables;
  MeasureVector measures;
  runmanager::Workflow workflow;

  // almost-default constructor
  Problem problem("Problem",variables,workflow);
  EXPECT_EQ(0,problem.numVariables());
  OptionalInt combinatorialSize = problem.combinatorialSize(true);
  ASSERT_TRUE(combinatorialSize);
  EXPECT_EQ(0,*combinatorialSize);

  // variables with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  openstudio::path rubyScriptPath = toPath(rubyLibDir()) /
                                    toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 3",measures));
  problem = Problem("Problem",variables,workflow);
  EXPECT_EQ(3,problem.numVariables());
  EXPECT_EQ(6,problem.combinatorialSize(true).get());

  // variables with inconistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);

  // variables and non-null workflow with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 1",measures));
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  problem = Problem("Problem",variables,workflow);
  problem.setDisplayName("Display Name");
  problem.setDescription("long winded description");
  EXPECT_EQ(1,problem.numVariables());
  EXPECT_EQ(2,problem.combinatorialSize(true).get());

  // deserialization
  Problem problemCopy(problem.uuid(),
                      problem.versionUUID(),
                      problem.name(),
                      problem.displayName(),
                      problem.description(),
                      problem.workflow(),
                      problem.responses());
  EXPECT_FALSE(problem == problemCopy); // different impls
  EXPECT_TRUE(problem.uuid() == problemCopy.uuid());
  EXPECT_TRUE(problem.versionUUID() == problemCopy.versionUUID());
  EXPECT_EQ(problem.name(),problemCopy.name());
  EXPECT_EQ(problem.displayName(),problemCopy.displayName());
  EXPECT_EQ(problem.description(),problemCopy.description());
  EXPECT_TRUE(problem.workflow() == problemCopy.workflow());

  // variables and non-null workflow with inconsistent file types
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::ModelToIdf);
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);
}
TEST_F(AnalysisFixture, SequentialSearch) {
  // define dummy problem
  VariableVector variables;
  std::stringstream ss;
  for (int i = 0; i < 5; ++i) {
    MeasureVector measures;
    measures.push_back(NullMeasure());
    measures.push_back(RubyMeasure(toPath("in.rb"),FileReferenceType::OSM,FileReferenceType::OSM));
    ss << "var " << i + 1;
    variables.push_back(MeasureGroup(ss.str(),measures));
    ss.str("");
  }
  FunctionVector functions;
  functions.push_back(LinearFunction("",VariableVector(1u,OutputAttributeContinuousVariable("f1","f1"))));
  functions.push_back(LinearFunction("",VariableVector(1u,OutputAttributeContinuousVariable("f2","f2"))));
  OptimizationProblem problem("By-Hand Problem",functions,variables,runmanager::Workflow());

  // solve dummy problem and check results
  SequentialSearch algorithm(SequentialSearchOptions(0));
  FileReference seed(toPath("in.osm"));
  Analysis analysis("By-Hand Analysis",problem,algorithm,seed);

  int numAdded = algorithm.createNextIteration(analysis);
  EXPECT_EQ(1,numAdded);
  OptimizationDataPointVector nextIteration =
      castVector<OptimizationDataPoint>(analysis.dataPointsToQueue());
  EXPECT_EQ(1u,nextIteration.size());
  while (!nextIteration.empty()) {
    int n = analysis.dataPoints().size();
    LOG(Debug,"Conducting iteration " << algorithm.iter() << " of Sequential Search.");
    OptimizationDataPointVector completeDataPoints =
        castVector<OptimizationDataPoint>(analysis.completeDataPoints());
    OptimizationDataPointVector currentDataPoints =
        castVector<OptimizationDataPoint>(analysis.getDataPoints("current"));
    EXPECT_EQ(1u,currentDataPoints.size());
    ASSERT_FALSE(currentDataPoints.empty());
    EXPECT_EQ(unsigned(algorithm.iter()),analysis.getDataPoints("explored").size());
    if (algorithm.iter() == 0) {
      EXPECT_EQ(1,n);
      EXPECT_EQ(0u,completeDataPoints.size());
    }
    else if (algorithm.iter() == 1) {
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(6,n);
      EXPECT_EQ(1u,completeDataPoints.size());
      EXPECT_TRUE(currentDataPoints[0] == completeDataPoints[0]);
      EXPECT_TRUE(completeDataPoints[0].isTag("curve0"));
      EXPECT_TRUE(completeDataPoints[0].isTag("current"));
      EXPECT_TRUE(completeDataPoints[0].isTag("explored"));
      EXPECT_EQ(5u,nextIteration.size());
    }
    else if (algorithm.iter() == 2) {
      EXPECT_DOUBLE_EQ(17.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(19.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(10,n);
      EXPECT_EQ(6u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(4u,nextIteration.size());
    }
    else if (algorithm.iter() == 3) {
      EXPECT_DOUBLE_EQ(16.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(16.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(13,n);
      EXPECT_EQ(10u,completeDataPoints.size());
      EXPECT_EQ(3u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    else if (algorithm.iter() == 4) {
      // backtracking
      EXPECT_DOUBLE_EQ(15.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(19.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(16,n);
      EXPECT_EQ(13u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    else if (algorithm.iter() == 5) {
      // backtracking
      EXPECT_DOUBLE_EQ(18.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(18,n);
      EXPECT_EQ(16u,completeDataPoints.size());
      EXPECT_EQ(2u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(2u,nextIteration.size());
    }
    else if (algorithm.iter() == 6) {
      EXPECT_DOUBLE_EQ(17.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(14.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(20,n);
      EXPECT_EQ(18u,completeDataPoints.size());
      EXPECT_EQ(5u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(2u,nextIteration.size());
    }
    else if (algorithm.iter() == 7) {
      EXPECT_DOUBLE_EQ(20.0,currentDataPoints[0].objectiveValues()[0]);
      EXPECT_DOUBLE_EQ(12.0,currentDataPoints[0].objectiveValues()[1]);
      EXPECT_EQ(23,n);
      EXPECT_EQ(20u,completeDataPoints.size());
      EXPECT_EQ(6u,analysis.getDataPoints("curve0").size());
      EXPECT_EQ(3u,nextIteration.size());
    }
    EXPECT_EQ(static_cast<size_t>(n),completeDataPoints.size() + nextIteration.size());
    for (const OptimizationDataPoint& point : nextIteration) {
      std::vector<QVariant> values = point.variableValues();
      DoubleVector objectiveValues = getObjectiveValues(values);
      for (int i = 0; i < 5; ++i) {
        int value = values[i].toInt();
        if (value == 0) {
          ss << "  ";
        }
        else {
          EXPECT_EQ(1,value);
          ss << i + 1 << " ";
        }
      }
      ss << ": " << objectiveValues[0] << ", " << objectiveValues[1];
      LOG(Debug,ss.str()); ss.str("");

      completeDataPoints.push_back(OptimizationDataPoint(point.uuid(),
                                                         createUUID(),
                                                         "","","",
                                                         problem,
                                                         true,
                                                         false,
                                                         true,
                                                         DataPointRunType::Local,
                                                         values,
                                                         DoubleVector(),
                                                         objectiveValues,
                                                         openstudio::path(),
                                                         boost::none,
                                                         boost::none,
                                                         boost::none,
                                                         boost::none,
                                                         std::vector<openstudio::path>(),
                                                         point.tags(),
                                                         point.outputAttributes())); // DLM: Elaine is this ok?
    }
    EXPECT_EQ(static_cast<size_t>(n),completeDataPoints.size());
    analysis = Analysis(analysis.uuid(),
                        createUUID(),
                        analysis.name(),
                        analysis.displayName(),
                        analysis.description(),
                        problem,
                        algorithm,
                        seed,
                        boost::none,
                        castVector<DataPoint>(completeDataPoints),
                        false,
                        false);
    algorithm.createNextIteration(analysis);
    nextIteration = castVector<OptimizationDataPoint>(analysis.dataPointsToQueue());
  }

  EXPECT_EQ(23u,analysis.successfulDataPoints().size());
  OptimizationDataPointVector minimumCurve = algorithm.getMinimumCurve(0,analysis);
  ASSERT_EQ(6u,minimumCurve.size());
  DoubleVector values = minimumCurve[0].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(20.0,values[1]);
  values = minimumCurve[1].objectiveValues();
  EXPECT_DOUBLE_EQ(18.0,values[0]); EXPECT_DOUBLE_EQ(20.0,values[1]);
  values = minimumCurve[2].objectiveValues();
  EXPECT_DOUBLE_EQ(15.0,values[0]); EXPECT_DOUBLE_EQ(19.0,values[1]);
  values = minimumCurve[3].objectiveValues();
  EXPECT_DOUBLE_EQ(16.0,values[0]); EXPECT_DOUBLE_EQ(16.0,values[1]);
  values = minimumCurve[4].objectiveValues();
  EXPECT_DOUBLE_EQ(17.0,values[0]); EXPECT_DOUBLE_EQ(14.0,values[1]);
  values = minimumCurve[5].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(12.0,values[1]);
  OptimizationDataPointVector paretoFront = algorithm.getParetoFront(analysis);
  for (const OptimizationDataPoint& paretoPoint : paretoFront) {
    EXPECT_FALSE(std::find(minimumCurve.begin(),minimumCurve.end(),paretoPoint) == minimumCurve.end());
  }
  ASSERT_EQ(4u,paretoFront.size());
  // expect same order as curve0, although different method for calculating
  values = paretoFront[0].objectiveValues();
  EXPECT_DOUBLE_EQ(15.0,values[0]); EXPECT_DOUBLE_EQ(19.0,values[1]); // minimizes f0
  values = paretoFront[1].objectiveValues();
  EXPECT_DOUBLE_EQ(16.0,values[0]); EXPECT_DOUBLE_EQ(16.0,values[1]);
  values = paretoFront[2].objectiveValues();
  EXPECT_DOUBLE_EQ(17.0,values[0]); EXPECT_DOUBLE_EQ(14.0,values[1]);
  values = paretoFront[3].objectiveValues();
  EXPECT_DOUBLE_EQ(20.0,values[0]); EXPECT_DOUBLE_EQ(12.0,values[1]); // minimizes f1
}