diff --git a/src/lookup/AbstractUntemplatedLookupTableGenerator.hpp b/src/lookup/AbstractUntemplatedLookupTableGenerator.hpp index f4625a0..a315685 100644 --- a/src/lookup/AbstractUntemplatedLookupTableGenerator.hpp +++ b/src/lookup/AbstractUntemplatedLookupTableGenerator.hpp @@ -228,6 +228,19 @@ class AbstractUntemplatedLookupTableGenerator */ virtual void SetPacingFrequency(double frequency) = 0; + /** + * Set the maximum number of evaluations (simulations) to run at once, each on its own thread. + * Defaults to the number of cores available on the machine. + * + * @param numThreads The number of threads to use (at least 1). + */ + virtual void SetNumThreads(unsigned numThreads) = 0; + + /** + * @return The maximum number of evaluations that will run at once. + */ + virtual unsigned GetNumThreads() const = 0; + /** * Helper method that just returns DIM, to avoid template chaos. */ diff --git a/src/lookup/LookupTableGenerator.cpp b/src/lookup/LookupTableGenerator.cpp index d98ff9f..9033e75 100644 --- a/src/lookup/LookupTableGenerator.cpp +++ b/src/lookup/LookupTableGenerator.cpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -33,10 +33,13 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include // to avoid variable length arrays. -#include // for setprecision() -#include // for pthread_create, pthread_join, etc -#include // Timing delays to make pthreads behave themselves +#include +#include +#include +#include // for setprecision() +#include +#include +#include #include "FileFinder.hpp" #include "LookupTableGenerator.hpp" @@ -44,19 +47,8 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SetupModel.hpp" #include "SingleActionPotentialPrediction.hpp" -void *ThreadedActionPotential(void *argument); // Forward declaration. - -struct ThreadReturnData -{ - bool exceptionOccurred; - std::string exceptionMessage; - unsigned errorOccurred; - std::vector QoIs; -}; - struct ThreadInputData { - std::vector scalings; std::vector mParameterNames; std::vector mUnscaledParameters; std::vector mQuantitiesToRecord; @@ -67,12 +59,172 @@ struct ThreadInputData double mVoltageThreshold; }; +/** + * Run an action potential simulation with some parameters scaled, and work out the QoIs. + * Safe to call from several threads at once. + * + * @param rData Details of the model and QoIs. + * @param rScalings The scaling factor to apply to each parameter. + * @param rQoIs Filled with the quantities of interest. + * @param rErrorCode Set to the error code from the AP evaluation (0 if no error). + */ +void EvaluateActionPotential(const ThreadInputData& rData, + const std::vector& rScalings, + std::vector& rQoIs, + unsigned& rErrorCode); + +/** + * Runs evaluations of QoIs, one std::thread per evaluation, and hands back + * the results in the order that they finish. + * + * Only the thread that owns this object should call its methods. The destructor waits for any + * evaluations that are still running. + */ +class EvaluationThreads +{ +public: + /** The result of one evaluation */ + struct Result + { + /** The ID the evaluation was launched with */ + unsigned id; + /** The QoIs that were evaluated */ + std::vector qois; + /** The error code from the evaluation */ + unsigned errorCode; + /** Whether the evaluation threw an exception */ + bool exceptionOccurred; + /** The message of the exception, if there was one */ + std::string exceptionMessage; + }; + + /** + * Constructor + * + * @param rEvaluate The function to run on each thread. + * @param launchDelay A pause (in seconds) after launching each thread. + */ + EvaluationThreads(const LookupTableEvaluationFunction& rEvaluate, double launchDelay) + : mEvaluate(rEvaluate), + mLaunchDelay(launchDelay) + { + } + + /** Destructor - waits for all running evaluations to finish */ + ~EvaluationThreads() + { + for (auto& r_thread : mThreads) + { + r_thread.second.join(); + } + } + + /** @return The number of evaluations that are running or have finished but not been collected. */ + unsigned GetNumRunning() const + { + return mThreads.size(); + } + + /** + * Start an evaluation on a new thread. + * + * @param id An ID for this evaluation, which is returned with its result. + * @param rScalings The parameter scalings to evaluate at. + */ + void Launch(unsigned id, const std::vector& rScalings) + { + assert(mThreads.find(id) == mThreads.end()); + mThreads[id] = std::thread([this, id, rScalings]() + { + Result result; + result.id = id; + result.errorCode = 0u; + result.exceptionOccurred = false; + try + { + mEvaluate(rScalings, result.qois, result.errorCode); + } + catch (Exception& e) + { + result.exceptionOccurred = true; + result.exceptionMessage = e.GetShortMessage(); + } + catch (std::exception& e) + { + result.exceptionOccurred = true; + result.exceptionMessage = e.what(); + } + { + std::lock_guard lock(mMutex); + mFinished.push_back(result); + } + mFinishedCondition.notify_one(); + }); + + // Historically we have had seg. faults when launching AP simulation threads simultaneously, + // so we stagger them a little. + if (mLaunchDelay > 0.0) + { + std::this_thread::sleep_for(std::chrono::duration(mLaunchDelay)); + } + } + + /** + * Wait for any running evaluation to finish. + * + * @return Its result. + */ + Result WaitForResult() + { + assert(!mThreads.empty()); + Result result; + { + std::unique_lock lock(mMutex); + mFinishedCondition.wait(lock, [this]() { return !mFinished.empty(); }); + result = mFinished.front(); + mFinished.pop_front(); + } + mThreads[result.id].join(); + mThreads.erase(result.id); + return result; + } + +private: + /** The function that evaluates QoIs */ + LookupTableEvaluationFunction mEvaluate; + + /** A pause (in seconds) after launching each thread */ + double mLaunchDelay; + + /** The threads that have been launched and not yet collected, by ID */ + std::map mThreads; + + /** Protects #mFinished */ + std::mutex mMutex; + + /** Signalled when an evaluation finishes */ + std::condition_variable mFinishedCondition; + + /** Results of evaluations that have finished but not been collected */ + std::deque mFinished; +}; + +/** + * @return The number of threads to use by default, the number of cores on this machine. + */ +unsigned DefaultNumThreads() +{ + unsigned num_cores = std::thread::hardware_concurrency(); + return (num_cores > 0u) ? num_cores : 1u; +} + /* Private constructor - just for archiving */ template LookupTableGenerator::LookupTableGenerator() : AbstractUntemplatedLookupTableGenerator(), mModelIndex(0u), - mpParentBox(NULL){}; + mpParentBox(NULL), + mNumThreads(DefaultNumThreads()){}; template LookupTableGenerator::LookupTableGenerator( @@ -89,7 +241,8 @@ LookupTableGenerator::LookupTableGenerator( mMaxRefinementDifference(UNSIGNED_UNSET), mpParentBox(new ParameterBox(NULL)), mMaxNumPaces(UNSIGNED_UNSET), - mVoltageThreshold(-50.0) + mVoltageThreshold(-50.0), + mNumThreads(DefaultNumThreads()) { // empty } @@ -140,8 +293,8 @@ bool LookupTableGenerator::GenerateLookupTable() } *p_file << std::endl; - // Do a few special things the first time round. - if (!mGenerationHasBegun) + // Do a few special things the first time round (not needed for a test evaluation function). + if (!mGenerationHasBegun && !mEvaluationFunctionForTesting) { std::cout << "Generating from fresh" << std::endl; // Provide an initial guess for steady state ICs. @@ -178,13 +331,37 @@ bool LookupTableGenerator::GenerateLookupTable() mVoltageThreshold = ap_runner.DetectVoltageThresholdForActionPotential(); } p_model->SetStateVariables(mInitialConditions); // Put the model back to sensible state + } + + // Work out how to evaluate QoIs at each point. + LookupTableEvaluationFunction evaluate = mEvaluationFunctionForTesting; + double launch_delay = 0.0; + if (!evaluate) + { + ThreadInputData input_data; + input_data.mParameterNames = mParameterNames; + input_data.mUnscaledParameters = mUnscaledParameters; + input_data.mQuantitiesToRecord = mQuantitiesToRecord; + input_data.mInitialConditions = mInitialConditions; + input_data.mMaxNumPaces = mMaxNumPaces; + input_data.mModelIndex = mModelIndex; + input_data.mFrequency = mFrequency; + input_data.mVoltageThreshold = mVoltageThreshold; + evaluate = [input_data](const std::vector& rScalings, std::vector& rQoIs, unsigned& rErrorCode) + { + EvaluateActionPotential(input_data, rScalings, rQoIs, rErrorCode); + }; + launch_delay = 0.1; // seconds + } + if (!mGenerationHasBegun) + { // Initial scalings CornerSet set_of_points = mpParentBox->GetCorners(); assert(set_of_points.size() == pow(2, DIM)); // Run these initial evaluations multi-threaded. - RunEvaluationsForThesePoints(set_of_points, p_file); + RunEvaluationsForThesePoints(set_of_points, evaluate, launch_delay, p_file); mGenerationHasBegun = true; } @@ -223,34 +400,7 @@ bool LookupTableGenerator::GenerateLookupTable() for (unsigned quantitiy_idx = 0u; quantitiy_idx < mQuantitiesToRecord.size(); quantitiy_idx++) { - // While we are still less than the maximum number of evaluations then - // refine boxes. - bool meets_tolerance = false; - while (mNumEvaluations < mMaxNumEvaluations) - { - // Find which parameter box has the largest variation between its corners - ParameterBox *p_box = mpParentBox->FindBoxWithLargestQoIErrorEstimate( - quantitiy_idx, mQoITolerances[quantitiy_idx], - mMaxRefinementDifference); - - // If we don't get a box back, then we can quit this while loop, - // as variation in this QoI is within tols. - if (!p_box) - { - std::cout - << "Error estimates are within requested tolerances... finishing\n" - << std::flush; - meets_tolerance = true; - break; - } - - // Subdivide this box (NB if we GetCorners() after this, - // it includes the new points and makes no sense!). - CornerSet new_parameter_points = p_box->SubDivide(); - - // Evaluate at these points. - RunEvaluationsForThesePoints(new_parameter_points, p_file); - } + bool meets_tolerance = RefineForQuantityOfInterest(quantitiy_idx, evaluate, launch_delay, p_file); if (meets_tolerance && quantitiy_idx == 0u) { @@ -276,203 +426,350 @@ bool LookupTableGenerator::GenerateLookupTable() } template -void LookupTableGenerator::RunEvaluationsForThesePoints( - CornerSet setOfPoints, out_stream &rFile) +bool LookupTableGenerator::QueuedBoxPriorityCompare::operator()(const QueuedBox& rA, const QueuedBox& rB) const { - // Setup variables to control threading - unsigned num_threads = setOfPoints.size(); - boost::scoped_array thread_data(new ThreadInputData[num_threads]); - // struct ThreadInputData thread_data[num_threads]; - boost::scoped_array answers(new void *[num_threads]); - int return_code; - - // Generate the threads - boost::scoped_array threads(new pthread_t[num_threads]); - - // Create a couple of counters for convenience - CornerSetIter iter; - int i; - - /* - *This loop launches each of the threads. - */ - for (iter = setOfPoints.begin(), i = 0; - iter != setOfPoints.end(); - ++iter, ++i) + if (rA.numErrorCodes != rB.numErrorCodes) { - std::vector scalings; - for (unsigned j = 0; j < DIM; j++) - { - scalings.push_back((*(*iter))[j]); - } - thread_data[i].scalings = scalings; - thread_data[i].mParameterNames = mParameterNames; - thread_data[i].mUnscaledParameters = mUnscaledParameters; - thread_data[i].mQuantitiesToRecord = mQuantitiesToRecord; - thread_data[i].mInitialConditions = mInitialConditions; - thread_data[i].mMaxNumPaces = mMaxNumPaces; - thread_data[i].mModelIndex = mModelIndex; - thread_data[i].mFrequency = mFrequency; - thread_data[i].mVoltageThreshold = mVoltageThreshold; - - // std::cout << "Launching pthread[" << i << "]" << std::endl; + // We prioritise refining boxes in well-behaved space over those on edges of regions with errors. + return rA.numErrorCodes < rB.numErrorCodes; + } + if (rA.errorEstimate != rB.errorEstimate) + { + return rA.errorEstimate > rB.errorEstimate; + } + if (rA.refinementLevel != rB.refinementLevel) + { + return rA.refinementLevel < rB.refinementLevel; + } + // Leaf boxes don't overlap, so no two have the same minimum corner. + return c_vector_compare()(&rA.min, &rB.min); +} - // Launch the ThreadedActionPotential method on this thread - return_code = pthread_create(&threads[i], NULL, ThreadedActionPotential, - (void *)&thread_data[i]); +template +bool LookupTableGenerator::QueuedBoxLevelCompare::operator()(const QueuedBox& rA, const QueuedBox& rB) const +{ + if (rA.refinementLevel != rB.refinementLevel) + { + return rA.refinementLevel < rB.refinementLevel; + } + return QueuedBoxPriorityCompare()(rA, rB); +} - assert(0 == return_code); // Check launch worked OK - EXCEPT_IF_NOT(0 == return_code); +template +void LookupTableGenerator::CollectLeafBoxes(ParameterBox* pBox, std::vector*>& rLeaves) +{ + if (!pBox->mAmParent) + { + rLeaves.push_back(pBox); + return; + } + for (unsigned i = 0; i < pBox->mDaughterBoxes.size(); i++) + { + CollectLeafBoxes(pBox->mDaughterBoxes[i], rLeaves); + } +} - // Horrific seg. faults without the below line - bug in p_threads? - usleep(1e5); // 0.1 second pause to allow thread to launch properly! +template +void LookupTableGenerator::EnqueueIfNeedsRefinement(ParameterBox* pBox, + unsigned quantityIndex, + std::set& rQueue, + std::set& rQueueByLevel) +{ + if (!pBox->DoesBoxNeedFurtherRefinement(mQoITolerances[quantityIndex], quantityIndex)) + { + return; } + QueuedBox queued_box; + queued_box.pBox = pBox; + queued_box.numErrorCodes = pBox->GetNumErrors(); + queued_box.errorEstimate = pBox->GetMaxErrorInQoIEstimateInThisBox(quantityIndex); + queued_box.refinementLevel = pBox->GetRefinementLevel(); + queued_box.min = pBox->mMin; + rQueue.insert(queued_box); + rQueueByLevel.insert(queued_box); +} - /* - * This loop gets the answers back from all the threads. - */ - for (iter = setOfPoints.begin(), i = 0; - iter != setOfPoints.end(); - ++iter, ++i) - { - // Get the answers back - return_code = pthread_join(threads[i], &answers[i]); - assert(0 == return_code); - EXCEPT_IF_NOT(0 == return_code); - - // Translate back from the structs to sensible formats. - ThreadReturnData *thread_results = (ThreadReturnData *)answers[i]; - if (thread_results->exceptionOccurred) - { - EXCEPTION( - "A thread threw the exception: " << thread_results->exceptionMessage); - } +template +bool LookupTableGenerator::RefineForQuantityOfInterest(unsigned quantityIndex, + const LookupTableEvaluationFunction& rEvaluate, + double launchDelay, + out_stream& rFile) +{ + // A subdivision into 2^DIM boxes (the original refinement scheme) counts as DIM refinement levels, + // so we scale the maximum difference in refinement to keep its meaning the same. + unsigned max_refinement_level_difference = UNSIGNED_UNSET; + if (mMaxRefinementDifference != UNSIGNED_UNSET) + { + max_refinement_level_difference = mMaxRefinementDifference * DIM; + } - unsigned error_occurred = thread_results->errorOccurred; - std::vector results = thread_results->QoIs; - c_vector *p_scalings = *iter; - delete thread_results; // Clean up memory + // The boxes that need refining. All their corners have been evaluated, so their rankings won't change. + std::set queue; + std::set queue_by_level; + std::vector*> leaves; + CollectLeafBoxes(mpParentBox, leaves); + for (unsigned i = 0; i < leaves.size(); i++) + { + EnqueueIfNeedsRefinement(leaves[i], quantityIndex, queue, queue_by_level); + } + unsigned most_refined_level = mpParentBox->GetMostRefinedChild()->GetRefinementLevel(); - // Store all the info in the master process and tell boxes about it. - { - boost::shared_ptr data = boost::shared_ptr( - new ParameterPointData(results, error_occurred)); + // Boxes that have been bisected, and are waiting for evaluations at the new points before their + // daughters can be queued. + struct Refinement + { + std::vector*> daughters; + unsigned numPointsOutstanding; + }; + std::vector refinements; - mParameterPoints.push_back(*p_scalings); - mParameterPointData.push_back(data); - mNumEvaluations++; + // Points that are being evaluated (or waiting for a thread), and the refinements waiting for each. + // A point can be shared by refinements of neighbouring boxes, but it is only evaluated once. + std::map*, std::vector, c_vector_compare > points_in_progress; + std::deque*> points_to_launch; + std::map*> launched_points; + unsigned next_launch_id = 0u; - // Tell all parameter boxes this information for future refinement. - mpParentBox->AssignQoIValues(p_scalings, data); - // This should have updated our error estimates in the ParameterPointData* + EvaluationThreads threads(rEvaluate, launchDelay); - std::stringstream line_of_output; - line_of_output << std::setprecision(8); - for (unsigned j = 0; j < DIM; j++) + while (true) + { + // Refine boxes from the top of the queue until there is enough work for all the threads. + while (threads.GetNumRunning() + points_to_launch.size() < mNumThreads + && mNumEvaluations + points_in_progress.size() < mMaxNumEvaluations + && !queue.empty()) + { + QueuedBox next_box = *(queue.begin()); + + // Check the selected box isn't going to refine one area too much, + // if it is refine the least refined area instead. + const QueuedBox& r_least_refined = *(queue_by_level.begin()); + if (max_refinement_level_difference != UNSIGNED_UNSET + && most_refined_level - r_least_refined.refinementLevel >= max_refinement_level_difference + && next_box.refinementLevel == most_refined_level) + { + next_box = r_least_refined; + } + queue.erase(next_box); + queue_by_level.erase(next_box); + + // Bisect this box along the dimension with the largest error estimate + // (NB if we GetCorners() after this, it includes the new points and makes no sense!). + ParameterBox* p_box = next_box.pBox; + unsigned dimension_to_split = p_box->ChooseDimensionToSplit(quantityIndex); + CornerSet new_points = p_box->SubDivide(dimension_to_split); + most_refined_level = std::max(most_refined_level, next_box.refinementLevel + 1u); + + Refinement refinement; + refinement.daughters = p_box->mDaughterBoxes; + refinement.numPointsOutstanding = new_points.size(); + refinements.push_back(refinement); + const unsigned refinement_idx = refinements.size() - 1u; + + for (CornerSetIter iter = new_points.begin(); iter != new_points.end(); ++iter) { - line_of_output << (*p_scalings)[j] << "\t"; + if (points_in_progress.find(*iter) == points_in_progress.end()) + { + points_to_launch.push_back(*iter); + } + points_in_progress[*iter].push_back(refinement_idx); } - line_of_output << error_occurred; - for (unsigned j = 0; j < results.size(); j++) + + // If the new points had all been evaluated already, the daughters have error estimates now. + if (new_points.empty()) { - line_of_output << "\t" << results[j]; + for (unsigned i = 0; i < refinement.daughters.size(); i++) + { + EnqueueIfNeedsRefinement(refinement.daughters[i], quantityIndex, queue, queue_by_level); + } } - if (data->HasErrorEstimates()) + } + + // Start evaluating points if there are threads free. + while (!points_to_launch.empty() && threads.GetNumRunning() < mNumThreads) + { + c_vector* p_point = points_to_launch.front(); + points_to_launch.pop_front(); + std::vector scalings(p_point->begin(), p_point->end()); + launched_points[next_launch_id] = p_point; + threads.Launch(next_launch_id, scalings); + next_launch_id++; + } + + if (threads.GetNumRunning() == 0u) + { + // Nothing is running, so either the queue is empty or we have done enough evaluations. + assert(points_to_launch.empty()); + break; + } + + // Wait for an evaluation to finish and record it. + EvaluationThreads::Result result = threads.WaitForResult(); + if (result.exceptionOccurred) + { + EXCEPTION("A thread threw the exception: " << result.exceptionMessage); + } + c_vector* p_point = launched_points[result.id]; + launched_points.erase(result.id); + RecordEvaluation(p_point, result.qois, result.errorCode, rFile); + + // Queue the daughters of any refinements that now have all their new points evaluated. + const std::vector& r_waiting_refinements = points_in_progress[p_point]; + for (unsigned i = 0; i < r_waiting_refinements.size(); i++) + { + Refinement& r_refinement = refinements[r_waiting_refinements[i]]; + assert(r_refinement.numPointsOutstanding > 0u); + r_refinement.numPointsOutstanding--; + if (r_refinement.numPointsOutstanding == 0u) { - unsigned num_estimates = data->rGetQoIErrorEstimates().size(); - line_of_output << "\t" << num_estimates; - for (unsigned j = 0; j < num_estimates; j++) + for (unsigned j = 0; j < r_refinement.daughters.size(); j++) { - line_of_output << "\t" << data->rGetQoIErrorEstimates()[j]; - if (i == (int)(num_threads - 1u)) - { - // An extra bit of reporting that might be nice can only be called when all boxes have all corner data - // i.e. when the last thread has finished, so will appear sporadically in the output! - line_of_output << "\t" << mpParentBox->ReportPercentageOfSpaceWhereToleranceIsMetForQoI(mQoITolerances[j], j); - } + assert(r_refinement.daughters[j]->mAllCornersEvaluated); + EnqueueIfNeedsRefinement(r_refinement.daughters[j], quantityIndex, queue, queue_by_level); } } + } + points_in_progress.erase(p_point); + } + + if (queue.empty()) + { + std::cout << "Error estimates are within requested tolerances... finishing\n" + << std::flush; + return true; + } + return false; +} - *rFile << line_of_output.str() << std::endl; +template +void LookupTableGenerator::RecordEvaluation(c_vector* pPoint, + const std::vector& rQoIs, + unsigned errorCode, + out_stream& rFile) +{ + // Store all the info in the master process and tell boxes about it. + boost::shared_ptr data = boost::shared_ptr( + new ParameterPointData(rQoIs, errorCode)); + + mParameterPoints.push_back(*pPoint); + mParameterPointData.push_back(data); + mNumEvaluations++; + + // Tell all parameter boxes this information for future refinement. + mpParentBox->AssignQoIValues(pPoint, data); + // This should have updated our error estimates in the ParameterPointData* + + std::stringstream line_of_output; + line_of_output << std::setprecision(8); + for (unsigned j = 0; j < DIM; j++) + { + line_of_output << (*pPoint)[j] << "\t"; + } + line_of_output << errorCode; + for (unsigned j = 0; j < rQoIs.size(); j++) + { + line_of_output << "\t" << rQoIs[j]; + } + if (data->HasErrorEstimates()) + { + unsigned num_estimates = data->rGetQoIErrorEstimates().size(); + line_of_output << "\t" << num_estimates; + for (unsigned j = 0; j < num_estimates; j++) + { + line_of_output << "\t" << data->rGetQoIErrorEstimates()[j]; + // A report on progress towards meeting the tolerance on this QoI. + line_of_output << "\t" << mpParentBox->ReportPercentageOfSpaceWhereToleranceIsMetForQoI(mQoITolerances[j], j); } } + + *rFile << line_of_output.str() << std::endl; } -void *ThreadedActionPotential(void *argument) +template +void LookupTableGenerator::RunEvaluationsForThesePoints( + CornerSet setOfPoints, + const LookupTableEvaluationFunction& rEvaluate, + double launchDelay, + out_stream& rFile) { - // bool debugging_on = true; + std::vector*> points(setOfPoints.begin(), setOfPoints.end()); + std::vector results(points.size()); + + // Run up to mNumThreads evaluations at once. + { + EvaluationThreads threads(rEvaluate, launchDelay); + unsigned num_launched = 0u; + unsigned num_finished = 0u; + while (num_finished < points.size()) + { + while (num_launched < points.size() && threads.GetNumRunning() < mNumThreads) + { + std::vector scalings(points[num_launched]->begin(), points[num_launched]->end()); + threads.Launch(num_launched, scalings); + num_launched++; + } + EvaluationThreads::Result result = threads.WaitForResult(); + if (result.exceptionOccurred) + { + EXCEPTION("A thread threw the exception: " << result.exceptionMessage); + } + results[result.id] = result; + num_finished++; + } + } - struct ThreadInputData *my_data; - my_data = (struct ThreadInputData *)argument; + // Record the results in the order of the set, so output doesn't depend on thread timings. + for (unsigned i = 0; i < points.size(); i++) + { + RecordEvaluation(points[i], results[i].qois, results[i].errorCode, rFile); + } +} - std::vector scalings = my_data->scalings; - assert(scalings.size() == my_data->mParameterNames.size()); +void EvaluateActionPotential(const ThreadInputData& rData, + const std::vector& rScalings, + std::vector& rQoIs, + unsigned& rErrorCode) +{ + assert(rScalings.size() == rData.mParameterNames.size()); - SetupModel setup(my_data->mFrequency, - my_data->mModelIndex); // Ten tusscher '06 at 1 Hz + SetupModel setup(rData.mFrequency, + rData.mModelIndex); // Ten tusscher '06 at 1 Hz boost::shared_ptr p_model = setup.GetModel(); // Do parameter scalings - for (unsigned i = 0; i < scalings.size(); i++) + for (unsigned i = 0; i < rScalings.size(); i++) { std::string param_name; - if (p_model->HasParameter(my_data->mParameterNames[i])) + if (p_model->HasParameter(rData.mParameterNames[i])) { - param_name = my_data->mParameterNames[i]; + param_name = rData.mParameterNames[i]; } else { - param_name = my_data->mParameterNames[i] + "_scaling_factor"; + param_name = rData.mParameterNames[i] + "_scaling_factor"; } p_model->SetParameter(param_name, - my_data->mUnscaledParameters[i] * (scalings[i])); + rData.mUnscaledParameters[i] * (rScalings[i])); } // Reset the state variables to the 'standard' steady state - N_Vector state_vars = MakeNVector(my_data->mInitialConditions); - p_model->SetStateVariables(state_vars); + p_model->SetStateVariables(rData.mInitialConditions); SingleActionPotentialPrediction ap_runner(p_model); ap_runner.SuppressOutput(); - ap_runner.SetMaxNumPaces(my_data->mMaxNumPaces); + ap_runner.SetMaxNumPaces(rData.mMaxNumPaces); ap_runner.SetLackOfOneToOneCorrespondenceIsError(); ap_runner.SetVoltageThresholdForRecordingAsActionPotential( - my_data->mVoltageThreshold); - - // Call the SingleActionPotentialPrediction methods. - try - { - // if (debugging_on) - // { - // std::stringstream filename; - // for (unsigned i = 0; i < scalings.size(); i++) - // { - // filename << scalings[i] << "_"; - // } - // OdeSolution solution = ap_runner.RunSteadyPacingExperiment(); - // solution.WriteToFile("Debugging_Lookup", filename.str(), "ms", - // 1, false); - // } - // else - // { - ap_runner.RunSteadyPacingExperiment(); - // } - } - catch (Exception &e) - { - ThreadReturnData *return_data = new ThreadReturnData; - return_data->exceptionOccurred = true; - return_data->exceptionMessage = e.GetShortMessage(); + rData.mVoltageThreshold); - DeleteVector(state_vars); - pthread_exit(return_data); - } + // Call the SingleActionPotentialPrediction methods (any exception is passed back to the main thread). + ap_runner.RunSteadyPacingExperiment(); - unsigned error_occurred = ap_runner.GetErrorCode(); // 0 if there was no error + rErrorCode = ap_runner.GetErrorCode(); // 0 if there was no error // Record the results - std::vector results; - for (unsigned i = 0; i < my_data->mQuantitiesToRecord.size(); i++) + rQoIs.clear(); + for (unsigned i = 0; i < rData.mQuantitiesToRecord.size(); i++) { if (ap_runner.DidErrorOccur()) { @@ -483,64 +780,72 @@ void *ThreadedActionPotential(void *argument) // We could use different numerical codes for different errors here if we // wanted to, but for QNet all AP errors are just set to -DBL_MAX. - if (my_data->mQuantitiesToRecord[i] == QNet) + if (rData.mQuantitiesToRecord[i] == QNet) { - results.push_back(-DBL_MAX); + rQoIs.push_back(-DBL_MAX); continue; } // We could use different numerical codes for different errors here if we // wanted to. - if ((error_message == "NoActionPotential_2" || error_message == "NoActionPotential_3") && (my_data->mQuantitiesToRecord[i] == Apd90 || my_data->mQuantitiesToRecord[i] == Apd50)) + if ((error_message == "NoActionPotential_2" || error_message == "NoActionPotential_3") && (rData.mQuantitiesToRecord[i] == Apd90 || rData.mQuantitiesToRecord[i] == Apd50)) { // For an APD calculation failure on repolarisation put in the stimulus // period. double stim_period = boost::static_pointer_cast( p_model->GetStimulusFunction()) ->GetPeriod(); - results.push_back(stim_period); + rQoIs.push_back(stim_period); } else { // For everything else (failure to depolarize "NoActionPotential_1") // just put in zero for now. - results.push_back(0.0); + rQoIs.push_back(0.0); } continue; } // No error cases double temp; - if (my_data->mQuantitiesToRecord[i] == Apd90) + if (rData.mQuantitiesToRecord[i] == Apd90) { temp = ap_runner.GetApd90(); } - else if (my_data->mQuantitiesToRecord[i] == Apd50) + else if (rData.mQuantitiesToRecord[i] == Apd50) { temp = ap_runner.GetApd50(); } - else if (my_data->mQuantitiesToRecord[i] == UpstrokeVelocity) + else if (rData.mQuantitiesToRecord[i] == UpstrokeVelocity) { temp = ap_runner.GetUpstrokeVelocity(); } - else if (my_data->mQuantitiesToRecord[i] == PeakVoltage) + else if (rData.mQuantitiesToRecord[i] == PeakVoltage) { temp = ap_runner.GetPeakVoltage(); } - else if (my_data->mQuantitiesToRecord[i] == QNet) + else if (rData.mQuantitiesToRecord[i] == QNet) { temp = ap_runner.CalculateQNet(); } - results.push_back(temp); + rQoIs.push_back(temp); } +} - ThreadReturnData *return_data = new ThreadReturnData; - return_data->QoIs = results; - return_data->errorOccurred = error_occurred; - return_data->exceptionOccurred = false; +template +void LookupTableGenerator::SetNumThreads(unsigned numThreads) +{ + if (numThreads == 0u) + { + EXCEPTION("The number of threads must be at least one."); + } + mNumThreads = numThreads; +} - DeleteVector(state_vars); - pthread_exit(return_data); +template +unsigned LookupTableGenerator::GetNumThreads() const +{ + return mNumThreads; } template diff --git a/src/lookup/LookupTableGenerator.hpp b/src/lookup/LookupTableGenerator.hpp index 4079424..9037d45 100644 --- a/src/lookup/LookupTableGenerator.hpp +++ b/src/lookup/LookupTableGenerator.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -37,6 +37,7 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define LOOKUPTABLEGENERATOR_HPP_ #include +#include #include // Seems that whatever version of ublas we are using now contains // boost serialization methods for c_vector, which is nice. @@ -57,6 +58,14 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SingleActionPotentialPrediction.hpp" #include "UblasVectorInclude.hpp" // Chaste helper header to get c_vectors included with right namespace. +/** + * A function that evaluates the quantities of interest at a point in parameter space. + * + * Arguments are the parameter scalings, a vector to fill with the QoIs, and an error code + * to set (0 if there was no error). It may be called from several threads at once. + */ +typedef std::function&, std::vector&, unsigned&)> LookupTableEvaluationFunction; + /** * A class that will generate lookup tables in DIM-dimensional parameter space, * populate them, perform refinement, etc. @@ -72,6 +81,17 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * You should add QoIs in order of importance, as the lookup table will be * refined for each in turn. * + * Refinement works by keeping all the ParameterBoxes that need refining in a queue, + * ranked by the number of error codes at their corners (fewest first) and then by their + * error estimate (largest first). Whenever a thread is free the box at the top of the queue + * is bisected along the dimension with the largest error estimate + * (see ParameterBox::ChooseDimensionToSplit()), and its new points are evaluated. Once all of a + * box's new points have been evaluated its daughters have error estimates and are queued in turn. + * With more than one thread the order of refinement depends on which evaluations finish first. + * + * Tables generated with older versions of this class (which divided boxes into 2^DIM) can + * still be loaded, interpolated and refined further. + * * If you ever add a new DIM (6 or more) be sure to add new explicit instantiation and boost * serialization export wrappers... */ @@ -199,14 +219,123 @@ class LookupTableGenerator : public AbstractUntemplatedLookupTableGenerator double mVoltageThreshold; /** - * This method will farm out the evaluation of a set of points using - * multi-threading. - * - * @param setOfPoints A collection of points in parameter space at which to - * evaluate QoIs. - * @param rFile The output file to write a line of results into. - */ - void RunEvaluationsForThesePoints(CornerSet setOfPoints, out_stream& rFile); + * The maximum number of evaluations to run at once (each on its own thread). + * Not archived, as it is a property of the machine rather than the table. + */ + unsigned mNumThreads; + + /** + * If set, this is used to evaluate QoIs instead of running action potential simulations. + * Only for testing the refinement algorithm quickly, not archived. + */ + LookupTableEvaluationFunction mEvaluationFunctionForTesting; + + /** A box waiting in the refinement queue, with the quantities it is ranked by. */ + struct QueuedBox + { + /** The box */ + ParameterBox* pBox; + /** The number of its corners at which QoI evaluations gave error codes */ + unsigned numErrorCodes; + /** Its error estimate for the QoI being refined */ + double errorEstimate; + /** Its refinement level, see ParameterBox::GetRefinementLevel() */ + unsigned refinementLevel; + /** Its minimum corner, used to break ties deterministically */ + c_vector min; + }; + + /** + * Ranks queued boxes for refinement: fewest error codes first, then largest error estimate, + * then least refined, then by minimum corner. + */ + struct QueuedBoxPriorityCompare + { + /** + * @param rA a queued box + * @param rB another queued box + * @return whether rA should be refined before rB. + */ + bool operator()(const QueuedBox& rA, const QueuedBox& rB) const; + }; + + /** Ranks queued boxes by refinement level (least refined first), then by QueuedBoxPriorityCompare. */ + struct QueuedBoxLevelCompare + { + /** + * @param rA a queued box + * @param rB another queued box + * @return whether rA is less refined than rB (or ranked higher at the same refinement level). + */ + bool operator()(const QueuedBox& rA, const QueuedBox& rB) const; + }; + + /** + * This method will farm out the evaluation of a set of points using + * multi-threading, and record the results in the order of the set. + * + * @param setOfPoints A collection of points in parameter space at which to + * evaluate QoIs. + * @param rEvaluate The function that evaluates QoIs at a point. + * @param launchDelay A pause (in seconds) after launching each thread. + * @param rFile The output file to write a line of results into. + */ + void RunEvaluationsForThesePoints(CornerSet setOfPoints, + const LookupTableEvaluationFunction& rEvaluate, + double launchDelay, + out_stream& rFile); + + /** + * Refine the lookup table for one QoI until its error estimates meet the tolerance + * everywhere, or we reach the maximum number of evaluations. + * + * Keeps up to #mNumThreads evaluations running at once, refining further boxes from the top of + * the queue whenever a thread is free. Always waits for running evaluations to finish before + * returning, so the table is left in a consistent state for archiving. + * + * @param quantityIndex The index of the QoI to refine for. + * @param rEvaluate The function that evaluates QoIs at a point. + * @param launchDelay A pause (in seconds) after launching each thread. + * @param rFile The output file to write lines of results into. + * @return Whether the tolerance for this QoI is met. + */ + bool RefineForQuantityOfInterest(unsigned quantityIndex, + const LookupTableEvaluationFunction& rEvaluate, + double launchDelay, + out_stream& rFile); + + /** + * Record the result of an evaluation: store it, tell the parameter boxes about it, and write + * it to the output file. + * + * @param pPoint The point in parameter space that was evaluated. + * @param rQoIs The QoIs at this point. + * @param errorCode The error code from the evaluation (0 if no error). + * @param rFile The output file to write a line of results into. + */ + void RecordEvaluation(c_vector* pPoint, + const std::vector& rQoIs, + unsigned errorCode, + out_stream& rFile); + + /** + * Add a box to the refinement queues if it needs further refinement. + * + * @param pBox The box (which must be a leaf with all its corners evaluated). + * @param quantityIndex The index of the QoI being refined. + * @param rQueue The queue ranked by priority. + * @param rQueueByLevel The same boxes ranked by refinement level. + */ + void EnqueueIfNeedsRefinement(ParameterBox* pBox, + unsigned quantityIndex, + std::set& rQueue, + std::set& rQueueByLevel); + + /** + * @param pBox A box. + * @param rLeaves Filled with all the boxes in pBox's family (including itself) that have no children. + */ + void CollectLeafBoxes(ParameterBox* pBox, std::vector*>& rLeaves); /** * Private constructor, just for use in archiving @@ -324,6 +453,9 @@ class LookupTableGenerator : public AbstractUntemplatedLookupTableGenerator * other parts of * the parameter space. * + * The difference is measured in whole levels of refinement, i.e. halving a box in + * every dimension, which takes DIM bisections. + * * @param rMaxRefinementDifference The maximum difference in the parameter * boxes to allow. */ @@ -361,6 +493,19 @@ class LookupTableGenerator : public AbstractUntemplatedLookupTableGenerator */ void SetPacingFrequency(double frequency); + /** + * Set the maximum number of evaluations (simulations) to run at once, each on its own thread. + * Defaults to the number of cores available on the machine. + * + * @param numThreads The number of threads to use (at least 1). + */ + void SetNumThreads(unsigned numThreads); + + /** + * @return The maximum number of evaluations that will run at once. + */ + unsigned GetNumThreads() const; + /** * Helper method that just returns DIM, to avoid template chaos. */ diff --git a/src/lookup/ParameterBox.cpp b/src/lookup/ParameterBox.cpp index 895faff..fc6969f 100644 --- a/src/lookup/ParameterBox.cpp +++ b/src/lookup/ParameterBox.cpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -34,10 +34,14 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include // for binary ops. +#include #include "Exception.hpp" #include "ParameterBox.hpp" +/** Boxes narrower than this in a dimension will not be split any further in that dimension. */ +static const double MIN_SPLITTABLE_WIDTH = 1e-5; + template ParameterBox::ParameterBox(ParameterBox* pParent, const c_vector& rMin, @@ -282,6 +286,99 @@ std::set*, c_vector_compare > ParameterBox::SubD AssignQoIValues(new_corner, predicted_data, true); } + TidyUpAfterSubdivision(); + return new_corners; +} + +template +std::set*, c_vector_compare > ParameterBox::SubDivide(unsigned dimension) +{ + if (dimension >= DIM) + { + EXCEPTION("Cannot subdivide along dimension " << dimension << " of a " << DIM << "D box."); + } + if (mAmParent) + { + EXCEPTION("Already subdivided this box."); + } + + // In order to subdivide a box we require there to be data in existence at all its corners. + assert(mParameterPointDataMap.size() == mCorners.size()); + + // Work out the extent of the two daughter boxes, they share the plane half way along 'dimension'. + const double midpoint = mMin[dimension] + 0.5 * (mMax[dimension] - mMin[dimension]); + c_vector lower_max = mMax; + lower_max[dimension] = midpoint; + c_vector upper_min = mMin; + upper_min[dimension] = midpoint; + + ParameterBox* p_lower_box = new ParameterBox(this, mMin, lower_max); + mDaughterBoxes.push_back(p_lower_box); + ParameterBox* p_upper_box = new ParameterBox(this, upper_min, mMax); + mDaughterBoxes.push_back(p_upper_box); + + // The points on the shared plane are the corners of the lower box + // which are at the top end of 'dimension'. + CornerSet new_corners; + for (unsigned i = 0; i < pow(2, DIM); i++) + { + std::bitset bin_i(i); + if (!bin_i[dimension]) + { + continue; + } + c_vector* p_corner = p_lower_box->mCorners[i]; + + // Generate an estimate of the QoIs at this point based on interpolation of this box. + std::vector predicted_qois; + InterpolatePoint(*p_corner, predicted_qois); + + DataMapIter iter = mpGreatGrandParentBox->mParameterPointDataMap.find(p_corner); + assert(iter != mpGreatGrandParentBox->mParameterPointDataMap.end()); + boost::shared_ptr p_real_data = (*iter).second; + + if (!p_real_data) + { + // This point needs evaluating, store our prediction for comparison with real data later. + // We don't care whether an error actually occurred or not for this. + new_corners.insert(p_corner); + boost::shared_ptr predicted_data = boost::shared_ptr(new ParameterPointData(predicted_qois, 0u)); + AssignQoIValues(p_corner, predicted_data, true); + } + else + { + // This point has already been evaluated (a neighbouring box has been refined), + // so we can work out the error in our prediction right now. + // N.B. we don't set error estimates on the existing data, it belongs to the box that created it. + const std::vector& r_real_values = p_real_data->rGetQoIs(); + assert(predicted_qois.size() == r_real_values.size()); + std::vector errors_in_predictions; + for (unsigned j = 0; j < predicted_qois.size(); j++) + { + errors_in_predictions.push_back(predicted_qois[j] - r_real_values[j]); + } + p_lower_box->mErrorsInQoIs.push_back(errors_in_predictions); + p_upper_box->mErrorsInQoIs.push_back(errors_in_predictions); + } + } + + // If all of the points on the shared plane had already been evaluated, then the daughters' error + // estimates are already complete. + for (unsigned i = 0; i < mDaughterBoxes.size(); i++) + { + if (mDaughterBoxes[i]->mParameterPointDataMapPredictions.size() == 0u) + { + mDaughterBoxes[i]->FinaliseErrorEstimates(); + } + } + + TidyUpAfterSubdivision(); + return new_corners; +} + +template +void ParameterBox::TidyUpAfterSubdivision() +{ // Tidy up things that a parent box doesn't need. mCorners.clear(); mParameterPointDataMapPredictions.clear(); @@ -292,7 +389,6 @@ std::set*, c_vector_compare > ParameterBox::SubD mParameterPointDataMap.clear(); } mAmParent = true; // I am not actually a parent until I clear my own corners! - return new_corners; } template @@ -324,7 +420,7 @@ void ParameterBox::AssignQoIValues(c_vector* pCorner, if (!isPredictedQoI && !(mpGreatGrandParentBox == this)) { DataMapIter iter2 = mParameterPointDataMapPredictions.find(pCorner); - if (iter2 != mParameterPointDataMapPredictions.end()) + if (iter2 != mParameterPointDataMapPredictions.end() && (*iter2).second) { // If it does, we found a prediction, so evaluate it. std::vector predictions = (*iter2).second->rGetQoIs(); @@ -347,27 +443,7 @@ void ParameterBox::AssignQoIValues(c_vector* pCorner, // If we have now evaluated all of the predictions if (mParameterPointDataMapPredictions.size() == 0) { - mAllCornersEvaluated = true; - - mMaxErrorsInEachQoI.clear(); - // Take each QoI error at first corner to be the max for now. - for (unsigned i = 0; i < mErrorsInQoIs[0].size(); i++) - { - mMaxErrorsInEachQoI.push_back(fabs(mErrorsInQoIs[0][i])); - } - - // For each other corner - for (unsigned i = 1u; i < mErrorsInQoIs.size(); i++) - { - // For each QoI. - for (unsigned j = 0; j < mErrorsInQoIs[i].size(); j++) - { - if (fabs(mErrorsInQoIs[i][j]) > mMaxErrorsInEachQoI[j]) - { - mMaxErrorsInEachQoI[j] = fabs(mErrorsInQoIs[i][j]); - } - } - } + FinaliseErrorEstimates(); } } } @@ -380,6 +456,38 @@ void ParameterBox::AssignQoIValues(c_vector* pCorner, } } +template +void ParameterBox::FinaliseErrorEstimates() +{ + mAllCornersEvaluated = true; + + mMaxErrorsInEachQoI.clear(); + if (mErrorsInQoIs.size() == 0u) + { + // Nothing to compare, the error estimates will be treated as unknown. + return; + } + + // Take each QoI error at first corner to be the max for now. + for (unsigned i = 0; i < mErrorsInQoIs[0].size(); i++) + { + mMaxErrorsInEachQoI.push_back(fabs(mErrorsInQoIs[0][i])); + } + + // For each other corner + for (unsigned i = 1u; i < mErrorsInQoIs.size(); i++) + { + // For each QoI. + for (unsigned j = 0; j < mErrorsInQoIs[i].size(); j++) + { + if (fabs(mErrorsInQoIs[i][j]) > mMaxErrorsInEachQoI[j]) + { + mMaxErrorsInEachQoI[j] = fabs(mErrorsInQoIs[i][j]); + } + } + } +} + template bool ParameterBox::DoesBoxNeedFurtherRefinement(const double& rTolerance, const unsigned& rQuantityIndex) @@ -396,18 +504,16 @@ bool ParameterBox::DoesBoxNeedFurtherRefinement(const double& rTolerance, assert(!mAmParent); // Hardcode a stopping criteria based on the width of the box. - c_vector box_width; // should be able to combine with line below but optimised gcc 7.4.0 didn't like it! - box_width = mMax - mMin; - double max_width = -DBL_MAX; + bool any_dimension_splittable = false; for (unsigned i = 0; i < DIM; i++) { - if (box_width[i] > max_width) + if (IsDimensionSplittable(i)) { - max_width = box_width[i]; + any_dimension_splittable = true; + break; } } - double box_width_tolerance = 1e-5; - if (max_width < box_width_tolerance) + if (!any_dimension_splittable) { return false; } @@ -461,8 +567,154 @@ double ParameterBox::GetMaxErrorInQoIEstimateInThisBox(const unsigned& rQua // } // return max - min; - // New QoI error-estimate based measure - return mMaxErrorsInEachQoI[rQuantityIndex]; + // New QoI error-estimate based measure, the largest error estimate in any dimension that we could refine. + // For a box created by a subdivision into 2^DIM boxes this is just mMaxErrorsInEachQoI[rQuantityIndex]. + // NaN estimates (which can arise from QoIs flagged with -DBL_MAX) never trigger refinement. + std::vector errors = GetErrorEstimatesPerDimension(rQuantityIndex); + double max_error = 0.0; + for (unsigned i = 0; i < DIM; i++) + { + if (IsDimensionSplittable(i) && errors[i] > max_error) + { + max_error = errors[i]; + } + } + return max_error; +} + +template +std::vector ParameterBox::GetErrorEstimatesPerDimension(const unsigned& rQuantityIndex) +{ + std::vector errors(DIM, DBL_MAX); // Unknown until we find the split that measured them. + std::vector found(DIM, false); + unsigned num_found = 0u; + + // Walk up the family tree to find the most recent split in each dimension. + ParameterBox* p_box = this; + while (p_box->mpParentBox && num_found < DIM) + { + ParameterBox* p_parent = p_box->mpParentBox; + for (unsigned i = 0; i < DIM; i++) + { + // If this box was created by halving dimension i of its parent. + if (!found[i] && (p_box->mMax[i] - p_box->mMin[i]) < 0.75 * (p_parent->mMax[i] - p_parent->mMin[i])) + { + found[i] = true; + num_found++; + if (p_box->mMaxErrorsInEachQoI.size() > rQuantityIndex) + { + errors[i] = p_box->mMaxErrorsInEachQoI[rQuantityIndex]; + } + } + } + p_box = p_parent; + } + return errors; +} + +template +unsigned ParameterBox::GetRefinementLevel() const +{ + const ParameterBox* p_root = mpGreatGrandParentBox; + double level = 0.0; + for (unsigned i = 0; i < DIM; i++) + { + level += log2((p_root->mMax[i] - p_root->mMin[i]) / (mMax[i] - mMin[i])); + } + return (unsigned)(std::lround(level)); +} + +template +bool ParameterBox::IsDimensionSplittable(unsigned dimension) const +{ + return (mMax[dimension] - mMin[dimension] >= MIN_SPLITTABLE_WIDTH); +} + +template +double ParameterBox::GetQoIVariationAlongDimension(unsigned dimension, const unsigned& rQuantityIndex) +{ + double max_variation = 0.0; + // Corners are in binary order (see constructor), so corners i and i + 2^dimension + // are at either end of an edge parallel to 'dimension'. + for (unsigned i = 0; i < pow(2, DIM); i++) + { + std::bitset bin_i(i); + if (bin_i[dimension]) + { + continue; + } + DataMapIter lower = mParameterPointDataMap.find(mCorners[i]); + DataMapIter upper = mParameterPointDataMap.find(mCorners[i + (1u << dimension)]); + if (lower == mParameterPointDataMap.end() || upper == mParameterPointDataMap.end() + || !(*lower).second || !(*upper).second + || (*lower).second->GetErrorCode() > 0u || (*upper).second->GetErrorCode() > 0u) + { + continue; + } + double variation = fabs((*upper).second->rGetQoIs()[rQuantityIndex] - (*lower).second->rGetQoIs()[rQuantityIndex]); + if (variation > max_variation) + { + max_variation = variation; + } + } + return max_variation; +} + +template +unsigned ParameterBox::ChooseDimensionToSplit(const unsigned& rQuantityIndex) +{ + if (mAmParent) + { + EXCEPTION("This box has already been subdivided."); + } + + std::vector errors = GetErrorEstimatesPerDimension(rQuantityIndex); + + unsigned best_dim = UNSIGNED_UNSET; + double best_error = 0.0; + double best_variation = 0.0; + for (unsigned i = 0; i < DIM; i++) + { + if (!IsDimensionSplittable(i)) + { + continue; + } + + // Treat NaN error estimates as the lowest possible, so the choice is deterministic. + double error = std::isnan(errors[i]) ? -DBL_MAX : errors[i]; + double variation = GetQoIVariationAlongDimension(i, rQuantityIndex); + + bool is_better = false; + if (best_dim == UNSIGNED_UNSET || error > best_error) + { + is_better = true; + } + else if (error == best_error) + { + if (variation > best_variation) + { + is_better = true; + } + else if (variation == best_variation + && (mMax[i] - mMin[i]) > (mMax[best_dim] - mMin[best_dim])) + { + is_better = true; + } + } + + if (is_better) + { + best_dim = i; + best_error = error; + best_variation = variation; + } + } + + if (best_dim == UNSIGNED_UNSET) + { + EXCEPTION("This box is too small to split in any dimension."); + } + return best_dim; } template @@ -536,7 +788,7 @@ void ParameterBox::GetErrorEstimateInAllBoxes(ParameterBox*& pBestBox, template ParameterBox* ParameterBox::FindBoxWithLargestQoIErrorEstimate(const unsigned& rQuantityIndex, const double& rTolerance, - const unsigned& rMaxGenerationDifference) + const unsigned& rMaxRefinementLevelDifference) { // Only the grand parent should call this. If I have a parent I'm not it. if (mpParentBox) @@ -560,8 +812,9 @@ ParameterBox* ParameterBox::FindBoxWithLargestQoIErrorEstimate(const u // Check the selected box isn't going to refine one area too much, // if it is refine least refined area instead. if (least_refined // if an unrefined box exists that doesn't meet the tolerances. - && ((most_refined->GetGeneration() - least_refined->GetGeneration()) == rMaxGenerationDifference) - && (p_box->GetGeneration() == GetMostRefinedChild()->GetGeneration())) + && rMaxRefinementLevelDifference != UNSIGNED_UNSET + && ((most_refined->GetRefinementLevel() - least_refined->GetRefinementLevel()) >= rMaxRefinementLevelDifference) + && (p_box->GetRefinementLevel() == most_refined->GetRefinementLevel())) { return least_refined; } @@ -722,7 +975,7 @@ ParameterBox* ParameterBox::GetMostRefinedChild() for (unsigned i = 0; i < mDaughterBoxes.size(); i++) { ParameterBox* this_daughters_most_refined = mDaughterBoxes[i]->GetMostRefinedChild(); - if (!p_box || this_daughters_most_refined->GetGeneration() > p_box->GetGeneration()) + if (!p_box || this_daughters_most_refined->GetRefinementLevel() > p_box->GetRefinementLevel()) { p_box = this_daughters_most_refined; } @@ -756,7 +1009,7 @@ ParameterBox* ParameterBox::GetLeastRefinedChild(const double& rTolera if (this_daughters_least_refined // If this daughter box needs refinement && (!p_box // and we either don't have a box at the moment, or this box is a lower generation - || this_daughters_least_refined->GetGeneration() < p_box->GetGeneration())) + || this_daughters_least_refined->GetRefinementLevel() < p_box->GetRefinementLevel())) { p_box = this_daughters_least_refined; } @@ -809,7 +1062,9 @@ double ParameterBox::ReportPercentageOfSpaceWhereToleranceIsMetForQoI(const area_box *= widths[j]; } - if (all_boxes[i]->DoesBoxNeedFurtherRefinement(rTolerance, rQuantityIndex)) + // Boxes still waiting for some of their new corners to be evaluated don't have error estimates yet. + if ((all_boxes[i]->mpParentBox && !all_boxes[i]->mAllCornersEvaluated) + || all_boxes[i]->DoesBoxNeedFurtherRefinement(rTolerance, rQuantityIndex)) { area_not += area_box; } diff --git a/src/lookup/ParameterBox.hpp b/src/lookup/ParameterBox.hpp index 7a2c398..7e3874a 100644 --- a/src/lookup/ParameterBox.hpp +++ b/src/lookup/ParameterBox.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -52,6 +52,9 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static const double TOL = 1e-12; +template +class LookupTableGenerator; + /** * A special comparison method to allow std::map to sort and compare * a std::map* > @@ -84,8 +87,13 @@ struct c_vector_compare /** * This class stores the co-ordinates of the corners of N-D boxes. * - * It provides a method for dividing a box into equally sized sub-boxes, * - * and keeps track of their indices too. + * It provides methods for dividing a box into sub-boxes, either into 2^DIM + * equally sized sub-boxes (by halving every dimension), or into two by bisecting + * along a single dimension. It keeps track of their indices too. + * + * Boxes are therefore hyper-rectangles, not necessarily hyper-cubes. A tree may + * contain a mixture of both kinds of subdivision (e.g. a legacy archive that has + * been refined further by bisection). * * It also provides methods to find out which sub-boxes have the most and * least refinement, as well as suggesting the next one to refine. @@ -97,6 +105,9 @@ class ParameterBox /** Needed for serialization. */ friend class boost::serialization::access; friend class TestParameterBox; + friend class TestLookupTableBackwardsCompatibility; + friend class TestLookupTableGenerator; + friend class LookupTableGenerator; /** * Archive the object. * @@ -229,7 +240,59 @@ class ParameterBox ParameterBox(){}; /** - * @return A pointer to (one of) the children with the largest generation number. + * Work out, for each dimension, an estimate of the error in interpolating the given QoI + * across this box in that dimension. + * + * For each dimension we walk up the family tree from this box to the nearest box + * (including this one) that was created by a subdivision which halved that dimension, + * and use the error estimate that was measured when that subdivision was evaluated. + * If no such box exists (i.e. the tree has never been split along this dimension here) + * the error is unknown, and DBL_MAX is returned for that dimension. + * + * Nothing extra needs archiving for this: it is derived from box geometry and + * #mMaxErrorsInEachQoI, so it works for boxes loaded from older archives too. + * + * @param rQuantityIndex The index of the quantity of interest. + * @return An error estimate for each dimension. + */ + std::vector GetErrorEstimatesPerDimension(const unsigned& rQuantityIndex); + + /** + * @return The refinement level of this box, the number of times the original box has been + * halved (in any dimension) to get to this box, i.e. sum over dimensions of log2(1/width). + * + * A box created by g subdivisions into 2^DIM has level DIM*g, + * a box created by g bisections has level g. + */ + unsigned GetRefinementLevel() const; + + /** + * @param dimension The dimension of interest. + * @return Whether this box is wide enough in the given dimension to be split further. + */ + bool IsDimensionSplittable(unsigned dimension) const; + + /** + * @param dimension The dimension of interest. + * @param rQuantityIndex The index of the quantity of interest. + * @return The largest change in the QoI along any edge of this box that is parallel to the + * given dimension (ignoring edges where either corner had an error code). + */ + double GetQoIVariationAlongDimension(unsigned dimension, const unsigned& rQuantityIndex); + + /** + * Called once all the predicted QoIs have been compared with real data, + * to populate #mMaxErrorsInEachQoI from #mErrorsInQoIs. + */ + void FinaliseErrorEstimates(); + + /** + * Clear the data that a box doesn't need once it has become a parent. + */ + void TidyUpAfterSubdivision(); + + /** + * @return A pointer to (one of) the children with the largest refinement level. */ ParameterBox* GetMostRefinedChild(); @@ -240,7 +303,7 @@ class ParameterBox * @param rTolerance The error estimate we are happy with. * @param rQuantityIndex The index of the quantity of interest we are examining at present. * @return A pointer to (one of) the children (that has no children of its own) - * with the smallest generation number. + * with the smallest refinement level. */ ParameterBox* GetLeastRefinedChild(const double& rTolerance, const unsigned& rQuantityIndex); @@ -349,7 +412,7 @@ class ParameterBox /** * Report whether this box needs further refinement or not. * - * Contains a hard-coded limit on the maximum width in any dimension of the box, once this falls + * Contains a hard-coded limit on the width of the box, once every dimension falls * below a certain hard-coded limit we won't refine the boxes any more and will return false. * * @param rTolerance The error estimate in the QoI that we are happy with @@ -391,10 +454,42 @@ class ParameterBox * * Makes this box into a parent and creates daughter boxes. * + * N.B. This was the original refinement scheme used by the LookupTableGenerator, which now uses + * SubDivide(unsigned) instead. + * * @return The new points in parameter space at which quantities of interest need to be evaluated. */ CornerSet SubDivide(); + /** + * Bisect this box along a single dimension into two daughter boxes. + * This requires 2^(DIM-1) new points, on the plane half way along the given dimension. + * + * Makes this box into a parent and creates daughter boxes. + * + * Some (or all) of the points on this plane may already have been evaluated, if neighbouring + * boxes have been refined. In that case the error in this box's prediction at those points + * is recorded immediately. + * + * @param dimension The dimension along which to bisect this box. + * @return The new points in parameter space at which quantities of interest need to be evaluated + * (can be empty). + */ + CornerSet SubDivide(unsigned dimension); + + /** + * Choose the best dimension along which to bisect this box, for a given QoI. + * + * This is the dimension with the largest error estimate (see GetErrorEstimatesPerDimension()), + * with dimensions that have never been split in this part of the tree treated as having + * an infinite error. Ties are broken by the largest variation in the QoI across the box + * in that dimension, then by the widest dimension, then the lowest index. + * + * @param rQuantityIndex The index of the quantity of interest. + * @return The dimension along which to bisect. + */ + unsigned ChooseDimensionToSplit(const unsigned& rQuantityIndex); + /** * Tell this box, and any children, the values of the quantities of interest (QoIs) at a given point. * @@ -408,19 +503,20 @@ class ParameterBox /** * Find the parameter box that has the largest error estimate in a given quantity of interest. - * Will not over-refine one area if a rMaxGenerationDifference is set. + * Will not over-refine one area if a rMaxRefinementLevelDifference is set. * * If all boxes meet the tolerance a null pointer is returned. * - * @param quantityIndex The index of the quantity of interest to check. - * @param tolerance The tolerance for this quantity of interest across the box. - * @param maxGenerationDifference The maximum difference in refinement levels in terms of generation. + * @param rQuantityIndex The index of the quantity of interest to check. + * @param rTolerance The tolerance for this quantity of interest across the box. + * @param rMaxRefinementLevelDifference The maximum difference in refinement levels (see + * GetRefinementLevel(), a subdivision into 2^DIM boxes counts as DIM levels). * * @return The box that most exceeds the tolerance in the quantity of interest, if any. */ ParameterBox* FindBoxWithLargestQoIErrorEstimate(const unsigned& rQuantityIndex, const double& rTolerance, - const unsigned& rMaxGenerationDifference = UNSIGNED_UNSET); + const unsigned& rMaxRefinementLevelDifference = UNSIGNED_UNSET); /** * Calculate a regular grid interpolation by finding the box containing the point and @@ -442,6 +538,8 @@ class ParameterBox * @param rTolerance The tolerance that needs to be met * @param rQuantityIndex The QoI that we want a report on. * + * Boxes whose new corners are still being evaluated count as not meeting the tolerance. + * * @return the percentage of parameter space (by volume) where the tolerance on this QoI is met. */ double ReportPercentageOfSpaceWhereToleranceIsMetForQoI(const double& rTolerance, diff --git a/test/ContinuousTestPack.txt b/test/ContinuousTestPack.txt index 9192db3..b7b0371 100644 --- a/test/ContinuousTestPack.txt +++ b/test/ContinuousTestPack.txt @@ -9,6 +9,7 @@ TestDoseCalculator.hpp TestDoseResponseFitting.hpp TestLinearDiscriminantAnalysis.hpp TestLookupTableGenerator.hpp +TestLookupTableBackwardsCompatibility.hpp TestLogisticDistribution.hpp TestMakeALookupTable.hpp TestMetadataCellmlModels.hpp diff --git a/test/NightlyTestPack.txt b/test/NightlyTestPack.txt index 9aecd1c..c435220 100644 --- a/test/NightlyTestPack.txt +++ b/test/NightlyTestPack.txt @@ -1,4 +1,5 @@ TestApPredictLong.hpp +TestLookupTable2dHergIks.hpp TestPkpdInterpolations.hpp TestTorsadePredictLong.hpp TestTroublesomeApEvaluations.hpp diff --git a/test/TestConvertLookupTableArchiveToBinary.hpp b/test/TestConvertLookupTableArchiveToBinary.hpp index 6a10a65..5866608 100644 --- a/test/TestConvertLookupTableArchiveToBinary.hpp +++ b/test/TestConvertLookupTableArchiveToBinary.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -213,6 +213,67 @@ class TestConvertLookupTableArchiveToBinary : public CxxTest::TestSuite #endif // CHASTE_BOOST_IOSTREAMS } + void TestInterpolationOfPublishedTable() + { + // This published table was generated by an older version of the LookupTableGenerator + // (which subdivided boxes into 2^DIM rather than bisecting them), so this checks that + // the current code still reads and interpolates it sensibly. + if (!mpAsciiArchiveFile->IsFile()) + { + WARNING("Ascii archive is not present, can't test interpolation!"); + return; + } + + std::ifstream ifs((mpAsciiArchiveFile->GetAbsolutePath()).c_str(), std::ios::binary); + boost::archive::text_iarchive input_arch(ifs); + AbstractUntemplatedLookupTableGenerator* p_abstract_generator; + input_arch >> p_abstract_generator; + + TS_ASSERT_EQUALS(p_abstract_generator->GetDimension(), 4u); + LookupTableGenerator<4u>* p_generator = dynamic_cast*>(p_abstract_generator); + TS_ASSERT(p_generator); + + std::vector > points = p_generator->GetParameterPoints(); + std::vector > values = p_generator->GetFunctionValues(); + TS_ASSERT_EQUALS(points.size(), p_generator->GetNumEvaluations()); + TS_ASSERT_LESS_THAN(16u, points.size()); + + // The first 16 points evaluated are the corners of parameter space, where interpolation is exact. + std::vector > corners(points.begin(), points.begin() + 16u); + std::vector > interpolated = p_generator->Interpolate(corners); + for (unsigned i = 0; i < corners.size(); i++) + { + TS_ASSERT_DELTA(interpolated[i][0], values[i][0], 1e-12); + } + + // Multilinear interpolation shouldn't go outside the range of the data anywhere. + double min_value = DBL_MAX; + double max_value = -DBL_MAX; + for (unsigned i = 0; i < values.size(); i++) + { + min_value = std::min(min_value, values[i][0]); + max_value = std::max(max_value, values[i][0]); + } + std::vector > sample_points; + for (unsigned i = 0; i < 50u; i++) + { + std::vector point; + for (unsigned j = 0; j < 4u; j++) + { + point.push_back(fmod(0.137 * (i + 1) * (j + 1), 1.0)); + } + sample_points.push_back(point); + } + interpolated = p_abstract_generator->Interpolate(sample_points); + for (unsigned i = 0; i < interpolated.size(); i++) + { + TS_ASSERT_LESS_THAN_EQUALS(min_value - 1e-9, interpolated[i][0]); + TS_ASSERT_LESS_THAN_EQUALS(interpolated[i][0], max_value + 1e-9); + } + + delete p_abstract_generator; + } + void TestTimeLoadingFromEachArchive() { if (mpAsciiArchiveFile->IsFile()) diff --git a/test/TestLookupTable2dHergIks.hpp b/test/TestLookupTable2dHergIks.hpp new file mode 100644 index 0000000..af467c5 --- /dev/null +++ b/test/TestLookupTable2dHergIks.hpp @@ -0,0 +1,101 @@ +/* + +Copyright (c) 2005-2026, University of Oxford. +All rights reserved. + +University of Oxford means the Chancellor, Masters and Scholars of the +University of Oxford, having an administrative office at Wellington +Square, Oxford OX1 2JD, UK. + +This file is part of Chaste. + +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. + * Neither the name of the University of Oxford nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +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 COPYRIGHT HOLDER 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. + +*/ + +#ifndef TESTLOOKUPTABLE2DHERGIKS_HPP_ +#define TESTLOOKUPTABLE2DHERGIKS_HPP_ + +#include +#include + +#include "CheckpointArchiveTypes.hpp" + +#include "LookupTableGenerator.hpp" +#include "OutputFileHandler.hpp" + +/** + * Generates a 2D APD90 lookup table for hERG and IKs block (ten Tusscher 2006 epi at 1Hz) + * refined to a 1ms error tolerance, like the example plotted at the end of + * https://github.com/Chaste/trac_archive/blob/master/issues/2366.md + * + * It writes the evaluated points (2d_hERG_IKs_1Hz.dat) and the table interpolated onto a + * regular grid (2d_hERG_IKs_1Hz_interpolated.dat), so the interpolated surface and the + * distribution of evaluated points can both be plotted. + */ +class TestLookupTable2dHergIks : public CxxTest::TestSuite +{ +public: + void TestGenerate2dHergIksTable() + { + const std::string output_folder = "TestLookupTable2dHergIks"; + const std::string file_name = "2d_hERG_IKs_1Hz"; + OutputFileHandler handler(output_folder); // Wipe the folder for a fresh table each time. + + LookupTableGenerator<2> generator(2u, file_name, output_folder); // Ten Tusscher 2006 epi + generator.SetParameterToScale("membrane_rapid_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.SetParameterToScale("membrane_slow_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.AddQuantityOfInterest(Apd90, 1.0 /*ms*/); // QoI and tolerance + generator.SetMaxNumPaces(30u * 60u); // 30 minutes of 1Hz pacing, as in TestMakeALookupTable + generator.SetMaxVariationInRefinement(5u); + generator.SetMaxNumEvaluations(20000u); + + bool converged = generator.GenerateLookupTable(); + TS_ASSERT(converged); + std::cout << "Lookup table used " << generator.GetNumEvaluations() << " evaluations.\n"; + + // Interpolate the table onto a regular grid for plotting the surface. + const unsigned num_grid_points = 101u; + std::vector > grid_points; + for (unsigned i = 0; i < num_grid_points; i++) + { + for (unsigned j = 0; j < num_grid_points; j++) + { + grid_points.push_back(std::vector{ (double)(i) / (num_grid_points - 1u), + (double)(j) / (num_grid_points - 1u) }); + } + } + std::vector > interpolated = generator.Interpolate(grid_points); + + out_stream p_file = handler.OpenOutputFile(file_name + "_interpolated.dat"); + *p_file << std::setprecision(8); + *p_file << "hERG\tIKs\tAPD90\n"; + for (unsigned i = 0; i < grid_points.size(); i++) + { + *p_file << grid_points[i][0] << "\t" << grid_points[i][1] << "\t" << interpolated[i][0] << "\n"; + } + p_file->close(); + } +}; + +#endif // TESTLOOKUPTABLE2DHERGIKS_HPP_ diff --git a/test/TestLookupTableBackwardsCompatibility.hpp b/test/TestLookupTableBackwardsCompatibility.hpp new file mode 100644 index 0000000..eb00a74 --- /dev/null +++ b/test/TestLookupTableBackwardsCompatibility.hpp @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2005-2026, University of Oxford. +All rights reserved. + +University of Oxford means the Chancellor, Masters and Scholars of the +University of Oxford, having an administrative office at Wellington +Square, Oxford OX1 2JD, UK. + +This file is part of Chaste. + +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. + * Neither the name of the University of Oxford nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +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 COPYRIGHT HOLDER 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. + +*/ + +#ifndef TESTLOOKUPTABLEBACKWARDSCOMPATIBILITY_HPP_ +#define TESTLOOKUPTABLEBACKWARDSCOMPATIBILITY_HPP_ + +#include +#include "CheckpointArchiveTypes.hpp" + +#include +#include "FileFinder.hpp" +#include "ParameterBox.hpp" + +/** + * Check that lookup table data structures archived by older versions of ApPredict + * (which refined by subdividing boxes into 2^DIM) still load, interpolate identically, + * and can be refined further by the current code (which bisects boxes). + * + * The archive in test/data/legacy_lookup_tables was generated by + * test/data/legacy_lookup_tables/GenerateLegacyParameterBoxFixture.cpp with the older code. + * See also TestConvertLookupTableArchiveToBinary for a check on a published lookup table. + */ +class TestLookupTableBackwardsCompatibility : public CxxTest::TestSuite +{ +private: + /** The function that was used to generate the legacy archive */ + static double Separable2d(const c_vector& rX) + { + return exp(rX[0]) * (1.0 + rX[1] * rX[1]); + } + + void AssignDataAtPoint(ParameterBox<2>& rBox, c_vector* pPoint) + { + std::vector qoi(1u, Separable2d(*pPoint)); + rBox.AssignQoIValues(pPoint, boost::shared_ptr(new ParameterPointData(qoi, 0u))); + } + + double MaxInterpolationError(ParameterBox<2>& rBox) + { + double max_error = 0.0; + for (unsigned i = 0; i <= 20u; i++) + { + for (unsigned j = 0; j <= 20u; j++) + { + c_vector point; + point[0] = 0.05 * i; + point[1] = 0.05 * j; + max_error = std::max(max_error, fabs(rBox.InterpolateQoIsAt(point)[0] - Separable2d(point))); + } + } + return max_error; + } + +public: + void TestLoadAndRefineLegacyParameterBox() + { + FileFinder archive_file("projects/ApPredict/test/data/legacy_lookup_tables/ParameterBox2d_legacy.arch", + RelativeTo::ChasteSourceRoot); + FileFinder interpolated_file("projects/ApPredict/test/data/legacy_lookup_tables/ParameterBox2d_legacy_interpolated.dat", + RelativeTo::ChasteSourceRoot); + TS_ASSERT(archive_file.IsFile()); + TS_ASSERT(interpolated_file.IsFile()); + + ParameterBox<2>* p_box; + { + std::ifstream ifs(archive_file.GetAbsolutePath().c_str(), std::ios::binary); + boost::archive::text_iarchive input_arch(ifs); + input_arch >> p_box; + } + + // Check this is the structure we expect from the old algorithm + TS_ASSERT_EQUALS(p_box->GetCorners().size(), 153u); + TS_ASSERT_EQUALS(p_box->GetDaughterBoxes().size(), 4u); + + // Interpolation should be identical to the old code + std::ifstream interpolated_stream(interpolated_file.GetAbsolutePath().c_str()); + unsigned num_points_checked = 0u; + double x, y, value; + while (interpolated_stream >> x >> y >> value) + { + c_vector point; + point[0] = x; + point[1] = y; + TS_ASSERT_DELTA(p_box->InterpolateQoIsAt(point)[0], value, 1e-12); + num_points_checked++; + } + TS_ASSERT_EQUALS(num_points_checked, 441u); + + // The legacy table was refined to this tolerance, and the new code should agree that it is converged. + const double legacy_tolerance = 5e-2; + TS_ASSERT(p_box->FindBoxWithLargestQoIErrorEstimate(0u, legacy_tolerance) == NULL); + TS_ASSERT_DELTA(p_box->ReportPercentageOfSpaceWhereToleranceIsMetForQoI(legacy_tolerance, 0u), 100.0, 1e-9); + + // Carry on refining with bisection, as the LookupTableGenerator would now do. + const double tolerance = 5e-3; + unsigned num_bisections = 0u; + while (ParameterBox<2>* p_refine = p_box->FindBoxWithLargestQoIErrorEstimate(0u, tolerance)) + { + std::set*, c_vector_compare<2u> > new_points = p_refine->SubDivide(p_refine->ChooseDimensionToSplit(0u)); + TS_ASSERT_LESS_THAN_EQUALS(new_points.size(), 2u); + TS_ASSERT_EQUALS(p_refine->GetDaughterBoxes().size(), 2u); + for (auto iter = new_points.begin(); iter != new_points.end(); ++iter) + { + AssignDataAtPoint(*p_box, *iter); + } + num_bisections++; + TS_ASSERT_LESS_THAN(num_bisections, 10000u); + if (num_bisections >= 10000u) + { + break; + } + } + TS_ASSERT_LESS_THAN(0u, num_bisections); + TS_ASSERT_LESS_THAN(153u, p_box->GetCorners().size()); + TS_ASSERT_LESS_THAN(MaxInterpolationError(*p_box), tolerance); + + delete p_box; + } +}; + +#endif // TESTLOOKUPTABLEBACKWARDSCOMPATIBILITY_HPP_ diff --git a/test/TestLookupTableGenerator.hpp b/test/TestLookupTableGenerator.hpp index 71ac42a..912745c 100644 --- a/test/TestLookupTableGenerator.hpp +++ b/test/TestLookupTableGenerator.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -38,6 +38,12 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include +#include +#include +#include + #include "CheckpointArchiveTypes.hpp" #include "AbstractUntemplatedLookupTableGenerator.hpp" @@ -127,6 +133,240 @@ class TestLookupTableGenerator : public CxxTest::TestSuite TS_ASSERT_EQUALS(quantities_of_interest.size(), 10u); } + void TestLookupTableMaker2dBisection() + { + unsigned model_index = 2u; // Ten Tusscher 2006 epi + + std::string file_name = "2d_test"; + LookupTableGenerator<2> generator(model_index, file_name, "TestLookupTables"); + generator.SetParameterToScale("membrane_rapid_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.SetParameterToScale("membrane_L_type_calcium_current_conductance", 0.0, 1.0); + generator.AddQuantityOfInterest(Apd90, 0.5 /*ms*/); // QoI and tolerance + + generator.SetMaxNumEvaluations(1u); // Just does the corners + generator.GenerateLookupTable(); + TS_ASSERT_EQUALS(generator.GetNumEvaluations(), 4u); + + // Each refinement step bisects one box, adding at most 2^(DIM-1) = 2 new points. + for (unsigned i = 0; i < 6u; i++) + { + unsigned num_evals_before = generator.GetNumEvaluations(); + generator.SetMaxNumEvaluations(num_evals_before + 1u); + generator.GenerateLookupTable(); + TS_ASSERT_LESS_THAN_EQUALS(num_evals_before + 1u, generator.GetNumEvaluations()); + TS_ASSERT_LESS_THAN_EQUALS(generator.GetNumEvaluations(), num_evals_before + 2u); + } + + std::vector> parameter_values = generator.GetParameterPoints(); + std::vector> quantities_of_interest = generator.GetFunctionValues(); + TS_ASSERT_EQUALS(parameter_values.size(), generator.GetNumEvaluations()); + TS_ASSERT_EQUALS(quantities_of_interest.size(), generator.GetNumEvaluations()); + + // Interpolation at the corners of parameter space (the first points evaluated) is exact, + // and bilinear interpolation shouldn't go outside the range of the data anywhere. + std::vector> interpolated = generator.Interpolate(parameter_values); + double min_apd = DBL_MAX; + double max_apd = -DBL_MAX; + for (unsigned i = 0; i < quantities_of_interest.size(); i++) + { + min_apd = std::min(min_apd, quantities_of_interest[i][0]); + max_apd = std::max(max_apd, quantities_of_interest[i][0]); + if (i < 4u) + { + TS_ASSERT_DELTA(interpolated[i][0], quantities_of_interest[i][0], 1e-12); + } + } + std::vector> sample_points; + for (unsigned i = 0; i <= 10u; i++) + { + sample_points.push_back(std::vector{ 0.1 * i, 1.0 - 0.1 * i }); + sample_points.push_back(std::vector{ 0.1 * i, 0.33 }); + } + interpolated = generator.Interpolate(sample_points); + for (unsigned i = 0; i < interpolated.size(); i++) + { + TS_ASSERT_LESS_THAN_EQUALS(min_apd - 1e-9, interpolated[i][0]); + TS_ASSERT_LESS_THAN_EQUALS(interpolated[i][0], max_apd + 1e-9); + } + + // Check archiving and resuming work on a bisected table. + OutputFileHandler handler("TestLookupTableArchiving", false); + std::string archive_filename = handler.GetOutputDirectoryFullPath() + "Generator2d.arch"; + { + AbstractUntemplatedLookupTableGenerator* const p_generator = &generator; + std::ofstream ofs(archive_filename.c_str()); + boost::archive::text_oarchive output_arch(ofs); + output_arch << p_generator; + } + { + AbstractUntemplatedLookupTableGenerator* p_generator; + std::ifstream ifs(archive_filename.c_str(), std::ios::binary); + boost::archive::text_iarchive input_arch(ifs); + input_arch >> p_generator; + + TS_ASSERT_EQUALS(p_generator->GetDimension(), 2u); + TS_ASSERT_EQUALS(p_generator->GetNumEvaluations(), generator.GetNumEvaluations()); + std::vector> loaded_interpolated = p_generator->Interpolate(sample_points); + for (unsigned i = 0; i < interpolated.size(); i++) + { + TS_ASSERT_DELTA(loaded_interpolated[i][0], interpolated[i][0], 1e-12); + } + + unsigned num_evals_before = p_generator->GetNumEvaluations(); + p_generator->SetMaxNumEvaluations(num_evals_before + 1u); + p_generator->GenerateLookupTable(); + TS_ASSERT_LESS_THAN_EQUALS(num_evals_before + 1u, p_generator->GetNumEvaluations()); + TS_ASSERT_LESS_THAN_EQUALS(p_generator->GetNumEvaluations(), num_evals_before + 2u); + delete p_generator; + } + } + + /** + * Refine a 2D table of a cheap analytic function, which has a region where 'evaluation' reports + * an error code, and takes a pseudo-random time to evaluate so that evaluations finish + * out of order. + * + * The function value is continuous across the error region, so boxes on its edge can converge + * (a jump in value there would be refined down to the minimum box width). + * + * @param rGenerator the generator to set up (parameters must be set already) + * @param rNumCalls incremented on every call to the function (from any thread) + */ + void SetAnalyticEvaluationFunction(LookupTableGenerator<2>& rGenerator, std::atomic& rNumCalls) + { + rGenerator.mEvaluationFunctionForTesting = [&rNumCalls](const std::vector& rX, std::vector& rQoIs, unsigned& rErrorCode) + { + rNumCalls++; + // A delay of 0-9ms that varies between points. + unsigned delay_ms = (unsigned)(std::fabs(std::sin(1000.0 * rX[0] + 37.0 * rX[1])) * 10.0); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + + rQoIs.clear(); + rQoIs.push_back(300.0 + 100.0 / (0.2 + rX[0] + 0.5 * rX[1])); + rErrorCode = (rX[0] + rX[1] < 0.3) ? 2u : 0u; + }; + } + + void TestParallelRefinementWithAnalyticFunction() + { + std::atomic num_calls(0u); + LookupTableGenerator<2> generator(2u, "2d_analytic_parallel", "TestLookupTableParallel"); + generator.SetParameterToScale("membrane_rapid_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.SetParameterToScale("membrane_slow_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.AddQuantityOfInterest(Apd90, 2.0 /*ms*/); + generator.SetMaxVariationInRefinement(3u); + TS_ASSERT_THROWS_THIS(generator.SetNumThreads(0u), "The number of threads must be at least one."); + generator.SetNumThreads(8u); + TS_ASSERT_EQUALS(generator.GetNumThreads(), 8u); + SetAnalyticEvaluationFunction(generator, num_calls); + + // Stop part way through, the cap can only be exceeded by the points on one new plane (1 in 2D). + generator.SetMaxNumEvaluations(100u); + TS_ASSERT_EQUALS(generator.GenerateLookupTable(), false); + TS_ASSERT_LESS_THAN_EQUALS(100u, generator.GetNumEvaluations()); + TS_ASSERT_LESS_THAN_EQUALS(generator.GetNumEvaluations(), 101u); + CheckGeneratorIsConsistent(generator, num_calls); + + // Archive it, and carry on to convergence with a different number of threads. + OutputFileHandler handler("TestLookupTableParallel", false); + std::string archive_filename = handler.GetOutputDirectoryFullPath() + "Generator2dParallel.arch"; + { + AbstractUntemplatedLookupTableGenerator* const p_generator = &generator; + std::ofstream ofs(archive_filename.c_str()); + boost::archive::text_oarchive output_arch(ofs); + output_arch << p_generator; + } + AbstractUntemplatedLookupTableGenerator* p_abstract_generator; + { + std::ifstream ifs(archive_filename.c_str(), std::ios::binary); + boost::archive::text_iarchive input_arch(ifs); + input_arch >> p_abstract_generator; + } + LookupTableGenerator<2>* p_loaded = dynamic_cast*>(p_abstract_generator); + TS_ASSERT_EQUALS(p_loaded->GetNumEvaluations(), generator.GetNumEvaluations()); + p_loaded->SetNumThreads(3u); + SetAnalyticEvaluationFunction(*p_loaded, num_calls); + p_loaded->SetMaxNumEvaluations(100000u); + TS_ASSERT_EQUALS(p_loaded->GenerateLookupTable(), true); + std::cout << "Analytic 2D table converged with " << p_loaded->GetNumEvaluations() << " evaluations.\n"; + TS_ASSERT_LESS_THAN(101u, p_loaded->GetNumEvaluations()); + CheckGeneratorIsConsistent(*p_loaded, num_calls); + + // All the boxes meet the tolerance. + std::vector*> leaves; + p_loaded->CollectLeafBoxes(p_loaded->mpParentBox, leaves); + for (unsigned i = 0; i < leaves.size(); i++) + { + TS_ASSERT_EQUALS(leaves[i]->DoesBoxNeedFurtherRefinement(2.0, 0u), false); + } + + // And the interpolation is good (looking away from the steepest part of the function). + for (unsigned i = 0; i <= 20u; i++) + { + for (unsigned j = 0; j <= 20u; j++) + { + std::vector x{ 0.05 * i, 0.05 * j }; + if (x[0] + x[1] < 0.45) + { + continue; + } + double interpolated = p_loaded->Interpolate(std::vector >{ x })[0][0]; + TS_ASSERT_DELTA(interpolated, 300.0 + 100.0 / (0.2 + x[0] + 0.5 * x[1]), 4.0); + } + } + delete p_loaded; + } + + void TestSingleThreadedRefinementIsDeterministic() + { + std::vector > > runs; + for (unsigned run = 0; run < 2u; run++) + { + std::atomic num_calls(0u); + LookupTableGenerator<2> generator(2u, "2d_analytic_serial", "TestLookupTableParallel"); + generator.SetParameterToScale("membrane_rapid_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.SetParameterToScale("membrane_slow_delayed_rectifier_potassium_current_conductance", 0.0, 1.0); + generator.AddQuantityOfInterest(Apd90, 2.0 /*ms*/); + generator.SetNumThreads(1u); + SetAnalyticEvaluationFunction(generator, num_calls); + generator.SetMaxNumEvaluations(150u); + generator.GenerateLookupTable(); + CheckGeneratorIsConsistent(generator, num_calls); + runs.push_back(generator.GetParameterPoints()); + } + TS_ASSERT_EQUALS(runs[0].size(), runs[1].size()); + for (unsigned i = 0; i < std::min(runs[0].size(), runs[1].size()); i++) + { + TS_ASSERT_DELTA(runs[0][i][0], runs[1][i][0], 1e-12); + TS_ASSERT_DELTA(runs[0][i][1], runs[1][i][1], 1e-12); + } + } + + /** + * Check that every point was evaluated exactly once, and every box has all its corners evaluated. + */ + void CheckGeneratorIsConsistent(LookupTableGenerator<2>& rGenerator, std::atomic& rNumCalls) + { + std::vector > points = rGenerator.GetParameterPoints(); + TS_ASSERT_EQUALS(points.size(), rGenerator.GetNumEvaluations()); + TS_ASSERT_EQUALS(rNumCalls.load(), rGenerator.GetNumEvaluations()); + std::set*, c_vector_compare<2u> > unique_points; + for (unsigned i = 0; i < points.size(); i++) + { + unique_points.insert(&points[i]); + } + TS_ASSERT_EQUALS(unique_points.size(), points.size()); + + std::vector*> leaves; + rGenerator.CollectLeafBoxes(rGenerator.mpParentBox, leaves); + for (unsigned i = 0; i < leaves.size(); i++) + { + TS_ASSERT(leaves[i]->mAllCornersEvaluated); + TS_ASSERT_EQUALS(leaves[i]->mParameterPointDataMapPredictions.size(), 0u); + } + TS_ASSERT_EQUALS(rGenerator.mpParentBox->GetCorners().size(), rGenerator.GetNumEvaluations()); + } + void TestLookupTableMaker5d() { unsigned model_index = 2u; // Ten tusscher '06 (table generated for 1 Hz at present) diff --git a/test/TestMakeALookupTable.hpp b/test/TestMakeALookupTable.hpp index 069fa66..d72e7de 100644 --- a/test/TestMakeALookupTable.hpp +++ b/test/TestMakeALookupTable.hpp @@ -78,6 +78,8 @@ class TestMakeALookupTable : public CxxTest::TestSuite " * --hertz (the pacing frequency in Hertz - defaults to 1Hz)\n" " then a list of ion channels that you would like to block:\n" " * --channels (choice of: hERG, ICaL, INa, IKs, Ito, INaL, IK1)\n" + " and optionally:\n" + " * --threads (the number of simulations to run at once - defaults to the number of cores)\n" << std::flush; return; } @@ -230,6 +232,12 @@ class TestMakeALookupTable : public CxxTest::TestSuite p_generator->SetMaxVariationInRefinement(5u); // This prevents over-refining in one area. } + if (CommandLineArguments::Instance()->OptionExists("--threads")) + { + p_generator->SetNumThreads(CommandLineArguments::Instance()->GetUnsignedCorrespondingToOption("--threads")); + } + std::cout << "Running up to " << p_generator->GetNumThreads() << " simulations at once.\n"; + const unsigned start_evaluations = p_generator->GetNumEvaluations(); std::cout << "Started with " << start_evaluations << " evaluations.\n"; const unsigned max_num_evaluations = 2000000; diff --git a/test/TestParameterBox.hpp b/test/TestParameterBox.hpp index caa0418..10c7384 100644 --- a/test/TestParameterBox.hpp +++ b/test/TestParameterBox.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2005-2025, University of Oxford. +Copyright (c) 2005-2026, University of Oxford. All rights reserved. University of Oxford means the Chancellor, Masters and Scholars of the @@ -42,6 +42,13 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "OutputFileHandler.hpp" #include "ParameterBox.hpp" +/** Helper to define a pointer to a test function without it being used for template argument deduction. */ +template +struct TestFunction +{ + typedef double (*Type)(const c_vector&); +}; + /** * Test the Parameter Box class. * @@ -49,6 +56,132 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. class TestParameterBox : public CxxTest::TestSuite { private: + /* Some cheap test functions to emulate QoIs with */ + static double Exp1d(const c_vector& rX) + { + return exp(rX[0]); + } + + static double ExpX2d(const c_vector& rX) + { + return exp(rX[0]); + } + + static double ExpTwoX2d(const c_vector& rX) + { + return exp(2.0 * rX[0]); + } + + static double Separable2d(const c_vector& rX) + { + return exp(rX[0]) * (1.0 + rX[1] * rX[1]); + } + + static double Bilinear2d(const c_vector& rX) + { + return 1.0 + 2.0 * rX[0] - 3.0 * rX[1] + 4.0 * rX[0] * rX[1]; + } + + static double Trilinear3d(const c_vector& rX) + { + return rX[0] + 2.0 * rX[1] + 3.0 * rX[2] + rX[0] * rX[1] * rX[2]; + } + + c_vector MakePoint(double x, double y) + { + c_vector point; + point[0] = x; + point[1] = y; + return point; + } + + // Assign data from a test function at a single point + template + void AssignFunctionDataAtPoint(ParameterBox& rBox, + c_vector* pPoint, + typename TestFunction::Type pFunction) + { + std::vector qoi(1u, pFunction(*pPoint)); + boost::shared_ptr p_data = boost::shared_ptr(new ParameterPointData(qoi, 0u)); + rBox.AssignQoIValues(pPoint, p_data); + } + + // Assign data from a test function at all the corners in a box + template + void AssignFunctionData(ParameterBox& rBox, + typename TestFunction::Type pFunction) + { + std::vector*> corners = rBox.GetCornersAsVector(); + for (unsigned i = 0; i < corners.size(); i++) + { + AssignFunctionDataAtPoint(rBox, corners[i], pFunction); + } + } + + // Mimic what the LookupTableGenerator does, but with a cheap function, returns number of points. + template + unsigned RefineToTolerance(ParameterBox& rBox, + typename TestFunction::Type pFunction, + double tolerance, + bool bisect) + { + AssignFunctionData(rBox, pFunction); + for (unsigned iteration = 0; iteration < 10000u; iteration++) + { + ParameterBox* p_box = rBox.FindBoxWithLargestQoIErrorEstimate(0u, tolerance); + if (!p_box) + { + break; + } + std::set*, c_vector_compare > new_points; + if (bisect) + { + new_points = p_box->SubDivide(p_box->ChooseDimensionToSplit(0u)); + } + else + { + new_points = p_box->SubDivide(); + } + for (auto iter = new_points.begin(); iter != new_points.end(); ++iter) + { + AssignFunctionDataAtPoint(rBox, *iter, pFunction); + } + } + return rBox.GetCornersAsVector().size(); + } + + template + std::vector*> GetLeafBoxes(ParameterBox& rBox) + { + std::vector*> all_boxes = rBox.GetWholeFamilyOfBoxes(); + std::vector*> leaves; + for (unsigned i = 0; i < all_boxes.size(); i++) + { + if (!all_boxes[i]->IsParent()) + { + leaves.push_back(all_boxes[i]); + } + } + return leaves; + } + + // Largest interpolation error on a regular grid over [0,1]^2 + double MaxInterpolationError2d(ParameterBox<2>& rBox, + double (*pFunction)(const c_vector&)) + { + double max_error = 0.0; + for (unsigned i = 0; i <= 20u; i++) + { + for (unsigned j = 0; j <= 20u; j++) + { + c_vector point = MakePoint(0.05 * i, 0.05 * j); + double error = fabs(rBox.InterpolateQoIsAt(point)[0] - pFunction(point)); + max_error = std::max(max_error, error); + } + } + return max_error; + } + // 1D box void AssignExponentialData(ParameterBox<1>& rBox, std::vector*>& rCorners) @@ -419,6 +552,358 @@ class TestParameterBox : public CxxTest::TestSuite TS_ASSERT_DELTA((*(daughter_boxes[3]->GetCornersAsVector()[3]))[0], 1, 1e-12); TS_ASSERT_DELTA((*(daughter_boxes[3]->GetCornersAsVector()[3]))[1], 1, 1e-12); } + + void TestBisection1dMatchesFullSubdivision() + { + // In 1D a bisection is the same as a subdivision into 2^DIM boxes, so should give identical results. + ParameterBox<1> legacy_box(NULL); + ParameterBox<1> bisected_box(NULL); + + unsigned num_legacy_points = RefineToTolerance(legacy_box, Exp1d, 1e-4, false); + unsigned num_bisected_points = RefineToTolerance(bisected_box, Exp1d, 1e-4, true); + + TS_ASSERT_EQUALS(num_legacy_points, num_bisected_points); + TS_ASSERT_LESS_THAN(10u, num_bisected_points); // Check it did something + + std::vector*> legacy_points = legacy_box.GetCornersAsVector(); + std::vector*> bisected_points = bisected_box.GetCornersAsVector(); + for (unsigned i = 0; i < std::min(legacy_points.size(), bisected_points.size()); i++) + { + TS_ASSERT_DELTA((*legacy_points[i])[0], (*bisected_points[i])[0], 1e-15); + } + + for (unsigned i = 0; i <= 100u; i++) + { + c_vector point; + point[0] = 0.01 * i; + TS_ASSERT_DELTA(legacy_box.InterpolateQoIsAt(point)[0], bisected_box.InterpolateQoIsAt(point)[0], 1e-15); + } + } + + void TestBisection2dGeometryAndErrorEstimates() + { + ParameterBox<2> parent_box(NULL); + AssignFunctionData(parent_box, ExpX2d); + TS_ASSERT_EQUALS(parent_box.GetRefinementLevel(), 0u); + + // All dimensions have unknown errors, so tie-break on variation, which is only along x. + TS_ASSERT_EQUALS(parent_box.ChooseDimensionToSplit(0u), 0u); + + TS_ASSERT_THROWS_THIS(parent_box.SubDivide(2u), + "Cannot subdivide along dimension 2 of a 2D box."); + + std::set*, c_vector_compare<2u> > new_points = parent_box.SubDivide(0u); + TS_ASSERT_EQUALS(parent_box.IsParent(), true); + TS_ASSERT_EQUALS(new_points.size(), 2u); // In 2D a bisection requires the addition of 2 new points. + std::vector*> new_points_vec(new_points.begin(), new_points.end()); + TS_ASSERT_DELTA((*new_points_vec[0])[0], 0.5, 1e-12); + TS_ASSERT_DELTA((*new_points_vec[0])[1], 0.0, 1e-12); + TS_ASSERT_DELTA((*new_points_vec[1])[0], 0.5, 1e-12); + TS_ASSERT_DELTA((*new_points_vec[1])[1], 1.0, 1e-12); + + TS_ASSERT_THROWS_THIS(parent_box.SubDivide(1u), "Already subdivided this box."); + TS_ASSERT_THROWS_THIS(parent_box.ChooseDimensionToSplit(0u), "This box has already been subdivided."); + TS_ASSERT_EQUALS(parent_box.GetCornersAsVector().size(), 6u); + + std::vector*> daughter_boxes = parent_box.GetDaughterBoxes(); + TS_ASSERT_EQUALS(daughter_boxes.size(), 2u); + TS_ASSERT_EQUALS(daughter_boxes[0]->GetNewCorners().size(), 2u); + TS_ASSERT_EQUALS(daughter_boxes[1]->GetNewCorners().size(), 0u); + TS_ASSERT_DELTA(daughter_boxes[0]->mMin[0], 0.0, 1e-12); + TS_ASSERT_DELTA(daughter_boxes[0]->mMax[0], 0.5, 1e-12); + TS_ASSERT_DELTA(daughter_boxes[0]->mMax[1], 1.0, 1e-12); + TS_ASSERT_DELTA(daughter_boxes[1]->mMin[0], 0.5, 1e-12); + TS_ASSERT_DELTA(daughter_boxes[1]->mMin[1], 0.0, 1e-12); + TS_ASSERT_DELTA(daughter_boxes[1]->mMax[0], 1.0, 1e-12); + for (unsigned i = 0; i < 2u; i++) + { + TS_ASSERT_EQUALS(daughter_boxes[i]->GetRefinementLevel(), 1u); + TS_ASSERT_EQUALS(daughter_boxes[i]->GetGeneration(), 1u); + TS_ASSERT_EQUALS(daughter_boxes[i]->GetOwnCorners().size(), 4u); + TS_ASSERT_THROWS_THIS(daughter_boxes[i]->GetMaxErrorsInPredictedQoIs(), + "Not all the parameter points (which you can get with GetNewCorners()) have been assigned data. Error estimates unavailable."); + } + + AssignFunctionData(parent_box, ExpX2d); + + // Linear interpolation between exp(0) and exp(1), compared to exp(0.5). + const double expected_error = (exp(0.0) + exp(1.0)) / 2.0 - exp(0.5); + for (unsigned i = 0; i < 2u; i++) + { + TS_ASSERT_DELTA(daughter_boxes[i]->GetMaxErrorsInPredictedQoIs()[0], expected_error, 1e-12); + + // We only know about errors in x, the error in y is unknown. + std::vector errors = daughter_boxes[i]->GetErrorEstimatesPerDimension(0u); + TS_ASSERT_DELTA(errors[0], expected_error, 1e-12); + TS_ASSERT_EQUALS(errors[1], DBL_MAX); + TS_ASSERT_EQUALS(daughter_boxes[i]->GetMaxErrorInQoIEstimateInThisBox(0u), DBL_MAX); + TS_ASSERT_EQUALS(daughter_boxes[i]->ChooseDimensionToSplit(0u), 1u); + } + + // Split along y + new_points = daughter_boxes[0]->SubDivide(1u); + TS_ASSERT_EQUALS(new_points.size(), 2u); // (0,0.5) and (0.5,0.5) + AssignFunctionData(parent_box, ExpX2d); + + ParameterBox<2>* p_grand_daughter = daughter_boxes[0]->GetDaughterBoxes()[0]; + TS_ASSERT_EQUALS(p_grand_daughter->GetRefinementLevel(), 2u); + TS_ASSERT_DELTA(p_grand_daughter->mMax[0], 0.5, 1e-12); + TS_ASSERT_DELTA(p_grand_daughter->mMax[1], 0.5, 1e-12); + + // exp(x) is constant in y so there is no error in the y direction, + // and the x error estimate is inherited from the parent. + TS_ASSERT_DELTA(p_grand_daughter->GetMaxErrorsInPredictedQoIs()[0], 0.0, 1e-12); + std::vector errors = p_grand_daughter->GetErrorEstimatesPerDimension(0u); + TS_ASSERT_DELTA(errors[0], expected_error, 1e-12); + TS_ASSERT_DELTA(errors[1], 0.0, 1e-12); + TS_ASSERT_DELTA(p_grand_daughter->GetMaxErrorInQoIEstimateInThisBox(0u), expected_error, 1e-12); + TS_ASSERT_EQUALS(p_grand_daughter->ChooseDimensionToSplit(0u), 0u); + + // For comparison, a subdivision into 2^DIM boxes counts as DIM refinement levels, + // and gives error estimates in every dimension. + ParameterBox<2> legacy_box(NULL); + AssignFunctionData(legacy_box, ExpX2d); + legacy_box.SubDivide(); + AssignFunctionData(legacy_box, ExpX2d); + ParameterBox<2>* p_legacy_daughter = legacy_box.GetDaughterBoxes()[0]; + TS_ASSERT_EQUALS(p_legacy_daughter->GetRefinementLevel(), 2u); + errors = p_legacy_daughter->GetErrorEstimatesPerDimension(0u); + TS_ASSERT_DELTA(errors[0], p_legacy_daughter->GetMaxErrorsInPredictedQoIs()[0], 1e-12); + TS_ASSERT_DELTA(errors[1], p_legacy_daughter->GetMaxErrorsInPredictedQoIs()[0], 1e-12); + } + + void TestBisectionWhenAllNewPointsAlreadyExist() + { + /* + * Build up a situation where neighbouring boxes have already evaluated all the points + * on the plane that a bisection needs, so no new points are required. + */ + ParameterBox<2> parent_box(NULL); + AssignFunctionData(parent_box, Separable2d); + + parent_box.SubDivide(0u); // [0,0.5]x[0,1] and [0.5,1]x[0,1] + AssignFunctionData(parent_box, Separable2d); + ParameterBox<2>* p_left = parent_box.GetDaughterBoxes()[0]; + + p_left->SubDivide(1u); // [0,0.5]x[0,0.5] and [0,0.5]x[0.5,1] + AssignFunctionData(parent_box, Separable2d); + ParameterBox<2>* p_left_bottom = p_left->GetDaughterBoxes()[0]; + ParameterBox<2>* p_left_top = p_left->GetDaughterBoxes()[1]; + + p_left_bottom->SubDivide(1u); // [0,0.5]x[0,0.25] and [0,0.5]x[0.25,0.5] + AssignFunctionData(parent_box, Separable2d); + ParameterBox<2>* p_lbb = p_left_bottom->GetDaughterBoxes()[0]; + ParameterBox<2>* p_lbt = p_left_bottom->GetDaughterBoxes()[1]; + + TS_ASSERT_EQUALS(p_lbb->SubDivide(0u).size(), 2u); // Creates (0.25,0) and (0.25,0.25) + AssignFunctionData(parent_box, Separable2d); + TS_ASSERT_EQUALS(p_left_top->SubDivide(0u).size(), 2u); // Creates (0.25,0.5) and (0.25,1) + AssignFunctionData(parent_box, Separable2d); + + // Now both (0.25,0.25) and (0.25,0.5) have been evaluated already. + std::set*, c_vector_compare<2u> > new_points = p_lbt->SubDivide(0u); + TS_ASSERT_EQUALS(new_points.size(), 0u); + + // But we should still have error estimates, from interpolating across p_lbt. + double error_low = fabs(0.5 * (Separable2d(MakePoint(0.0, 0.25)) + Separable2d(MakePoint(0.5, 0.25))) + - Separable2d(MakePoint(0.25, 0.25))); + double error_high = fabs(0.5 * (Separable2d(MakePoint(0.0, 0.5)) + Separable2d(MakePoint(0.5, 0.5))) + - Separable2d(MakePoint(0.25, 0.5))); + std::vector*> daughters = p_lbt->GetDaughterBoxes(); + TS_ASSERT_EQUALS(daughters.size(), 2u); + for (unsigned i = 0; i < daughters.size(); i++) + { + TS_ASSERT_EQUALS(daughters[i]->mAllCornersEvaluated, true); + TS_ASSERT_EQUALS(daughters[i]->GetMaxErrorsInPredictedQoIs().size(), 1u); + TS_ASSERT_DELTA(daughters[i]->GetMaxErrorsInPredictedQoIs()[0], std::max(error_low, error_high), 1e-12); + TS_ASSERT_DELTA(daughters[i]->GetErrorEstimatesPerDimension(0u)[0], std::max(error_low, error_high), 1e-12); + } + + // And the rest of the tree should still work as usual. + TS_ASSERT(parent_box.FindBoxWithLargestQoIErrorEstimate(0u, 1e-6) != NULL); + double percentage = parent_box.ReportPercentageOfSpaceWhereToleranceIsMetForQoI(1.0, 0u); + TS_ASSERT_LESS_THAN_EQUALS(0.0, percentage); + TS_ASSERT_LESS_THAN_EQUALS(percentage, 100.0); + + // Interpolation in the refined region [0,0.5)x[0.25,0.5] should be reasonable. + // (Points on x=0.5 are interpolated in the unrefined box to the right.) + for (unsigned i = 0; i < 10u; i++) + { + for (unsigned j = 0; j <= 10u; j++) + { + c_vector point = MakePoint(0.05 * i, 0.25 + 0.025 * j); + TS_ASSERT_DELTA(parent_box.InterpolateQoIsAt(point)[0], Separable2d(point), 5e-2); + } + } + } + + void TestBisection3dVisitsEveryDimensionOnce() + { + // A multilinear function has no interpolation error, but we need to split + // each part of the tree along each dimension once to find that out. + // So we should end up with the same 3x3x3 grid as one subdivision into 2^DIM. + ParameterBox<3> parent_box(NULL); + TS_ASSERT_EQUALS(RefineToTolerance(parent_box, Trilinear3d, 1e-6, true), 27u); + + std::vector*> leaves = GetLeafBoxes(parent_box); + TS_ASSERT_EQUALS(leaves.size(), 8u); + for (unsigned i = 0; i < leaves.size(); i++) + { + TS_ASSERT_EQUALS(leaves[i]->GetRefinementLevel(), 3u); + for (unsigned j = 0; j < 3u; j++) + { + TS_ASSERT_DELTA(leaves[i]->mMax[j] - leaves[i]->mMin[j], 0.5, 1e-12); + } + } + + for (unsigned i = 0; i <= 10u; i++) + { + c_vector point; + point[0] = 0.1 * i; + point[1] = 0.07 * i; + point[2] = 1.0 - 0.1 * i; + TS_ASSERT_DELTA(parent_box.InterpolateQoIsAt(point)[0], Trilinear3d(point), 1e-12); + } + } + + void TestBisectionOnlyRefinesDimensionsThatNeedIt() + { + const double tolerance = 1e-2; + + // exp(2x) only varies in x + ParameterBox<2> bisected_box(NULL); + ParameterBox<2> legacy_box(NULL); + unsigned num_bisected_points = RefineToTolerance(bisected_box, ExpTwoX2d, tolerance, true); + unsigned num_legacy_points = RefineToTolerance(legacy_box, ExpTwoX2d, tolerance, false); + std::cout << "exp(2x) to tolerance " << tolerance << ": bisection used " << num_bisected_points + << " points, subdivision into 2^DIM used " << num_legacy_points << " points.\n"; + TS_ASSERT_LESS_THAN(num_bisected_points, num_legacy_points); + + // We only needed to split once in y, all refinement after that is in x. + std::vector*> leaves = GetLeafBoxes(bisected_box); + for (unsigned i = 0; i < leaves.size(); i++) + { + TS_ASSERT_DELTA(leaves[i]->mMax[1] - leaves[i]->mMin[1], 0.5, 1e-12); + } + + TS_ASSERT_LESS_THAN(MaxInterpolationError2d(bisected_box, ExpTwoX2d), tolerance); + TS_ASSERT_LESS_THAN(MaxInterpolationError2d(legacy_box, ExpTwoX2d), tolerance); + TS_ASSERT_DELTA(bisected_box.ReportPercentageOfSpaceWhereToleranceIsMetForQoI(tolerance, 0u), 100.0, 1e-9); + + // A function that is curved in both dimensions + ParameterBox<2> bisected_box2(NULL); + ParameterBox<2> legacy_box2(NULL); + num_bisected_points = RefineToTolerance(bisected_box2, Separable2d, tolerance, true); + num_legacy_points = RefineToTolerance(legacy_box2, Separable2d, tolerance, false); + std::cout << "exp(x)(1+y^2) to tolerance " << tolerance << ": bisection used " << num_bisected_points + << " points, subdivision into 2^DIM used " << num_legacy_points << " points.\n"; + TS_ASSERT_LESS_THAN(num_bisected_points, num_legacy_points); + TS_ASSERT_LESS_THAN(MaxInterpolationError2d(bisected_box2, Separable2d), tolerance); + } + + void TestInterpolationOnMixedTree() + { + // A bilinear function should be interpolated exactly on any tree of rectangles, + // here we mix up subdivisions into 2^DIM with bisections. + ParameterBox<2> parent_box(NULL); + AssignFunctionData(parent_box, Bilinear2d); + parent_box.SubDivide(); + AssignFunctionData(parent_box, Bilinear2d); + std::vector*> daughters = parent_box.GetDaughterBoxes(); + TS_ASSERT_EQUALS(daughters.size(), 4u); + + daughters[0]->SubDivide(0u); + AssignFunctionData(parent_box, Bilinear2d); + daughters[3]->SubDivide(1u); + AssignFunctionData(parent_box, Bilinear2d); + daughters[3]->GetDaughterBoxes()[1]->SubDivide(0u); + AssignFunctionData(parent_box, Bilinear2d); + daughters[1]->SubDivide(); + AssignFunctionData(parent_box, Bilinear2d); + + TS_ASSERT_DELTA(MaxInterpolationError2d(parent_box, Bilinear2d), 0.0, 1e-12); + + // Every leaf has had every dimension split somewhere in its family tree, + // and there is no error, so nothing needs refining. + std::vector*> leaves = GetLeafBoxes(parent_box); + TS_ASSERT_EQUALS(leaves.size(), 10u); + for (unsigned i = 0; i < leaves.size(); i++) + { + std::vector errors = leaves[i]->GetErrorEstimatesPerDimension(0u); + TS_ASSERT_DELTA(errors[0], 0.0, 1e-12); + TS_ASSERT_DELTA(errors[1], 0.0, 1e-12); + } + TS_ASSERT(parent_box.FindBoxWithLargestQoIErrorEstimate(0u, 1e-10) == NULL); + TS_ASSERT_DELTA(parent_box.ReportPercentageOfSpaceWhereToleranceIsMetForQoI(1e-10, 0u), 100.0, 1e-9); + } + + void TestBisectingATreeMadeBySubdivision() + { + // This is what happens when an old lookup table is refined further. + ParameterBox<2> parent_box(NULL); + unsigned num_legacy_points = RefineToTolerance(parent_box, Separable2d, 5e-2, false); + TS_ASSERT_LESS_THAN(MaxInterpolationError2d(parent_box, Separable2d), 5e-2); + + unsigned num_points = RefineToTolerance(parent_box, Separable2d, 5e-3, true); + TS_ASSERT_LESS_THAN(num_legacy_points, num_points); + TS_ASSERT_LESS_THAN(MaxInterpolationError2d(parent_box, Separable2d), 5e-3); + TS_ASSERT_DELTA(parent_box.ReportPercentageOfSpaceWhereToleranceIsMetForQoI(5e-3, 0u), 100.0, 1e-9); + } + + void TestArchivingBisectedParameterBox() + { + OutputFileHandler handler("archive", false); + std::string archive_filename = handler.GetOutputDirectoryFullPath() + "BisectedParameterBox.arch"; + + const double tolerance = 1e-2; + unsigned num_points; + std::vector interpolated_values; + c_vector next_box_min; + unsigned next_dimension; + + // SAVE + { + ParameterBox<2>* p_box = new ParameterBox<2>(NULL); + num_points = RefineToTolerance(*p_box, Separable2d, tolerance, true); + for (unsigned i = 0; i <= 20u; i++) + { + interpolated_values.push_back(p_box->InterpolateQoIsAt(MakePoint(0.05 * i, 1.0 - 0.04 * i))[0]); + } + ParameterBox<2>* p_next_box = p_box->FindBoxWithLargestQoIErrorEstimate(0u, 0.5 * tolerance); + TS_ASSERT(p_next_box); + next_box_min = p_next_box->mMin; + next_dimension = p_next_box->ChooseDimensionToSplit(0u); + + std::ofstream ofs(archive_filename.c_str()); + boost::archive::text_oarchive output_arch(ofs); + output_arch << p_box; + delete p_box; + } + + // LOAD + { + ParameterBox<2>* p_box; + std::ifstream ifs(archive_filename.c_str(), std::ios::binary); + boost::archive::text_iarchive input_arch(ifs); + input_arch >> p_box; + + TS_ASSERT_EQUALS(p_box->GetCornersAsVector().size(), num_points); + for (unsigned i = 0; i <= 20u; i++) + { + TS_ASSERT_DELTA(p_box->InterpolateQoIsAt(MakePoint(0.05 * i, 1.0 - 0.04 * i))[0], interpolated_values[i], 1e-12); + } + ParameterBox<2>* p_next_box = p_box->FindBoxWithLargestQoIErrorEstimate(0u, 0.5 * tolerance); + TS_ASSERT(p_next_box); + TS_ASSERT_DELTA(p_next_box->mMin[0], next_box_min[0], 1e-12); + TS_ASSERT_DELTA(p_next_box->mMin[1], next_box_min[1], 1e-12); + TS_ASSERT_EQUALS(p_next_box->ChooseDimensionToSplit(0u), next_dimension); + + // And we can carry on refining + TS_ASSERT_LESS_THAN(num_points, RefineToTolerance(*p_box, Separable2d, 0.5 * tolerance, true)); + + delete p_box; + } + } }; #endif // TESTPARAMETERBOX_HPP_ diff --git a/test/data/legacy_lookup_tables/GenerateLegacyParameterBoxFixture.cpp b/test/data/legacy_lookup_tables/GenerateLegacyParameterBoxFixture.cpp new file mode 100644 index 0000000..a6c78ca --- /dev/null +++ b/test/data/legacy_lookup_tables/GenerateLegacyParameterBoxFixture.cpp @@ -0,0 +1,107 @@ +/* + +Copyright (c) 2005-2026, University of Oxford. +All rights reserved. + +University of Oxford means the Chancellor, Masters and Scholars of the +University of Oxford, having an administrative office at Wellington +Square, Oxford OX1 2JD, UK. + +This file is part of Chaste. + +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. + * Neither the name of the University of Oxford nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +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 COPYRIGHT HOLDER 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. + +*/ + +/* + * This program generated the files in this folder, it is kept here for reference (it is not built + * by the Chaste build system). + * + * It was built against the ParameterBox.hpp/.cpp from ApPredict before bisection refinement was + * introduced (git commit 0813da9), with Boost 1.74 (the oldest Boost used by ApPredict's CI and docker + * images - text archives made with newer Boost versions can't be read by older ones). + * + * It creates a ParameterBox<2> refined by subdivision into 2^DIM boxes, as the LookupTableGenerator + * used to do, archives it, and records interpolated values so that + * TestLookupTableBackwardsCompatibility can check that newer code still loads and interpolates + * these archives in exactly the same way. + */ +#include +#include +#include "CheckpointArchiveTypes.hpp" +#include "Exception.hpp" +#include "ParameterBox.hpp" + +double Separable2d(const c_vector& rX) +{ + return exp(rX[0]) * (1.0 + rX[1] * rX[1]); +} + +void AssignDataAtPoint(ParameterBox<2>& rBox, c_vector* pPoint) +{ + std::vector qoi(1u, Separable2d(*pPoint)); + rBox.AssignQoIValues(pPoint, boost::shared_ptr(new ParameterPointData(qoi, 0u))); +} + +int main() +{ + ParameterBox<2>* p_box = new ParameterBox<2>(NULL); + std::set*, c_vector_compare<2u> > corners = p_box->GetCorners(); + for (auto iter = corners.begin(); iter != corners.end(); ++iter) + { + AssignDataAtPoint(*p_box, *iter); + } + + // Refine as the old LookupTableGenerator did. + const double tolerance = 5e-2; + while (ParameterBox<2>* p_refine = p_box->FindBoxWithLargestQoIErrorEstimate(0u, tolerance)) + { + std::set*, c_vector_compare<2u> > new_points = p_refine->SubDivide(); + for (auto iter = new_points.begin(); iter != new_points.end(); ++iter) + { + AssignDataAtPoint(*p_box, *iter); + } + } + std::cout << "Created ParameterBox<2> with " << p_box->GetCorners().size() << " points.\n"; + + { + std::ofstream ofs("ParameterBox2d_legacy.arch"); + boost::archive::text_oarchive output_arch(ofs); + output_arch << p_box; + } + + std::ofstream interp_file("ParameterBox2d_legacy_interpolated.dat"); + interp_file << std::setprecision(17); + for (unsigned i = 0; i <= 20u; i++) + { + for (unsigned j = 0; j <= 20u; j++) + { + c_vector point; + point[0] = 0.05 * i; + point[1] = 0.03 + 0.047 * j; + interp_file << point[0] << "\t" << point[1] << "\t" << p_box->InterpolateQoIsAt(point)[0] << "\n"; + } + } + delete p_box; + return 0; +} diff --git a/test/data/legacy_lookup_tables/ParameterBox2d_legacy.arch b/test/data/legacy_lookup_tables/ParameterBox2d_legacy.arch new file mode 100644 index 0000000..44e7e7f --- /dev/null +++ b/test/data/legacy_lookup_tables/ParameterBox2d_legacy.arch @@ -0,0 +1,802 @@ +22 serialization::archive 18 0 1 0 +0 1 -1 0 0 1 0 +1 2 2 0.00000000000000000e+00 0.00000000000000000e+00 +2 2 2 1.00000000000000000e+00 1.00000000000000000e+00 0 0 0 0 0 0 4 0 1 +3 2 2 0.00000000000000000e+00 0.00000000000000000e+00 1 +4 2 2 0.00000000000000000e+00 1.00000000000000000e+00 1 +5 2 2 1.00000000000000000e+00 0.00000000000000000e+00 1 +6 2 2 1.00000000000000000e+00 1.00000000000000000e+00 0 0 4 0 0 +7 1 0 0 0 0 +8 2 2 0.00000000000000000e+00 0.00000000000000000e+00 +9 2 2 5.00000000000000000e-01 5.00000000000000000e-01 0 0 3 0 1 +10 2 2 0.00000000000000000e+00 5.00000000000000000e-01 1 +11 2 2 5.00000000000000000e-01 0.00000000000000000e+00 1 +12 2 2 5.00000000000000000e-01 5.00000000000000000e-01 4 0 0 +13 1 0 7 0 0 +14 2 2 0.00000000000000000e+00 0.00000000000000000e+00 +15 2 2 2.50000000000000000e-01 2.50000000000000000e-01 0 0 3 0 1 +16 2 2 0.00000000000000000e+00 2.50000000000000000e-01 1 +17 2 2 2.50000000000000000e-01 0.00000000000000000e+00 1 +18 2 2 2.50000000000000000e-01 2.50000000000000000e-01 4 0 0 +19 0 0 13 0 0 +20 2 2 0.00000000000000000e+00 0.00000000000000000e+00 +21 2 2 1.25000000000000000e-01 1.25000000000000000e-01 4 0 1 3 1 +22 2 2 1.25000000000000000e-01 0.00000000000000000e+00 1 +23 2 2 0.00000000000000000e+00 1.25000000000000000e-01 1 +24 2 2 1.25000000000000000e-01 1.25000000000000000e-01 3 0 1 23 1 22 1 24 0 0 0 0 4 0 0 0 1 3 0 1 8 1 1 +25 1 0 1.00000000000000000e+00 0 0 0 1 23 8 +26 1 0 1.01562500000000000e+00 0 1 0 1.56250000000000000e-02 1 22 8 +27 1 0 1.13314845306682632e+00 0 1 0 8.86425527704437499e-03 1 24 8 +28 1 0 1.15085389764599544e+00 0 1 0 2.68467078336211973e-02 0 0 0 0 3 0 1 0 1.56250000000000000e-02 1 0 8.86425527704437499e-03 1 0 2.68467078336211973e-02 1 0 2.68467078336211973e-02 1 3 0 +29 0 0 13 0 0 +30 2 2 1.25000000000000000e-01 0.00000000000000000e+00 +31 2 2 2.50000000000000000e-01 1.25000000000000000e-01 4 0 1 22 1 17 1 24 1 +32 2 2 2.50000000000000000e-01 1.25000000000000000e-01 1 0 1 32 0 0 4 0 1 22 8 27 1 24 8 28 1 17 8 +33 1 0 1.28402541668774139e+00 0 1 0 4.03352186623227027e-02 1 32 8 +34 1 0 1.30408831382348733e+00 0 1 0 2.00628971357459385e-02 0 0 3 0 1 0 8.86425527704437499e-03 1 0 2.68467078336211973e-02 1 0 2.00628971357459385e-02 1 0 2.68467078336211973e-02 1 3 0 +35 0 0 13 0 0 +36 2 2 0.00000000000000000e+00 1.25000000000000000e-01 +37 2 2 1.25000000000000000e-01 2.50000000000000000e-01 4 0 1 23 1 24 1 16 1 +38 2 2 1.25000000000000000e-01 2.50000000000000000e-01 1 0 1 38 0 0 4 0 1 23 8 26 1 16 8 +39 1 0 1.06250000000000000e+00 0 1 0 6.25000000000000000e-02 1 24 8 28 1 38 8 +40 1 0 1.20397023138350301e+00 0 1 0 9.41827123185956516e-03 0 0 3 0 1 0 1.56250000000000000e-02 1 0 2.68467078336211973e-02 1 0 9.41827123185956516e-03 1 0 2.68467078336211973e-02 1 3 0 +41 0 0 13 0 0 +42 2 2 1.25000000000000000e-01 1.25000000000000000e-01 +43 2 2 2.50000000000000000e-01 2.50000000000000000e-01 4 0 1 24 1 32 1 38 1 18 0 0 0 0 4 0 1 24 8 28 1 38 8 40 1 32 8 34 1 18 8 +44 1 0 1.36427700523072515e+00 0 1 0 1.25628709538097016e-01 0 0 3 0 1 0 2.68467078336211973e-02 1 0 9.41827123185956516e-03 1 0 2.00628971357459385e-02 1 0 2.68467078336211973e-02 1 3 0 0 0 0 3 0 1 0 6.25000000000000000e-02 1 0 4.03352186623227027e-02 1 0 1.25628709538097016e-01 1 0 1.25628709538097016e-01 1 2 0 +45 1 0 7 0 0 +46 2 2 2.50000000000000000e-01 0.00000000000000000e+00 +47 2 2 5.00000000000000000e-01 2.50000000000000000e-01 0 0 1 0 1 +48 2 2 5.00000000000000000e-01 2.50000000000000000e-01 4 0 0 +49 0 0 45 0 0 +50 2 2 2.50000000000000000e-01 0.00000000000000000e+00 +51 2 2 3.75000000000000000e-01 1.25000000000000000e-01 4 0 1 17 1 +52 2 2 3.75000000000000000e-01 0.00000000000000000e+00 1 32 1 +53 2 2 3.75000000000000000e-01 1.25000000000000000e-01 2 0 1 52 1 53 0 0 4 0 1 17 8 33 1 32 8 34 1 52 8 +54 1 0 1.45499141461820125e+00 0 1 0 1.13819290757335434e-02 1 53 8 +55 1 0 1.47772565547161072e+00 0 1 0 3.44718552127596389e-02 0 0 2 0 1 0 1.13819290757335434e-02 1 0 3.44718552127596389e-02 1 0 3.44718552127596389e-02 1 3 0 +56 0 0 45 0 0 +57 2 2 3.75000000000000000e-01 0.00000000000000000e+00 +58 2 2 5.00000000000000000e-01 1.25000000000000000e-01 4 0 1 52 1 11 1 53 1 +59 2 2 5.00000000000000000e-01 1.25000000000000000e-01 0 0 0 0 4 0 1 52 8 54 1 53 8 55 1 11 8 +60 1 0 1.64872127070012819e+00 0 1 0 2.10419643529394351e-01 1 59 8 +61 1 0 1.67448254055481760e+00 0 1 0 2.57612698546896279e-02 0 0 2 0 1 0 1.13819290757335434e-02 1 0 3.44718552127596389e-02 1 0 3.44718552127596389e-02 1 3 0 +62 0 0 45 0 0 +63 2 2 2.50000000000000000e-01 1.25000000000000000e-01 +64 2 2 3.75000000000000000e-01 2.50000000000000000e-01 4 0 1 32 1 53 1 18 1 +65 2 2 3.75000000000000000e-01 2.50000000000000000e-01 1 0 1 65 0 0 4 0 1 32 8 34 1 18 8 44 1 53 8 55 1 65 8 +66 1 0 1.54592837803183891e+00 0 1 0 1.20932996429667927e-02 0 0 2 0 1 0 3.44718552127596389e-02 1 0 1.20932996429667927e-02 1 0 3.44718552127596389e-02 1 3 0 +67 0 0 45 0 0 +68 2 2 3.75000000000000000e-01 1.25000000000000000e-01 +69 2 2 5.00000000000000000e-01 2.50000000000000000e-01 4 0 1 53 1 59 1 65 1 48 0 0 0 0 4 0 1 53 8 55 1 65 8 66 1 59 8 61 1 48 8 +70 1 0 1.75176635011888626e+00 0 1 0 1.03045079418758068e-01 0 0 2 0 1 0 3.44718552127596389e-02 1 0 1.20932996429667927e-02 1 0 3.44718552127596389e-02 1 3 0 0 0 0 3 0 1 0 4.03352186623227027e-02 1 0 1.25628709538097016e-01 1 0 1.03045079418758068e-01 1 0 1.25628709538097016e-01 1 2 0 +71 1 0 7 0 0 +72 2 2 0.00000000000000000e+00 2.50000000000000000e-01 +73 2 2 2.50000000000000000e-01 5.00000000000000000e-01 0 0 1 0 1 +74 2 2 2.50000000000000000e-01 5.00000000000000000e-01 4 0 0 +75 0 0 71 0 0 +76 2 2 0.00000000000000000e+00 2.50000000000000000e-01 +77 2 2 1.25000000000000000e-01 3.75000000000000000e-01 4 0 1 16 1 38 1 +78 2 2 0.00000000000000000e+00 3.75000000000000000e-01 1 +79 2 2 1.25000000000000000e-01 3.75000000000000000e-01 2 0 1 78 1 79 0 0 4 0 1 16 8 39 1 78 8 +80 1 0 1.14062500000000000e+00 0 1 0 1.56250000000000000e-02 1 38 8 40 1 79 8 +81 1 0 1.29249745427934881e+00 0 1 0 2.79547397432517997e-02 0 0 2 0 1 0 1.56250000000000000e-02 1 0 2.79547397432517997e-02 1 0 2.79547397432517997e-02 1 3 0 +82 0 0 71 0 0 +83 2 2 1.25000000000000000e-01 2.50000000000000000e-01 +84 2 2 2.50000000000000000e-01 3.75000000000000000e-01 4 0 1 38 1 18 1 79 1 +85 2 2 2.50000000000000000e-01 3.75000000000000000e-01 1 0 1 85 0 0 4 0 1 38 8 40 1 79 8 81 1 18 8 44 1 85 8 +86 1 0 1.46459149090945506e+00 0 1 0 2.00628971357459385e-02 0 0 2 0 1 0 2.79547397432517997e-02 1 0 2.00628971357459385e-02 1 0 2.79547397432517997e-02 1 3 0 +87 0 0 71 0 0 +88 2 2 0.00000000000000000e+00 3.75000000000000000e-01 +89 2 2 1.25000000000000000e-01 5.00000000000000000e-01 4 0 1 78 1 79 1 10 1 +90 2 2 1.25000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 78 8 80 1 10 8 +91 1 0 1.25000000000000000e+00 0 1 0 2.50000000000000000e-01 1 79 8 81 1 90 8 +92 1 0 1.41643556633353285e+00 0 1 0 1.10803190963055798e-02 0 0 2 0 1 0 1.56250000000000000e-02 1 0 2.79547397432517997e-02 1 0 2.79547397432517997e-02 1 3 0 +93 0 0 71 0 0 +94 2 2 1.25000000000000000e-01 3.75000000000000000e-01 +95 2 2 2.50000000000000000e-01 5.00000000000000000e-01 4 0 1 79 1 85 1 90 1 74 0 0 0 0 4 0 1 79 8 81 1 90 8 92 1 85 8 86 1 74 8 +96 1 0 1.60503177085967685e+00 0 1 0 5.04190233279033784e-02 0 0 2 0 1 0 2.79547397432517997e-02 1 0 2.00628971357459385e-02 1 0 2.79547397432517997e-02 1 3 0 0 0 0 3 0 1 0 6.25000000000000000e-02 1 0 1.25628709538097016e-01 1 0 5.04190233279033784e-02 1 0 1.25628709538097016e-01 1 2 0 +97 1 0 7 0 0 +98 2 2 2.50000000000000000e-01 2.50000000000000000e-01 +99 2 2 5.00000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 0 +100 0 0 97 0 0 +101 2 2 2.50000000000000000e-01 2.50000000000000000e-01 +102 2 2 3.75000000000000000e-01 3.75000000000000000e-01 4 0 1 18 1 65 1 85 1 +103 2 2 3.75000000000000000e-01 3.75000000000000000e-01 1 0 1 103 0 0 4 0 1 18 8 44 1 85 8 86 1 65 8 66 1 103 8 +104 1 0 1.65959958229888582e+00 0 1 0 3.58945963472263596e-02 0 0 1 0 1 0 3.58945963472263596e-02 1 0 3.58945963472263596e-02 1 3 0 +105 0 0 97 0 0 +106 2 2 3.75000000000000000e-01 2.50000000000000000e-01 +107 2 2 5.00000000000000000e-01 3.75000000000000000e-01 4 0 1 65 1 48 1 103 1 +108 2 2 5.00000000000000000e-01 3.75000000000000000e-01 0 0 0 0 4 0 1 65 8 66 1 103 8 104 1 48 8 70 1 108 8 +109 1 0 1.88057269939233374e+00 0 1 0 2.57612698546896279e-02 0 0 1 0 1 0 3.58945963472263596e-02 1 0 3.58945963472263596e-02 1 3 0 +110 0 0 97 0 0 +111 2 2 2.50000000000000000e-01 3.75000000000000000e-01 +112 2 2 3.75000000000000000e-01 5.00000000000000000e-01 4 0 1 85 1 103 1 74 1 +113 2 2 3.75000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 85 8 86 1 74 8 96 1 103 8 104 1 113 8 +114 1 0 1.81873926827275145e+00 0 1 0 1.42274113446672068e-02 0 0 1 0 1 0 3.58945963472263596e-02 1 0 3.58945963472263596e-02 1 3 0 +115 0 0 97 0 0 +116 2 2 3.75000000000000000e-01 3.75000000000000000e-01 +117 2 2 5.00000000000000000e-01 5.00000000000000000e-01 4 0 1 103 1 108 1 113 1 12 0 0 0 0 4 0 1 103 8 104 1 113 8 114 1 108 8 109 1 12 8 +118 1 0 2.06090158837516046e+00 0 1 0 7.27809782969123020e-01 0 0 1 0 1 0 3.58945963472263596e-02 1 0 3.58945963472263596e-02 1 3 0 0 0 0 3 0 1 0 1.25628709538097016e-01 1 0 5.04190233279033784e-02 1 0 1.03045079418758068e-01 1 0 1.25628709538097016e-01 1 2 0 0 0 0 3 0 1 0 2.50000000000000000e-01 1 0 2.10419643529394351e-01 1 0 7.27809782969123020e-01 1 0 7.27809782969123020e-01 1 1 0 +119 1 0 0 0 0 +120 2 2 5.00000000000000000e-01 0.00000000000000000e+00 +121 2 2 1.00000000000000000e+00 5.00000000000000000e-01 0 0 1 0 1 +122 2 2 1.00000000000000000e+00 5.00000000000000000e-01 4 0 0 +123 1 0 119 0 0 +124 2 2 5.00000000000000000e-01 0.00000000000000000e+00 +125 2 2 7.50000000000000000e-01 2.50000000000000000e-01 0 0 2 0 1 +126 2 2 7.50000000000000000e-01 0.00000000000000000e+00 1 +127 2 2 7.50000000000000000e-01 2.50000000000000000e-01 4 0 0 +128 0 0 123 0 0 +129 2 2 5.00000000000000000e-01 0.00000000000000000e+00 +130 2 2 6.25000000000000000e-01 1.25000000000000000e-01 4 0 1 11 1 +131 2 2 6.25000000000000000e-01 0.00000000000000000e+00 1 59 1 +132 2 2 6.25000000000000000e-01 1.25000000000000000e-01 3 0 1 59 1 131 1 132 0 0 4 0 1 11 8 60 1 59 8 61 1 131 8 +133 1 0 1.86824595743222233e+00 0 1 0 1.46146862241791631e-02 1 132 8 +134 1 0 1.89743730051710080e+00 0 1 0 4.42627382535631941e-02 0 0 3 0 1 0 2.57612698546896279e-02 1 0 1.46146862241791631e-02 1 0 4.42627382535631941e-02 1 0 4.42627382535631941e-02 1 3 0 +135 0 0 123 0 0 +136 2 2 6.25000000000000000e-01 0.00000000000000000e+00 +137 2 2 7.50000000000000000e-01 1.25000000000000000e-01 4 0 1 131 1 126 1 132 1 +138 2 2 7.50000000000000000e-01 1.25000000000000000e-01 1 0 1 138 0 0 4 0 1 131 8 133 1 132 8 134 1 126 8 +139 1 0 2.11700001661267478e+00 0 1 0 6.65015329669120803e-02 1 138 8 +140 1 0 2.15007814187224788e+00 0 1 0 3.30781252595730990e-02 0 0 3 0 1 0 1.46146862241791631e-02 1 0 4.42627382535631941e-02 1 0 3.30781252595730990e-02 1 0 4.42627382535631941e-02 1 3 0 +141 0 0 123 0 0 +142 2 2 5.00000000000000000e-01 1.25000000000000000e-01 +143 2 2 6.25000000000000000e-01 2.50000000000000000e-01 4 0 1 59 1 132 1 48 1 +144 2 2 6.25000000000000000e-01 2.50000000000000000e-01 1 0 1 144 0 0 4 0 1 59 8 61 1 48 8 70 1 132 8 134 1 144 8 +145 1 0 1.98501132977173622e+00 0 1 0 1.55281041131907216e-02 0 0 3 0 1 0 2.57612698546896279e-02 1 0 4.42627382535631941e-02 1 0 1.55281041131907216e-02 1 0 4.42627382535631941e-02 1 3 0 +146 0 0 123 0 0 +147 2 2 6.25000000000000000e-01 1.25000000000000000e-01 +148 2 2 7.50000000000000000e-01 2.50000000000000000e-01 4 0 1 132 1 138 1 144 1 127 0 0 0 0 4 0 1 132 8 134 1 144 8 145 1 138 8 140 1 127 8 +149 1 0 2.24931251765096718e+00 0 1 0 2.07126725626067820e-01 0 0 3 0 1 0 4.42627382535631941e-02 1 0 1.55281041131907216e-02 1 0 3.30781252595730990e-02 1 0 4.42627382535631941e-02 1 3 0 0 0 0 2 0 1 0 6.65015329669120803e-02 1 0 2.07126725626067820e-01 1 0 2.07126725626067820e-01 1 2 0 +150 1 0 119 0 0 +151 2 2 7.50000000000000000e-01 0.00000000000000000e+00 +152 2 2 1.00000000000000000e+00 2.50000000000000000e-01 0 0 1 0 1 +153 2 2 1.00000000000000000e+00 2.50000000000000000e-01 4 0 0 +154 1 0 150 0 0 +155 2 2 7.50000000000000000e-01 0.00000000000000000e+00 +156 2 2 8.75000000000000000e-01 1.25000000000000000e-01 0 0 2 0 1 +157 2 2 8.75000000000000000e-01 0.00000000000000000e+00 1 +158 2 2 8.75000000000000000e-01 1.25000000000000000e-01 4 0 0 +159 0 0 154 0 0 +160 2 2 7.50000000000000000e-01 0.00000000000000000e+00 +161 2 2 8.12500000000000000e-01 6.25000000000000000e-02 4 0 1 126 1 +162 2 2 8.12500000000000000e-01 0.00000000000000000e+00 1 +163 2 2 7.50000000000000000e-01 6.25000000000000000e-02 1 +164 2 2 8.12500000000000000e-01 6.25000000000000000e-02 3 0 1 163 1 162 1 164 0 0 4 0 1 126 8 139 1 163 8 +165 1 0 2.12526954792756806e+00 0 1 0 8.26953131489327475e-03 1 162 8 +166 1 0 2.25353478721320855e+00 0 1 0 4.40286807667789049e-03 1 164 8 +167 1 0 2.26233765747576010e+00 0 1 0 1.32401357460785185e-02 0 0 3 0 1 0 8.26953131489327475e-03 1 0 4.40286807667789049e-03 1 0 1.32401357460785185e-02 1 0 1.32401357460785185e-02 1 4 0 +168 0 0 154 0 0 +169 2 2 8.12500000000000000e-01 0.00000000000000000e+00 +170 2 2 8.75000000000000000e-01 6.25000000000000000e-02 4 0 1 162 1 157 1 164 1 +171 2 2 8.75000000000000000e-01 6.25000000000000000e-02 1 0 1 171 0 0 4 0 1 162 8 166 1 164 8 167 1 157 8 +172 1 0 2.39887529396709809e+00 0 1 0 1.87656285687616275e-02 1 171 8 +173 1 0 2.40824590058415700e+00 0 1 0 9.37060661705890752e-03 0 0 3 0 1 0 4.40286807667789049e-03 1 0 1.32401357460785185e-02 1 0 9.37060661705890752e-03 1 0 1.32401357460785185e-02 1 4 0 +174 0 0 154 0 0 +175 2 2 7.50000000000000000e-01 6.25000000000000000e-02 +176 2 2 8.12500000000000000e-01 1.25000000000000000e-01 4 0 1 163 1 164 1 138 1 +177 2 2 8.12500000000000000e-01 1.25000000000000000e-01 1 0 1 177 0 0 4 0 1 163 8 165 1 138 8 140 1 164 8 167 1 177 8 +178 1 0 2.28874626826341476e+00 0 1 0 4.47166289037603804e-03 0 0 3 0 1 0 8.26953131489327475e-03 1 0 1.32401357460785185e-02 1 0 4.47166289037603804e-03 1 0 1.32401357460785185e-02 1 4 0 +179 0 0 154 0 0 +180 2 2 8.12500000000000000e-01 6.25000000000000000e-02 +181 2 2 8.75000000000000000e-01 1.25000000000000000e-01 4 0 1 164 1 171 1 177 1 158 0 0 0 0 4 0 1 164 8 167 1 177 8 178 1 171 8 173 1 158 8 +182 1 0 2.43635772043533416e+00 0 1 0 5.68344809297713915e-02 0 0 3 0 1 0 1.32401357460785185e-02 1 0 4.47166289037603804e-03 1 0 9.37060661705890752e-03 1 0 1.32401357460785185e-02 1 4 0 0 0 0 2 0 1 0 1.87656285687616275e-02 1 0 5.68344809297713915e-02 1 0 5.68344809297713915e-02 1 3 0 +183 1 0 150 0 0 +184 2 2 8.75000000000000000e-01 0.00000000000000000e+00 +185 2 2 1.00000000000000000e+00 1.25000000000000000e-01 0 0 1 0 1 +186 2 2 1.00000000000000000e+00 1.25000000000000000e-01 4 0 0 +187 0 0 183 0 0 +188 2 2 8.75000000000000000e-01 0.00000000000000000e+00 +189 2 2 9.37500000000000000e-01 6.25000000000000000e-02 4 0 1 157 1 +190 2 2 9.37500000000000000e-01 0.00000000000000000e+00 1 171 1 +191 2 2 9.37500000000000000e-01 6.25000000000000000e-02 2 0 1 190 1 191 0 0 4 0 1 157 8 172 1 171 8 173 1 190 8 +192 1 0 2.55358945806292681e+00 0 1 0 4.98910315014455819e-03 1 191 8 +193 1 0 2.56356441688348502e+00 0 1 0 1.50030393390636618e-02 0 0 2 0 1 0 4.98910315014455819e-03 1 0 1.50030393390636618e-02 1 0 1.50030393390636618e-02 1 4 0 +194 0 0 183 0 0 +195 2 2 9.37500000000000000e-01 0.00000000000000000e+00 +196 2 2 1.00000000000000000e+00 6.25000000000000000e-02 4 0 1 190 1 5 1 191 1 +197 2 2 1.00000000000000000e+00 6.25000000000000000e-02 1 0 1 197 0 0 4 0 1 190 8 192 1 191 8 193 1 5 8 +198 1 0 2.71828182845904509e+00 0 0 0 1 197 8 +199 1 0 2.72890011685146305e+00 0 1 0 1.06182883924184068e-02 0 0 3 0 1 0 4.98910315014455819e-03 1 0 1.50030393390636618e-02 1 0 1.06182883924184068e-02 1 0 1.50030393390636618e-02 1 4 0 +200 0 0 183 0 0 +201 2 2 8.75000000000000000e-01 6.25000000000000000e-02 +202 2 2 9.37500000000000000e-01 1.25000000000000000e-01 4 0 1 171 1 191 1 158 1 +203 2 2 9.37500000000000000e-01 1.25000000000000000e-01 1 0 1 203 0 0 4 0 1 171 8 173 1 158 8 182 1 191 8 193 1 203 8 +204 1 0 2.59348929334516010e+00 0 1 0 5.06705788686589997e-03 0 0 2 0 1 0 1.50030393390636618e-02 1 0 5.06705788686589997e-03 1 0 1.50030393390636618e-02 1 4 0 +205 0 0 183 0 0 +206 2 2 9.37500000000000000e-01 6.25000000000000000e-02 +207 2 2 1.00000000000000000e+00 1.25000000000000000e-01 4 0 1 191 1 197 1 203 1 186 0 0 0 0 4 0 1 191 8 193 1 203 8 204 1 197 8 199 1 186 8 +208 1 0 2.76075498202871783e+00 0 1 0 4.24731535696722950e-02 0 0 3 0 1 0 1.50030393390636618e-02 1 0 5.06705788686589997e-03 1 0 1.06182883924184068e-02 1 0 1.50030393390636618e-02 1 4 0 0 0 0 3 0 1 0 1.87656285687616275e-02 1 0 5.68344809297713915e-02 1 0 4.24731535696722950e-02 1 0 5.68344809297713915e-02 1 3 0 +209 1 0 150 0 0 +210 2 2 7.50000000000000000e-01 1.25000000000000000e-01 +211 2 2 8.75000000000000000e-01 2.50000000000000000e-01 0 0 1 0 1 +212 2 2 8.75000000000000000e-01 2.50000000000000000e-01 4 0 0 +213 0 0 209 0 0 +214 2 2 7.50000000000000000e-01 1.25000000000000000e-01 +215 2 2 8.12500000000000000e-01 1.87500000000000000e-01 4 0 1 138 1 177 1 +216 2 2 7.50000000000000000e-01 1.87500000000000000e-01 1 +217 2 2 8.12500000000000000e-01 1.87500000000000000e-01 2 0 1 216 1 217 0 0 4 0 1 138 8 140 1 216 8 +218 1 0 2.19142579844671426e+00 0 1 0 8.26953131489327475e-03 1 177 8 178 1 217 8 +219 1 0 2.33276061957617298e+00 0 1 0 1.33777253734743695e-02 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.33777253734743695e-02 1 0 1.33777253734743695e-02 1 4 0 +220 0 0 209 0 0 +221 2 2 8.12500000000000000e-01 1.25000000000000000e-01 +222 2 2 8.75000000000000000e-01 1.87500000000000000e-01 4 0 1 177 1 158 1 217 1 +223 2 2 8.75000000000000000e-01 1.87500000000000000e-01 1 0 1 223 0 0 4 0 1 177 8 178 1 217 8 219 1 158 8 182 1 223 8 +224 1 0 2.48321075352062870e+00 0 1 0 9.37060661705935161e-03 0 0 2 0 1 0 1.33777253734743695e-02 1 0 9.37060661705935161e-03 1 0 1.33777253734743695e-02 1 4 0 +225 0 0 209 0 0 +226 2 2 7.50000000000000000e-01 1.87500000000000000e-01 +227 2 2 8.12500000000000000e-01 2.50000000000000000e-01 4 0 1 216 1 217 1 127 1 +228 2 2 8.12500000000000000e-01 2.50000000000000000e-01 0 0 0 0 4 0 1 216 8 218 1 127 8 149 1 217 8 219 1 228 8 +229 1 0 2.39438071141403430e+00 0 1 0 4.67804733147003660e-03 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.33777253734743695e-02 1 0 1.33777253734743695e-02 1 4 0 +230 0 0 209 0 0 +231 2 2 8.12500000000000000e-01 1.87500000000000000e-01 +232 2 2 8.75000000000000000e-01 2.50000000000000000e-01 4 0 1 217 1 223 1 228 1 212 0 0 0 0 4 0 1 217 8 219 1 228 8 229 1 223 8 224 1 212 8 +233 1 0 2.54880499984004150e+00 0 1 0 1.99384803543098954e-02 0 0 2 0 1 0 1.33777253734743695e-02 1 0 9.37060661705935161e-03 1 0 1.33777253734743695e-02 1 4 0 0 0 0 2 0 1 0 5.68344809297713915e-02 1 0 1.99384803543098954e-02 1 0 5.68344809297713915e-02 1 3 0 +234 1 0 150 0 0 +235 2 2 8.75000000000000000e-01 1.25000000000000000e-01 +236 2 2 1.00000000000000000e+00 2.50000000000000000e-01 0 0 0 0 4 0 0 +237 0 0 234 0 0 +238 2 2 8.75000000000000000e-01 1.25000000000000000e-01 +239 2 2 9.37500000000000000e-01 1.87500000000000000e-01 4 0 1 158 1 203 1 223 1 +240 2 2 9.37500000000000000e-01 1.87500000000000000e-01 1 0 1 240 0 0 4 0 1 158 8 182 1 223 8 224 1 203 8 204 1 240 8 +241 1 0 2.64336408744795159e+00 0 1 0 1.51589488125059013e-02 0 0 1 0 1 0 1.51589488125059013e-02 1 0 1.51589488125059013e-02 1 4 0 +242 0 0 234 0 0 +243 2 2 9.37500000000000000e-01 1.25000000000000000e-01 +244 2 2 1.00000000000000000e+00 1.87500000000000000e-01 4 0 1 203 1 186 1 240 1 +245 2 2 1.00000000000000000e+00 1.87500000000000000e-01 1 0 1 245 0 0 4 0 1 203 8 204 1 240 8 241 1 186 8 208 1 245 8 +246 1 0 2.81384642399080853e+00 0 1 0 1.06182883924184068e-02 0 0 2 0 1 0 1.51589488125059013e-02 1 0 1.06182883924184068e-02 1 0 1.51589488125059013e-02 1 4 0 +247 0 0 234 0 0 +248 2 2 8.75000000000000000e-01 1.87500000000000000e-01 +249 2 2 9.37500000000000000e-01 2.50000000000000000e-01 4 0 1 223 1 240 1 212 1 +250 2 2 9.37500000000000000e-01 2.50000000000000000e-01 0 0 0 0 4 0 1 223 8 224 1 212 8 233 1 240 8 241 1 250 8 +251 1 0 2.71318879919185996e+00 0 1 0 5.30092209702859307e-03 0 0 1 0 1 0 1.51589488125059013e-02 1 0 1.51589488125059013e-02 1 4 0 +252 0 0 234 0 0 +253 2 2 9.37500000000000000e-01 1.87500000000000000e-01 +254 2 2 1.00000000000000000e+00 2.50000000000000000e-01 4 0 1 240 1 245 1 250 1 153 0 0 0 0 4 0 1 240 8 241 1 250 8 251 1 245 8 246 1 153 8 +255 1 0 2.88817444273773560e+00 0 1 0 1.69892614278690068e-01 0 0 2 0 1 0 1.51589488125059013e-02 1 0 1.06182883924184068e-02 1 0 1.51589488125059013e-02 1 4 0 0 0 0 3 0 1 0 5.68344809297713915e-02 1 0 1.99384803543098954e-02 1 0 4.24731535696722950e-02 1 0 5.68344809297713915e-02 1 3 0 0 0 0 3 0 1 0 6.65015329669120803e-02 1 0 2.07126725626067820e-01 1 0 1.69892614278690068e-01 1 0 2.07126725626067820e-01 1 2 0 +256 1 0 119 0 0 +257 2 2 5.00000000000000000e-01 2.50000000000000000e-01 +258 2 2 7.50000000000000000e-01 5.00000000000000000e-01 0 0 1 0 1 +259 2 2 7.50000000000000000e-01 5.00000000000000000e-01 4 0 0 +260 0 0 256 0 0 +261 2 2 5.00000000000000000e-01 2.50000000000000000e-01 +262 2 2 6.25000000000000000e-01 3.75000000000000000e-01 4 0 1 48 1 144 1 108 1 +263 2 2 6.25000000000000000e-01 3.75000000000000000e-01 2 0 1 108 1 263 0 0 4 0 1 48 8 70 1 108 8 109 1 144 8 145 1 263 8 +264 1 0 2.13096804519612881e+00 0 1 0 4.60895740315856450e-02 0 0 2 0 1 0 2.57612698546896279e-02 1 0 4.60895740315856450e-02 1 0 4.60895740315856450e-02 1 3 0 +265 0 0 256 0 0 +266 2 2 6.25000000000000000e-01 2.50000000000000000e-01 +267 2 2 7.50000000000000000e-01 3.75000000000000000e-01 4 0 1 144 1 127 1 263 1 +268 2 2 7.50000000000000000e-01 3.75000000000000000e-01 1 0 1 268 0 0 4 0 1 144 8 145 1 263 8 264 1 127 8 149 1 268 8 +269 1 0 2.41470314394883223e+00 0 1 0 3.30781252595730990e-02 0 0 2 0 1 0 4.60895740315856450e-02 1 0 3.30781252595730990e-02 1 0 4.60895740315856450e-02 1 3 0 +270 0 0 256 0 0 +271 2 2 5.00000000000000000e-01 3.75000000000000000e-01 +272 2 2 6.25000000000000000e-01 5.00000000000000000e-01 4 0 1 108 1 263 1 12 1 +273 2 2 6.25000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 108 8 109 1 12 8 118 1 263 8 264 1 273 8 +274 1 0 2.33530744679027791e+00 0 1 0 1.82683577802240649e-02 0 0 2 0 1 0 2.57612698546896279e-02 1 0 4.60895740315856450e-02 1 0 4.60895740315856450e-02 1 3 0 +275 0 0 256 0 0 +276 2 2 6.25000000000000000e-01 3.75000000000000000e-01 +277 2 2 7.50000000000000000e-01 5.00000000000000000e-01 4 0 1 263 1 268 1 273 1 259 0 0 0 0 4 0 1 263 8 264 1 273 8 274 1 268 8 269 1 259 8 +278 1 0 2.64625002076584348e+00 0 1 0 8.31269162086396562e-02 0 0 2 0 1 0 4.60895740315856450e-02 1 0 3.30781252595730990e-02 1 0 4.60895740315856450e-02 1 3 0 0 0 0 2 0 1 0 2.07126725626067820e-01 1 0 8.31269162086396562e-02 1 0 2.07126725626067820e-01 1 2 0 +279 1 0 119 0 0 +280 2 2 7.50000000000000000e-01 2.50000000000000000e-01 +281 2 2 1.00000000000000000e+00 5.00000000000000000e-01 0 0 0 0 4 0 0 +282 1 0 279 0 0 +283 2 2 7.50000000000000000e-01 2.50000000000000000e-01 +284 2 2 8.75000000000000000e-01 3.75000000000000000e-01 0 0 1 0 1 +285 2 2 8.75000000000000000e-01 3.75000000000000000e-01 4 0 0 +286 0 0 282 0 0 +287 2 2 7.50000000000000000e-01 2.50000000000000000e-01 +288 2 2 8.12500000000000000e-01 3.12500000000000000e-01 4 0 1 127 1 228 1 +289 2 2 7.50000000000000000e-01 3.12500000000000000e-01 1 +290 2 2 8.12500000000000000e-01 3.12500000000000000e-01 3 0 1 289 1 228 1 290 0 0 4 0 1 127 8 149 1 289 8 +291 1 0 2.32373829948500621e+00 0 1 0 8.26953131489371884e-03 1 228 8 229 1 290 8 +292 1 0 2.47360654377699829e+00 0 1 0 1.36529046282674038e-02 0 0 3 0 1 0 8.26953131489371884e-03 1 0 4.67804733147003660e-03 1 0 1.36529046282674038e-02 1 0 1.36529046282674038e-02 1 4 0 +293 0 0 282 0 0 +294 2 2 8.12500000000000000e-01 2.50000000000000000e-01 +295 2 2 8.75000000000000000e-01 3.12500000000000000e-01 4 0 1 228 1 212 1 290 1 +296 2 2 8.75000000000000000e-01 3.12500000000000000e-01 1 0 1 296 0 0 4 0 1 228 8 229 1 290 8 292 1 212 8 233 1 296 8 +297 1 0 2.63314045939357255e+00 0 1 0 9.37060661705890752e-03 0 0 3 0 1 0 4.67804733147003660e-03 1 0 1.36529046282674038e-02 1 0 9.37060661705890752e-03 1 0 1.36529046282674038e-02 1 4 0 +298 0 0 282 0 0 +299 2 2 7.50000000000000000e-01 3.12500000000000000e-01 +300 2 2 8.12500000000000000e-01 3.75000000000000000e-01 4 0 1 289 1 290 1 268 1 +301 2 2 8.12500000000000000e-01 3.75000000000000000e-01 1 0 1 301 0 0 4 0 1 289 8 291 1 268 8 269 1 290 8 292 1 301 8 +302 1 0 2.57043811666506583e+00 0 1 0 5.02202139996077435e-03 0 0 3 0 1 0 8.26953131489371884e-03 1 0 1.36529046282674038e-02 1 0 5.02202139996077435e-03 1 0 1.36529046282674038e-02 1 4 0 +303 0 0 282 0 0 +304 2 2 8.12500000000000000e-01 3.12500000000000000e-01 +305 2 2 8.75000000000000000e-01 3.75000000000000000e-01 4 0 1 290 1 296 1 301 1 285 0 0 0 0 4 0 1 290 8 292 1 301 8 302 1 296 8 297 1 285 8 +306 1 0 2.73621713218122142e+00 0 1 0 5.91801845008665950e-02 0 0 3 0 1 0 1.36529046282674038e-02 1 0 5.02202139996077435e-03 1 0 9.37060661705890752e-03 1 0 1.36529046282674038e-02 1 4 0 0 0 0 1 0 1 0 5.91801845008665950e-02 1 0 5.91801845008665950e-02 1 3 0 +307 1 0 279 0 0 +308 2 2 8.75000000000000000e-01 2.50000000000000000e-01 +309 2 2 1.00000000000000000e+00 3.75000000000000000e-01 0 0 1 0 1 +310 2 2 1.00000000000000000e+00 3.75000000000000000e-01 4 0 0 +311 0 0 307 0 0 +312 2 2 8.75000000000000000e-01 2.50000000000000000e-01 +313 2 2 9.37500000000000000e-01 3.12500000000000000e-01 4 0 1 212 1 250 1 296 1 +314 2 2 9.37500000000000000e-01 3.12500000000000000e-01 2 0 1 250 1 314 0 0 4 0 1 212 8 233 1 296 8 297 1 250 8 251 1 314 8 +315 1 0 2.80296342857688430e+00 0 1 0 1.54707677593899362e-02 0 0 2 0 1 0 5.30092209702859307e-03 1 0 1.54707677593899362e-02 1 0 1.54707677593899362e-02 1 4 0 +316 0 0 307 0 0 +317 2 2 9.37500000000000000e-01 2.50000000000000000e-01 +318 2 2 1.00000000000000000e+00 3.12500000000000000e-01 4 0 1 250 1 153 1 314 1 +319 2 2 1.00000000000000000e+00 3.12500000000000000e-01 1 0 1 319 0 0 4 0 1 250 8 251 1 314 8 315 1 153 8 255 1 319 8 +320 1 0 2.98373903826949860e+00 0 1 0 1.06182883924184068e-02 0 0 3 0 1 0 5.30092209702859307e-03 1 0 1.54707677593899362e-02 1 0 1.06182883924184068e-02 1 0 1.54707677593899362e-02 1 4 0 +321 0 0 307 0 0 +322 2 2 8.75000000000000000e-01 3.12500000000000000e-01 +323 2 2 9.37500000000000000e-01 3.75000000000000000e-01 4 0 1 296 1 314 1 285 1 +324 2 2 9.37500000000000000e-01 3.75000000000000000e-01 1 0 1 324 0 0 4 0 1 296 8 297 1 285 8 306 1 314 8 315 1 324 8 +325 1 0 2.91268797560302595e+00 0 1 0 5.69069578063396975e-03 0 0 2 0 1 0 1.54707677593899362e-02 1 0 5.69069578063396975e-03 1 0 1.54707677593899362e-02 1 4 0 +326 0 0 307 0 0 +327 2 2 9.37500000000000000e-01 3.12500000000000000e-01 +328 2 2 1.00000000000000000e+00 3.75000000000000000e-01 4 0 1 314 1 319 1 324 1 310 0 0 0 0 4 0 1 314 8 315 1 324 8 325 1 319 8 320 1 310 8 +329 1 0 3.10054021058609841e+00 0 1 0 4.24731535696727391e-02 0 0 3 0 1 0 1.54707677593899362e-02 1 0 5.69069578063396975e-03 1 0 1.06182883924184068e-02 1 0 1.54707677593899362e-02 1 4 0 0 0 0 2 0 1 0 5.91801845008665950e-02 1 0 4.24731535696727391e-02 1 0 5.91801845008665950e-02 1 3 0 +330 1 0 279 0 0 +331 2 2 7.50000000000000000e-01 3.75000000000000000e-01 +332 2 2 8.75000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 0 +333 0 0 330 0 0 +334 2 2 7.50000000000000000e-01 3.75000000000000000e-01 +335 2 2 8.12500000000000000e-01 4.37500000000000000e-01 4 0 1 268 1 301 1 +336 2 2 7.50000000000000000e-01 4.37500000000000000e-01 1 +337 2 2 8.12500000000000000e-01 4.37500000000000000e-01 2 0 1 336 1 337 0 0 4 0 1 268 8 269 1 336 8 +338 1 0 2.52220705104244436e+00 0 1 0 8.26953131489327475e-03 1 301 8 302 1 337 8 +339 1 0 2.68487543007823692e+00 0 1 0 1.40656735104554009e-02 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.40656735104554009e-02 1 0 1.40656735104554009e-02 1 4 0 +340 0 0 330 0 0 +341 2 2 8.12500000000000000e-01 3.75000000000000000e-01 +342 2 2 8.75000000000000000e-01 4.37500000000000000e-01 4 0 1 301 1 285 1 337 1 +343 2 2 8.75000000000000000e-01 4.37500000000000000e-01 1 0 1 343 0 0 4 0 1 301 8 302 1 337 8 339 1 285 8 306 1 343 8 +344 1 0 2.85803501820298811e+00 0 1 0 9.37060661705890752e-03 0 0 2 0 1 0 1.40656735104554009e-02 1 0 9.37060661705890752e-03 1 0 1.40656735104554009e-02 1 4 0 +345 0 0 330 0 0 +346 2 2 7.50000000000000000e-01 4.37500000000000000e-01 +347 2 2 8.12500000000000000e-01 5.00000000000000000e-01 4 0 1 336 1 337 1 259 1 +348 2 2 8.12500000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 336 8 338 1 259 8 278 1 337 8 339 1 348 8 +349 1 0 2.81691848401651068e+00 0 1 0 5.50358509584736311e-03 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.40656735104554009e-02 1 0 1.40656735104554009e-02 1 4 0 +350 0 0 330 0 0 +351 2 2 8.12500000000000000e-01 4.37500000000000000e-01 +352 2 2 8.75000000000000000e-01 5.00000000000000000e-01 4 0 1 337 1 343 1 348 1 +353 2 2 8.75000000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 337 8 339 1 348 8 349 1 343 8 344 1 353 8 +354 1 0 2.99859411745887261e+00 0 1 0 2.34570357109520344e-02 0 0 2 0 1 0 1.40656735104554009e-02 1 0 9.37060661705890752e-03 1 0 1.40656735104554009e-02 1 4 0 0 0 0 1 0 1 0 5.91801845008665950e-02 1 0 5.91801845008665950e-02 1 3 0 +355 1 0 279 0 0 +356 2 2 8.75000000000000000e-01 3.75000000000000000e-01 +357 2 2 1.00000000000000000e+00 5.00000000000000000e-01 0 0 0 0 4 0 0 +358 0 0 355 0 0 +359 2 2 8.75000000000000000e-01 3.75000000000000000e-01 +360 2 2 9.37500000000000000e-01 4.37500000000000000e-01 4 0 1 285 1 324 1 343 1 +361 2 2 9.37500000000000000e-01 4.37500000000000000e-01 1 0 1 361 0 0 4 0 1 285 8 306 1 343 8 344 1 324 8 325 1 361 8 +362 1 0 3.04236244027028402e+00 0 1 0 1.59384961797157665e-02 0 0 1 0 1 0 1.59384961797157665e-02 1 0 1.59384961797157665e-02 1 4 0 +363 0 0 355 0 0 +364 2 2 9.37500000000000000e-01 3.75000000000000000e-01 +365 2 2 1.00000000000000000e+00 4.37500000000000000e-01 4 0 1 324 1 310 1 361 1 +366 2 2 1.00000000000000000e+00 4.37500000000000000e-01 1 0 1 366 0 0 4 0 1 324 8 325 1 361 8 362 1 310 8 329 1 366 8 +367 1 0 3.23857795968753415e+00 0 1 0 1.06182883924184068e-02 0 0 2 0 1 0 1.59384961797157665e-02 1 0 1.06182883924184068e-02 1 0 1.59384961797157665e-02 1 4 0 +368 0 0 355 0 0 +369 2 2 8.75000000000000000e-01 4.37500000000000000e-01 +370 2 2 9.37500000000000000e-01 5.00000000000000000e-01 4 0 1 343 1 361 1 353 1 +371 2 2 9.37500000000000000e-01 5.00000000000000000e-01 0 0 0 0 4 0 1 343 8 344 1 353 8 354 1 361 8 362 1 371 8 +372 1 0 3.19198682257865851e+00 0 1 0 6.23637893768069773e-03 0 0 1 0 1 0 1.59384961797157665e-02 1 0 1.59384961797157665e-02 1 4 0 +373 0 0 355 0 0 +374 2 2 9.37500000000000000e-01 4.37500000000000000e-01 +375 2 2 1.00000000000000000e+00 5.00000000000000000e-01 4 0 1 361 1 366 1 371 1 122 0 0 0 0 4 0 1 361 8 362 1 371 8 372 1 366 8 367 1 122 8 +376 1 0 3.39785228557380625e+00 0 1 0 6.79570457114761606e-01 0 0 2 0 1 0 1.59384961797157665e-02 1 0 1.06182883924184068e-02 1 0 1.59384961797157665e-02 1 4 0 0 0 0 2 0 1 0 5.91801845008665950e-02 1 0 4.24731535696727391e-02 1 0 5.91801845008665950e-02 1 3 0 0 0 0 3 0 1 0 2.07126725626067820e-01 1 0 8.31269162086396562e-02 1 0 1.69892614278690068e-01 1 0 2.07126725626067820e-01 1 2 0 0 0 0 3 0 1 0 2.10419643529394351e-01 1 0 7.27809782969123020e-01 1 0 6.79570457114761606e-01 1 0 7.27809782969123020e-01 1 1 0 +377 1 0 0 0 0 +378 2 2 0.00000000000000000e+00 5.00000000000000000e-01 +379 2 2 5.00000000000000000e-01 1.00000000000000000e+00 0 0 1 0 1 +380 2 2 5.00000000000000000e-01 1.00000000000000000e+00 4 0 0 +381 1 0 377 0 0 +382 2 2 0.00000000000000000e+00 5.00000000000000000e-01 +383 2 2 2.50000000000000000e-01 7.50000000000000000e-01 0 0 2 0 1 +384 2 2 0.00000000000000000e+00 7.50000000000000000e-01 1 +385 2 2 2.50000000000000000e-01 7.50000000000000000e-01 4 0 0 +386 0 0 381 0 0 +387 2 2 0.00000000000000000e+00 5.00000000000000000e-01 +388 2 2 1.25000000000000000e-01 6.25000000000000000e-01 4 0 1 10 1 90 1 +389 2 2 0.00000000000000000e+00 6.25000000000000000e-01 1 +390 2 2 1.25000000000000000e-01 6.25000000000000000e-01 3 0 1 389 1 90 1 390 0 0 4 0 1 10 8 91 1 389 8 +391 1 0 1.39062500000000000e+00 0 1 0 1.56250000000000000e-02 1 90 8 92 1 390 8 +392 1 0 1.57578456754605534e+00 0 1 0 3.01708035625127824e-02 0 0 3 0 1 0 1.56250000000000000e-02 1 0 1.10803190963055798e-02 1 0 3.01708035625127824e-02 1 0 3.01708035625127824e-02 1 3 0 +393 0 0 381 0 0 +394 2 2 1.25000000000000000e-01 5.00000000000000000e-01 +395 2 2 2.50000000000000000e-01 6.25000000000000000e-01 4 0 1 90 1 74 1 390 1 +396 2 2 2.50000000000000000e-01 6.25000000000000000e-01 1 0 1 396 0 0 4 0 1 90 8 92 1 390 8 392 1 74 8 96 1 396 8 +397 1 0 1.78559784508139030e+00 0 1 0 2.00628971357461605e-02 0 0 3 0 1 0 1.10803190963055798e-02 1 0 3.01708035625127824e-02 1 0 2.00628971357461605e-02 1 0 3.01708035625127824e-02 1 3 0 +398 0 0 381 0 0 +399 2 2 0.00000000000000000e+00 6.25000000000000000e-01 +400 2 2 1.25000000000000000e-01 7.50000000000000000e-01 4 0 1 389 1 390 1 384 1 +401 2 2 1.25000000000000000e-01 7.50000000000000000e-01 1 0 1 401 0 0 4 0 1 389 8 391 1 384 8 +402 1 0 1.56250000000000000e+00 0 1 0 6.25000000000000000e-02 1 390 8 392 1 401 8 +403 1 0 1.77054445791691606e+00 0 1 0 1.38503988703819747e-02 0 0 3 0 1 0 1.56250000000000000e-02 1 0 3.01708035625127824e-02 1 0 1.38503988703819747e-02 1 0 3.01708035625127824e-02 1 3 0 +404 0 0 381 0 0 +405 2 2 1.25000000000000000e-01 6.25000000000000000e-01 +406 2 2 2.50000000000000000e-01 7.50000000000000000e-01 4 0 1 390 1 396 1 401 1 385 0 0 0 0 4 0 1 390 8 392 1 401 8 403 1 396 8 397 1 385 8 +407 1 0 2.00628971357459607e+00 0 1 0 1.45796318869258368e-01 0 0 3 0 1 0 3.01708035625127824e-02 1 0 1.38503988703819747e-02 1 0 2.00628971357461605e-02 1 0 3.01708035625127824e-02 1 3 0 0 0 0 2 0 1 0 6.25000000000000000e-02 1 0 1.45796318869258368e-01 1 0 1.45796318869258368e-01 1 2 0 +408 1 0 377 0 0 +409 2 2 2.50000000000000000e-01 5.00000000000000000e-01 +410 2 2 5.00000000000000000e-01 7.50000000000000000e-01 0 0 1 0 1 +411 2 2 5.00000000000000000e-01 7.50000000000000000e-01 4 0 0 +412 0 0 408 0 0 +413 2 2 2.50000000000000000e-01 5.00000000000000000e-01 +414 2 2 3.75000000000000000e-01 6.25000000000000000e-01 4 0 1 74 1 113 1 396 1 +415 2 2 3.75000000000000000e-01 6.25000000000000000e-01 2 0 1 113 1 415 0 0 4 0 1 74 8 96 1 396 8 397 1 113 8 114 1 415 8 +416 1 0 2.02334743595343625e+00 0 1 0 3.87400786161595789e-02 0 0 2 0 1 0 1.42274113446672068e-02 1 0 3.87400786161595789e-02 1 0 3.87400786161595789e-02 1 3 0 +417 0 0 408 0 0 +418 2 2 3.75000000000000000e-01 5.00000000000000000e-01 +419 2 2 5.00000000000000000e-01 6.25000000000000000e-01 4 0 1 113 1 12 1 415 1 +420 2 2 5.00000000000000000e-01 6.25000000000000000e-01 0 0 0 0 4 0 1 113 8 114 1 415 8 416 1 12 8 118 1 420 8 +421 1 0 2.29275301706736556e+00 0 1 0 2.57612698546898500e-02 0 0 2 0 1 0 1.42274113446672068e-02 1 0 3.87400786161595789e-02 1 0 3.87400786161595789e-02 1 3 0 +422 0 0 408 0 0 +423 2 2 2.50000000000000000e-01 6.25000000000000000e-01 +424 2 2 3.75000000000000000e-01 7.50000000000000000e-01 4 0 1 396 1 415 1 385 1 +425 2 2 3.75000000000000000e-01 7.50000000000000000e-01 1 0 1 425 0 0 4 0 1 396 8 397 1 385 8 407 1 415 8 416 1 425 8 +426 1 0 2.27342408534093954e+00 0 1 0 1.77842641808338975e-02 0 0 2 0 1 0 3.87400786161595789e-02 1 0 1.77842641808338975e-02 1 0 3.87400786161595789e-02 1 3 0 +427 0 0 408 0 0 +428 2 2 3.75000000000000000e-01 6.25000000000000000e-01 +429 2 2 5.00000000000000000e-01 7.50000000000000000e-01 4 0 1 415 1 420 1 425 1 411 0 0 0 0 4 0 1 415 8 416 1 425 8 426 1 420 8 421 1 411 8 +430 1 0 2.57612698546895036e+00 0 1 0 1.03045079418758068e-01 0 0 2 0 1 0 3.87400786161595789e-02 1 0 1.77842641808338975e-02 1 0 3.87400786161595789e-02 1 3 0 0 0 0 2 0 1 0 1.45796318869258368e-01 1 0 1.03045079418758068e-01 1 0 1.45796318869258368e-01 1 2 0 +431 1 0 377 0 0 +432 2 2 0.00000000000000000e+00 7.50000000000000000e-01 +433 2 2 2.50000000000000000e-01 1.00000000000000000e+00 0 0 1 0 1 +434 2 2 2.50000000000000000e-01 1.00000000000000000e+00 4 0 0 +435 0 0 431 0 0 +436 2 2 0.00000000000000000e+00 7.50000000000000000e-01 +437 2 2 1.25000000000000000e-01 8.75000000000000000e-01 4 0 1 384 1 401 1 +438 2 2 0.00000000000000000e+00 8.75000000000000000e-01 1 +439 2 2 1.25000000000000000e-01 8.75000000000000000e-01 2 0 1 438 1 439 0 0 4 0 1 384 8 402 1 438 8 +440 1 0 1.76562500000000000e+00 0 1 0 1.56250000000000000e-02 1 401 8 403 1 439 8 +441 1 0 2.00071523744611524e+00 0 1 0 3.34948992914045895e-02 0 0 2 0 1 0 1.56250000000000000e-02 1 0 3.34948992914045895e-02 1 0 3.34948992914045895e-02 1 3 0 +442 0 0 431 0 0 +443 2 2 1.25000000000000000e-01 7.50000000000000000e-01 +444 2 2 2.50000000000000000e-01 8.75000000000000000e-01 4 0 1 401 1 385 1 439 1 +445 2 2 2.50000000000000000e-01 8.75000000000000000e-01 1 0 1 445 0 0 4 0 1 401 8 403 1 439 8 441 1 385 8 407 1 445 8 +446 1 0 2.26710737633929327e+00 0 1 0 2.00628971357463826e-02 0 0 2 0 1 0 3.34948992914045895e-02 1 0 2.00628971357463826e-02 1 0 3.34948992914045895e-02 1 3 0 +447 0 0 431 0 0 +448 2 2 0.00000000000000000e+00 8.75000000000000000e-01 +449 2 2 1.25000000000000000e-01 1.00000000000000000e+00 4 0 1 438 1 439 1 4 1 +450 2 2 1.25000000000000000e-01 1.00000000000000000e+00 1 0 1 450 0 0 4 0 1 438 8 440 1 4 8 +451 1 0 2.00000000000000000e+00 0 0 0 1 439 8 441 1 450 8 +452 1 0 2.26629690613365264e+00 0 1 0 1.77285105540887500e-02 0 0 3 0 1 0 1.56250000000000000e-02 1 0 3.34948992914045895e-02 1 0 1.77285105540887500e-02 1 0 3.34948992914045895e-02 1 3 0 +453 0 0 431 0 0 +454 2 2 1.25000000000000000e-01 8.75000000000000000e-01 +455 2 2 2.50000000000000000e-01 1.00000000000000000e+00 4 0 1 439 1 445 1 450 1 434 0 0 0 0 4 0 1 439 8 441 1 450 8 452 1 445 8 446 1 434 8 +456 1 0 2.56805083337548279e+00 0 1 0 8.06704373246454054e-02 0 0 3 0 1 0 3.34948992914045895e-02 1 0 1.77285105540887500e-02 1 0 2.00628971357463826e-02 1 0 3.34948992914045895e-02 1 3 0 0 0 0 3 0 1 0 6.25000000000000000e-02 1 0 1.45796318869258368e-01 1 0 8.06704373246454054e-02 1 0 1.45796318869258368e-01 1 2 0 +457 1 0 377 0 0 +458 2 2 2.50000000000000000e-01 7.50000000000000000e-01 +459 2 2 5.00000000000000000e-01 1.00000000000000000e+00 0 0 0 0 4 0 0 +460 0 0 457 0 0 +461 2 2 2.50000000000000000e-01 7.50000000000000000e-01 +462 2 2 3.75000000000000000e-01 8.75000000000000000e-01 4 0 1 385 1 425 1 445 1 +463 2 2 3.75000000000000000e-01 8.75000000000000000e-01 1 0 1 463 0 0 4 0 1 385 8 407 1 445 8 446 1 425 8 426 1 463 8 +464 1 0 2.56896921643526177e+00 0 1 0 4.30083020195599630e-02 0 0 1 0 1 0 4.30083020195599630e-02 1 0 4.30083020195599630e-02 1 3 0 +465 0 0 457 0 0 +466 2 2 3.75000000000000000e-01 7.50000000000000000e-01 +467 2 2 5.00000000000000000e-01 8.75000000000000000e-01 4 0 1 425 1 411 1 463 1 +468 2 2 5.00000000000000000e-01 8.75000000000000000e-01 0 0 0 0 4 0 1 425 8 426 1 463 8 464 1 411 8 430 1 468 8 +469 1 0 2.91102349357991397e+00 0 1 0 2.57612698546894059e-02 0 0 1 0 1 0 4.30083020195599630e-02 1 0 4.30083020195599630e-02 1 3 0 +470 0 0 457 0 0 +471 2 2 2.50000000000000000e-01 8.75000000000000000e-01 +472 2 2 3.75000000000000000e-01 1.00000000000000000e+00 4 0 1 445 1 463 1 434 1 +473 2 2 3.75000000000000000e-01 1.00000000000000000e+00 1 0 1 473 0 0 4 0 1 445 8 446 1 434 8 456 1 463 8 464 1 473 8 +474 1 0 2.90998282923640250e+00 0 1 0 2.27638581514670868e-02 0 0 2 0 1 0 4.30083020195599630e-02 1 0 2.27638581514670868e-02 1 0 4.30083020195599630e-02 1 3 0 +475 0 0 457 0 0 +476 2 2 3.75000000000000000e-01 8.75000000000000000e-01 +477 2 2 5.00000000000000000e-01 1.00000000000000000e+00 4 0 1 463 1 468 1 473 1 380 0 0 0 0 4 0 1 463 8 464 1 473 8 474 1 468 8 469 1 380 8 +478 1 0 3.29744254140025639e+00 0 1 0 4.20839287058788702e-01 0 0 2 0 1 0 4.30083020195599630e-02 1 0 2.27638581514670868e-02 1 0 4.30083020195599630e-02 1 3 0 0 0 0 3 0 1 0 1.45796318869258368e-01 1 0 8.06704373246454054e-02 1 0 1.03045079418758068e-01 1 0 1.45796318869258368e-01 1 2 0 0 0 0 3 0 1 0 2.50000000000000000e-01 1 0 7.27809782969123020e-01 1 0 4.20839287058788702e-01 1 0 7.27809782969123020e-01 1 1 0 +479 1 0 0 0 0 +480 2 2 5.00000000000000000e-01 5.00000000000000000e-01 +481 2 2 1.00000000000000000e+00 1.00000000000000000e+00 0 0 0 0 4 0 0 +482 1 0 479 0 0 +483 2 2 5.00000000000000000e-01 5.00000000000000000e-01 +484 2 2 7.50000000000000000e-01 7.50000000000000000e-01 0 0 1 0 1 +485 2 2 7.50000000000000000e-01 7.50000000000000000e-01 4 0 0 +486 0 0 482 0 0 +487 2 2 5.00000000000000000e-01 5.00000000000000000e-01 +488 2 2 6.25000000000000000e-01 6.25000000000000000e-01 4 0 1 12 1 273 1 420 1 +489 2 2 6.25000000000000000e-01 6.25000000000000000e-01 3 0 1 420 1 273 1 489 0 0 4 0 1 12 8 118 1 420 8 421 1 273 8 274 1 489 8 +490 1 0 2.59802953455418439e+00 0 1 0 4.97432455876305468e-02 0 0 3 0 1 0 2.57612698546898500e-02 1 0 1.82683577802240649e-02 1 0 4.97432455876305468e-02 1 0 4.97432455876305468e-02 1 3 0 +491 0 0 482 0 0 +492 2 2 6.25000000000000000e-01 5.00000000000000000e-01 +493 2 2 7.50000000000000000e-01 6.25000000000000000e-01 4 0 1 273 1 259 1 489 1 +494 2 2 7.50000000000000000e-01 6.25000000000000000e-01 1 0 1 494 0 0 4 0 1 273 8 274 1 489 8 490 1 259 8 278 1 494 8 +495 1 0 2.94395314810200093e+00 0 1 0 3.30781252595730990e-02 0 0 3 0 1 0 1.82683577802240649e-02 1 0 4.97432455876305468e-02 1 0 3.30781252595730990e-02 1 0 4.97432455876305468e-02 1 3 0 +496 0 0 482 0 0 +497 2 2 5.00000000000000000e-01 6.25000000000000000e-01 +498 2 2 6.25000000000000000e-01 7.50000000000000000e-01 4 0 1 420 1 489 1 411 1 +499 2 2 6.25000000000000000e-01 7.50000000000000000e-01 1 0 1 499 0 0 4 0 1 420 8 421 1 411 8 430 1 489 8 490 1 499 8 +500 1 0 2.91913430848784738e+00 0 1 0 2.28354472252800811e-02 0 0 3 0 1 0 2.57612698546898500e-02 1 0 4.97432455876305468e-02 1 0 2.28354472252800811e-02 1 0 4.97432455876305468e-02 1 3 0 +501 0 0 482 0 0 +502 2 2 6.25000000000000000e-01 6.25000000000000000e-01 +503 2 2 7.50000000000000000e-01 7.50000000000000000e-01 4 0 1 489 1 494 1 499 1 485 0 0 0 0 4 0 1 489 8 490 1 499 8 500 1 494 8 495 1 485 8 +504 1 0 3.30781252595730457e+00 0 1 0 2.40377492109523416e-01 0 0 3 0 1 0 4.97432455876305468e-02 1 0 2.28354472252800811e-02 1 0 3.30781252595730990e-02 1 0 4.97432455876305468e-02 1 3 0 0 0 0 1 0 1 0 2.40377492109523416e-01 1 0 2.40377492109523416e-01 1 2 0 +505 1 0 479 0 0 +506 2 2 7.50000000000000000e-01 5.00000000000000000e-01 +507 2 2 1.00000000000000000e+00 7.50000000000000000e-01 0 0 1 0 1 +508 2 2 1.00000000000000000e+00 7.50000000000000000e-01 4 0 0 +509 1 0 505 0 0 +510 2 2 7.50000000000000000e-01 5.00000000000000000e-01 +511 2 2 8.75000000000000000e-01 6.25000000000000000e-01 0 0 2 0 1 353 1 +512 2 2 8.75000000000000000e-01 6.25000000000000000e-01 4 0 0 +513 0 0 509 0 0 +514 2 2 7.50000000000000000e-01 5.00000000000000000e-01 +515 2 2 8.12500000000000000e-01 5.62500000000000000e-01 4 0 1 259 1 348 1 +516 2 2 7.50000000000000000e-01 5.62500000000000000e-01 1 +517 2 2 8.12500000000000000e-01 5.62500000000000000e-01 3 0 1 516 1 348 1 517 0 0 4 0 1 259 8 278 1 516 8 +518 1 0 2.78683205311902871e+00 0 1 0 8.26953131489371884e-03 1 348 8 349 1 517 8 +519 1 0 2.96656727847988799e+00 0 1 0 1.46160320200401372e-02 0 0 3 0 1 0 8.26953131489371884e-03 1 0 5.50358509584736311e-03 1 0 1.46160320200401372e-02 1 0 1.46160320200401372e-02 1 4 0 +520 0 0 509 0 0 +521 2 2 8.12500000000000000e-01 5.00000000000000000e-01 +522 2 2 8.75000000000000000e-01 5.62500000000000000e-01 4 0 1 348 1 353 1 517 1 +523 2 2 8.75000000000000000e-01 5.62500000000000000e-01 1 0 1 523 0 0 4 0 1 348 8 349 1 517 8 519 1 353 8 354 1 523 8 +524 1 0 3.15789442994887537e+00 0 1 0 9.37060661705890752e-03 0 0 3 0 1 0 5.50358509584736311e-03 1 0 1.46160320200401372e-02 1 0 9.37060661705890752e-03 1 0 1.46160320200401372e-02 1 4 0 +525 0 0 509 0 0 +526 2 2 7.50000000000000000e-01 5.62500000000000000e-01 +527 2 2 8.12500000000000000e-01 6.25000000000000000e-01 4 0 1 516 1 517 1 494 1 +528 2 2 8.12500000000000000e-01 6.25000000000000000e-01 1 0 1 528 0 0 4 0 1 516 8 518 1 494 8 495 1 517 8 519 1 528 8 +529 1 0 3.13382181346836797e+00 0 1 0 6.12273841913024697e-03 0 0 3 0 1 0 8.26953131489371884e-03 1 0 1.46160320200401372e-02 1 0 6.12273841913024697e-03 1 0 1.46160320200401372e-02 1 4 0 +530 0 0 509 0 0 +531 2 2 8.12500000000000000e-01 5.62500000000000000e-01 +532 2 2 8.75000000000000000e-01 6.25000000000000000e-01 4 0 1 517 1 523 1 528 1 512 0 0 0 0 4 0 1 517 8 519 1 528 8 529 1 523 8 524 1 512 8 +533 1 0 3.33593595567299595e+00 0 1 0 6.38715916430570019e-02 0 0 3 0 1 0 1.46160320200401372e-02 1 0 6.12273841913024697e-03 1 0 9.37060661705890752e-03 1 0 1.46160320200401372e-02 1 4 0 0 0 0 2 0 1 0 2.34570357109520344e-02 1 0 6.38715916430570019e-02 1 0 6.38715916430570019e-02 1 3 0 +534 1 0 505 0 0 +535 2 2 8.75000000000000000e-01 5.00000000000000000e-01 +536 2 2 1.00000000000000000e+00 6.25000000000000000e-01 0 0 1 0 1 +537 2 2 1.00000000000000000e+00 6.25000000000000000e-01 4 0 0 +538 0 0 534 0 0 +539 2 2 8.75000000000000000e-01 5.00000000000000000e-01 +540 2 2 9.37500000000000000e-01 5.62500000000000000e-01 4 0 1 353 1 371 1 523 1 +541 2 2 9.37500000000000000e-01 5.62500000000000000e-01 2 0 1 371 1 541 0 0 4 0 1 353 8 354 1 523 8 524 1 371 8 372 1 541 8 +542 1 0 3.36156112252814987e+00 0 1 0 1.65621340734838363e-02 0 0 2 0 1 0 6.23637893768069773e-03 1 0 1.65621340734838363e-02 1 0 1.65621340734838363e-02 1 4 0 +543 0 0 534 0 0 +544 2 2 9.37500000000000000e-01 5.00000000000000000e-01 +545 2 2 1.00000000000000000e+00 5.62500000000000000e-01 4 0 1 371 1 122 1 541 1 +546 2 2 1.00000000000000000e+00 5.62500000000000000e-01 1 0 1 546 0 0 4 0 1 371 8 372 1 541 8 542 1 122 8 376 1 546 8 +547 1 0 3.57836318824491473e+00 0 1 0 1.06182883924179627e-02 0 0 3 0 1 0 6.23637893768069773e-03 1 0 1.65621340734838363e-02 1 0 1.06182883924179627e-02 1 0 1.65621340734838363e-02 1 4 0 +548 0 0 534 0 0 +549 2 2 8.75000000000000000e-01 5.62500000000000000e-01 +550 2 2 9.37500000000000000e-01 6.25000000000000000e-01 4 0 1 523 1 541 1 512 1 +551 2 2 9.37500000000000000e-01 6.25000000000000000e-01 1 0 1 551 0 0 4 0 1 523 8 524 1 512 8 533 1 541 8 542 1 551 8 +552 1 0 3.55108534011875765e+00 0 1 0 6.93797156817010929e-03 0 0 2 0 1 0 1.65621340734838363e-02 1 0 6.93797156817010929e-03 1 0 1.65621340734838363e-02 1 4 0 +553 0 0 534 0 0 +554 2 2 9.37500000000000000e-01 5.62500000000000000e-01 +555 2 2 1.00000000000000000e+00 6.25000000000000000e-01 4 0 1 541 1 546 1 551 1 537 0 0 0 0 4 0 1 541 8 542 1 551 8 552 1 546 8 547 1 537 8 +556 1 0 3.78011066770085957e+00 0 1 0 4.24731535696727391e-02 0 0 3 0 1 0 1.65621340734838363e-02 1 0 6.93797156817010929e-03 1 0 1.06182883924179627e-02 1 0 1.65621340734838363e-02 1 4 0 0 0 0 3 0 1 0 2.34570357109520344e-02 1 0 6.38715916430570019e-02 1 0 4.24731535696727391e-02 1 0 6.38715916430570019e-02 1 3 0 +557 1 0 505 0 0 +558 2 2 7.50000000000000000e-01 6.25000000000000000e-01 +559 2 2 8.75000000000000000e-01 7.50000000000000000e-01 0 0 1 0 1 +560 2 2 8.75000000000000000e-01 7.50000000000000000e-01 4 0 0 +561 0 0 557 0 0 +562 2 2 7.50000000000000000e-01 6.25000000000000000e-01 +563 2 2 8.12500000000000000e-01 6.87500000000000000e-01 4 0 1 494 1 528 1 +564 2 2 7.50000000000000000e-01 6.87500000000000000e-01 1 +565 2 2 8.12500000000000000e-01 6.87500000000000000e-01 2 0 1 564 1 565 0 0 4 0 1 494 8 495 1 564 8 +566 1 0 3.11761330571475925e+00 0 1 0 8.26953131489327475e-03 1 528 8 529 1 565 8 +567 1 0 3.31868208898195149e+00 0 1 0 1.53039801570216127e-02 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.53039801570216127e-02 1 0 1.53039801570216127e-02 1 4 0 +568 0 0 557 0 0 +569 2 2 8.12500000000000000e-01 6.25000000000000000e-01 +570 2 2 8.75000000000000000e-01 6.87500000000000000e-01 4 0 1 528 1 512 1 565 1 +571 2 2 8.75000000000000000e-01 6.87500000000000000e-01 1 0 1 571 0 0 4 0 1 528 8 529 1 565 8 567 1 512 8 533 1 571 8 +572 1 0 3.53271869463123434e+00 0 1 0 9.37060661705890752e-03 0 0 2 0 1 0 1.53039801570216127e-02 1 0 9.37060661705890752e-03 1 0 1.53039801570216127e-02 1 4 0 +573 0 0 557 0 0 +574 2 2 7.50000000000000000e-01 6.87500000000000000e-01 +575 2 2 8.12500000000000000e-01 7.50000000000000000e-01 4 0 1 564 1 565 1 485 1 +576 2 2 8.12500000000000000e-01 7.50000000000000000e-01 0 0 0 0 4 0 1 564 8 566 1 485 8 504 1 565 8 567 1 576 8 +577 1 0 3.52114810502063857e+00 0 1 0 6.87948136980898184e-03 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.53039801570216127e-02 1 0 1.53039801570216127e-02 1 4 0 +578 0 0 557 0 0 +579 2 2 8.12500000000000000e-01 6.87500000000000000e-01 +580 2 2 8.75000000000000000e-01 7.50000000000000000e-01 4 0 1 565 1 571 1 576 1 560 0 0 0 0 4 0 1 565 8 567 1 576 8 577 1 571 8 572 1 560 8 +581 1 0 3.74824264682359054e+00 0 1 0 2.93212946386907092e-02 0 0 2 0 1 0 1.53039801570216127e-02 1 0 9.37060661705890752e-03 1 0 1.53039801570216127e-02 1 4 0 0 0 0 2 0 1 0 6.38715916430570019e-02 1 0 2.93212946386907092e-02 1 0 6.38715916430570019e-02 1 3 0 +582 1 0 505 0 0 +583 2 2 8.75000000000000000e-01 6.25000000000000000e-01 +584 2 2 1.00000000000000000e+00 7.50000000000000000e-01 0 0 0 0 4 0 0 +585 0 0 582 0 0 +586 2 2 8.75000000000000000e-01 6.25000000000000000e-01 +587 2 2 9.37500000000000000e-01 6.87500000000000000e-01 4 0 1 512 1 551 1 571 1 +588 2 2 9.37500000000000000e-01 6.87500000000000000e-01 1 0 1 588 0 0 4 0 1 512 8 533 1 571 8 572 1 551 8 552 1 588 8 +589 1 0 3.76055947535048185e+00 0 1 0 1.73416814406941455e-02 0 0 1 0 1 0 1.73416814406941455e-02 1 0 1.73416814406941455e-02 1 4 0 +590 0 0 582 0 0 +591 2 2 9.37500000000000000e-01 6.25000000000000000e-01 +592 2 2 1.00000000000000000e+00 6.87500000000000000e-01 4 0 1 551 1 537 1 588 1 +593 2 2 1.00000000000000000e+00 6.87500000000000000e-01 1 0 1 593 0 0 4 0 1 551 8 552 1 588 8 589 1 537 8 556 1 593 8 +594 1 0 4.00309472394164079e+00 0 1 0 1.06182883924175187e-02 0 0 2 0 1 0 1.73416814406941455e-02 1 0 1.06182883924175187e-02 1 0 1.73416814406941455e-02 1 4 0 +595 0 0 582 0 0 +596 2 2 8.75000000000000000e-01 6.87500000000000000e-01 +597 2 2 9.37500000000000000e-01 7.50000000000000000e-01 4 0 1 571 1 588 1 560 1 +598 2 2 9.37500000000000000e-01 7.50000000000000000e-01 0 0 0 0 4 0 1 571 8 572 1 560 8 581 1 588 8 589 1 598 8 +599 1 0 3.98998352822332336e+00 0 1 0 7.79547367210087216e-03 0 0 1 0 1 0 1.73416814406941455e-02 1 0 1.73416814406941455e-02 1 4 0 +600 0 0 582 0 0 +601 2 2 9.37500000000000000e-01 6.87500000000000000e-01 +602 2 2 1.00000000000000000e+00 7.50000000000000000e-01 4 0 1 588 1 593 1 598 1 508 0 0 0 0 4 0 1 588 8 589 1 598 8 599 1 593 8 594 1 508 8 +603 1 0 4.24731535696725793e+00 0 1 0 1.69892614278690068e-01 0 0 2 0 1 0 1.73416814406941455e-02 1 0 1.06182883924175187e-02 1 0 1.73416814406941455e-02 1 4 0 0 0 0 3 0 1 0 6.38715916430570019e-02 1 0 2.93212946386907092e-02 1 0 4.24731535696727391e-02 1 0 6.38715916430570019e-02 1 3 0 0 0 0 2 0 1 0 2.40377492109523416e-01 1 0 1.69892614278690068e-01 1 0 2.40377492109523416e-01 1 2 0 +604 1 0 479 0 0 +605 2 2 5.00000000000000000e-01 7.50000000000000000e-01 +606 2 2 7.50000000000000000e-01 1.00000000000000000e+00 0 0 1 0 1 +607 2 2 7.50000000000000000e-01 1.00000000000000000e+00 4 0 0 +608 1 0 604 0 0 +609 2 2 5.00000000000000000e-01 7.50000000000000000e-01 +610 2 2 6.25000000000000000e-01 8.75000000000000000e-01 0 0 2 0 1 468 1 +611 2 2 6.25000000000000000e-01 8.75000000000000000e-01 4 0 0 +612 0 0 608 0 0 +613 2 2 5.00000000000000000e-01 7.50000000000000000e-01 +614 2 2 5.62500000000000000e-01 8.12500000000000000e-01 4 0 1 411 1 +615 2 2 5.62500000000000000e-01 7.50000000000000000e-01 1 +616 2 2 5.00000000000000000e-01 8.12500000000000000e-01 1 +617 2 2 5.62500000000000000e-01 8.12500000000000000e-01 3 0 1 616 1 615 1 617 0 0 4 0 1 411 8 430 1 616 8 +618 1 0 2.73713492206075948e+00 0 1 0 6.44031746367268454e-03 1 615 8 +619 1 0 2.74227290150046654e+00 0 1 0 5.35774547793232969e-03 1 617 8 +620 1 0 2.91366495784424551e+00 0 1 0 1.25616811877495849e-02 0 0 3 0 1 0 6.44031746367268454e-03 1 0 5.35774547793232969e-03 1 0 1.25616811877495849e-02 1 0 1.25616811877495849e-02 1 4 0 +621 0 0 608 0 0 +622 2 2 5.62500000000000000e-01 7.50000000000000000e-01 +623 2 2 6.25000000000000000e-01 8.12500000000000000e-01 4 0 1 615 1 499 1 617 1 +624 2 2 6.25000000000000000e-01 8.12500000000000000e-01 1 0 1 624 0 0 4 0 1 615 8 619 1 617 8 620 1 499 8 500 1 624 8 +625 1 0 3.10158020276833790e+00 0 1 0 7.29783577121967397e-03 0 0 3 0 1 0 5.35774547793232969e-03 1 0 1.25616811877495849e-02 1 0 7.29783577121967397e-03 1 0 1.25616811877495849e-02 1 4 0 +626 0 0 608 0 0 +627 2 2 5.00000000000000000e-01 8.12500000000000000e-01 +628 2 2 5.62500000000000000e-01 8.75000000000000000e-01 4 0 1 616 1 617 1 468 1 +629 2 2 5.62500000000000000e-01 8.75000000000000000e-01 1 0 1 629 0 0 4 0 1 616 8 618 1 468 8 469 1 617 8 620 1 629 8 +630 1 0 3.09876837869552713e+00 0 1 0 6.05425239006374127e-03 0 0 3 0 1 0 6.44031746367268454e-03 1 0 1.25616811877495849e-02 1 0 6.05425239006374127e-03 1 0 1.25616811877495849e-02 1 4 0 +631 0 0 608 0 0 +632 2 2 5.62500000000000000e-01 8.12500000000000000e-01 +633 2 2 6.25000000000000000e-01 8.75000000000000000e-01 4 0 1 617 1 624 1 629 1 611 0 0 0 0 4 0 1 617 8 620 1 629 8 630 1 624 8 625 1 611 8 +634 1 0 3.29862176859126777e+00 0 1 0 5.52237529216972334e-02 0 0 3 0 1 0 1.25616811877495849e-02 1 0 6.05425239006374127e-03 1 0 7.29783577121967397e-03 1 0 1.25616811877495849e-02 1 4 0 0 0 0 2 0 1 0 2.57612698546894059e-02 1 0 5.52237529216972334e-02 1 0 5.52237529216972334e-02 1 3 0 +635 1 0 604 0 0 +636 2 2 6.25000000000000000e-01 7.50000000000000000e-01 +637 2 2 7.50000000000000000e-01 8.75000000000000000e-01 0 0 1 0 1 +638 2 2 7.50000000000000000e-01 8.75000000000000000e-01 4 0 0 +639 0 0 635 0 0 +640 2 2 6.25000000000000000e-01 7.50000000000000000e-01 +641 2 2 6.87500000000000000e-01 8.12500000000000000e-01 4 0 1 499 1 +642 2 2 6.87500000000000000e-01 7.50000000000000000e-01 1 624 1 +643 2 2 6.87500000000000000e-01 8.12500000000000000e-01 2 0 1 642 1 643 0 0 4 0 1 499 8 500 1 624 8 625 1 642 8 +644 1 0 3.10740229622233111e+00 0 1 0 6.07112100024487233e-03 1 643 8 +645 1 0 3.30161493973622688e+00 0 1 0 1.42342496058165402e-02 0 0 2 0 1 0 6.07112100024487233e-03 1 0 1.42342496058165402e-02 1 0 1.42342496058165402e-02 1 4 0 +646 0 0 635 0 0 +647 2 2 6.87500000000000000e-01 7.50000000000000000e-01 +648 2 2 7.50000000000000000e-01 8.12500000000000000e-01 4 0 1 642 1 485 1 643 1 +649 2 2 7.50000000000000000e-01 8.12500000000000000e-01 0 0 0 0 4 0 1 642 8 644 1 643 8 645 1 485 8 504 1 649 8 +650 1 0 3.51455080882963600e+00 0 1 0 8.26953131489327475e-03 0 0 2 0 1 0 6.07112100024487233e-03 1 0 1.42342496058165402e-02 1 0 1.42342496058165402e-02 1 4 0 +651 0 0 635 0 0 +652 2 2 6.25000000000000000e-01 8.12500000000000000e-01 +653 2 2 6.87500000000000000e-01 8.75000000000000000e-01 4 0 1 624 1 643 1 611 1 +654 2 2 6.87500000000000000e-01 8.75000000000000000e-01 1 0 1 654 0 0 4 0 1 624 8 625 1 611 8 634 1 643 8 645 1 654 8 +655 1 0 3.51136459473123397e+00 0 1 0 6.86036673027690114e-03 0 0 2 0 1 0 1.42342496058165402e-02 1 0 6.86036673027690114e-03 1 0 1.42342496058165402e-02 1 4 0 +656 0 0 635 0 0 +657 2 2 6.87500000000000000e-01 8.12500000000000000e-01 +658 2 2 7.50000000000000000e-01 8.75000000000000000e-01 4 0 1 643 1 649 1 654 1 638 0 0 0 0 4 0 1 643 8 645 1 654 8 655 1 649 8 650 1 638 8 +659 1 0 3.73782815433175397e+00 0 1 0 3.30781252595730990e-02 0 0 2 0 1 0 1.42342496058165402e-02 1 0 6.86036673027690114e-03 1 0 1.42342496058165402e-02 1 4 0 0 0 0 2 0 1 0 5.52237529216972334e-02 1 0 3.30781252595730990e-02 1 0 5.52237529216972334e-02 1 3 0 +660 1 0 604 0 0 +661 2 2 5.00000000000000000e-01 8.75000000000000000e-01 +662 2 2 6.25000000000000000e-01 1.00000000000000000e+00 0 0 1 0 1 +663 2 2 6.25000000000000000e-01 1.00000000000000000e+00 4 0 0 +664 0 0 660 0 0 +665 2 2 5.00000000000000000e-01 8.75000000000000000e-01 +666 2 2 5.62500000000000000e-01 9.37500000000000000e-01 4 0 1 468 1 629 1 +667 2 2 5.00000000000000000e-01 9.37500000000000000e-01 1 +668 2 2 5.62500000000000000e-01 9.37500000000000000e-01 2 0 1 667 1 668 0 0 4 0 1 468 8 469 1 667 8 +669 1 0 3.09779270002641294e+00 0 1 0 6.44031746367224045e-03 1 629 8 630 1 668 8 +670 1 0 3.29758316405431096e+00 0 1 0 1.33117655546595159e-02 0 0 2 0 1 0 6.44031746367224045e-03 1 0 1.33117655546595159e-02 1 0 1.33117655546595159e-02 1 4 0 +671 0 0 660 0 0 +672 2 2 5.62500000000000000e-01 8.75000000000000000e-01 +673 2 2 6.25000000000000000e-01 9.37500000000000000e-01 4 0 1 629 1 611 1 668 1 +674 2 2 6.25000000000000000e-01 9.37500000000000000e-01 1 0 1 674 0 0 4 0 1 629 8 630 1 668 8 670 1 611 8 634 1 674 8 +675 1 0 3.51025900595663654e+00 0 1 0 7.29783577121967397e-03 0 0 2 0 1 0 1.33117655546595159e-02 1 0 7.29783577121967397e-03 1 0 1.33117655546595159e-02 1 4 0 +676 0 0 660 0 0 +677 2 2 5.00000000000000000e-01 9.37500000000000000e-01 +678 2 2 5.62500000000000000e-01 1.00000000000000000e+00 4 0 1 667 1 668 1 380 1 +679 2 2 5.62500000000000000e-01 1.00000000000000000e+00 1 0 1 679 0 0 4 0 1 667 8 669 1 380 8 478 1 668 8 670 1 679 8 +680 1 0 3.51010931392059700e+00 0 1 0 6.85791421175352411e-03 0 0 3 0 1 0 6.44031746367224045e-03 1 0 1.33117655546595159e-02 1 0 6.85791421175352411e-03 1 0 1.33117655546595159e-02 1 4 0 +681 0 0 660 0 0 +682 2 2 5.62500000000000000e-01 9.37500000000000000e-01 +683 2 2 6.25000000000000000e-01 1.00000000000000000e+00 4 0 1 668 1 674 1 679 1 663 0 0 0 0 4 0 1 668 8 670 1 679 8 680 1 674 8 675 1 663 8 +684 1 0 3.73649191486444465e+00 0 1 0 2.92293724483583262e-02 0 0 3 0 1 0 1.33117655546595159e-02 1 0 6.85791421175352411e-03 1 0 7.29783577121967397e-03 1 0 1.33117655546595159e-02 1 4 0 0 0 0 3 0 1 0 2.57612698546894059e-02 1 0 5.52237529216972334e-02 1 0 2.92293724483583262e-02 1 0 5.52237529216972334e-02 1 3 0 +685 1 0 604 0 0 +686 2 2 6.25000000000000000e-01 8.75000000000000000e-01 +687 2 2 7.50000000000000000e-01 1.00000000000000000e+00 0 0 0 0 4 0 0 +688 0 0 685 0 0 +689 2 2 6.25000000000000000e-01 8.75000000000000000e-01 +690 2 2 6.87500000000000000e-01 9.37500000000000000e-01 4 0 1 611 1 654 1 674 1 +691 2 2 6.87500000000000000e-01 9.37500000000000000e-01 1 0 1 691 0 0 4 0 1 611 8 634 1 674 8 675 1 654 8 655 1 691 8 +692 1 0 3.73665126120735325e+00 0 1 0 1.50842065458509644e-02 0 0 1 0 1 0 1.50842065458509644e-02 1 0 1.50842065458509644e-02 1 4 0 +693 0 0 685 0 0 +694 2 2 6.87500000000000000e-01 8.75000000000000000e-01 +695 2 2 7.50000000000000000e-01 9.37500000000000000e-01 4 0 1 654 1 638 1 691 1 +696 2 2 7.50000000000000000e-01 9.37500000000000000e-01 0 0 0 0 4 0 1 654 8 655 1 691 8 692 1 638 8 659 1 696 8 +697 1 0 3.97764456246365850e+00 0 1 0 8.26953131489327475e-03 0 0 1 0 1 0 1.50842065458509644e-02 1 0 1.50842065458509644e-02 1 4 0 +698 0 0 685 0 0 +699 2 2 6.25000000000000000e-01 9.37500000000000000e-01 +700 2 2 6.87500000000000000e-01 1.00000000000000000e+00 4 0 1 674 1 691 1 663 1 +701 2 2 6.87500000000000000e-01 1.00000000000000000e+00 1 0 1 701 0 0 4 0 1 674 8 675 1 663 8 684 1 691 8 692 1 701 8 +702 1 0 3.97747493916458383e+00 0 1 0 7.77103488031327672e-03 0 0 2 0 1 0 1.50842065458509644e-02 1 0 7.77103488031327672e-03 1 0 1.50842065458509644e-02 1 4 0 +703 0 0 685 0 0 +704 2 2 6.87500000000000000e-01 9.37500000000000000e-01 +705 2 2 7.50000000000000000e-01 1.00000000000000000e+00 4 0 1 691 1 696 1 701 1 607 0 0 0 0 4 0 1 691 8 692 1 701 8 702 1 696 8 697 1 607 8 +706 1 0 4.23400003322534957e+00 0 1 0 1.33003065933824161e-01 0 0 2 0 1 0 1.50842065458509644e-02 1 0 7.77103488031327672e-03 1 0 1.50842065458509644e-02 1 4 0 0 0 0 3 0 1 0 5.52237529216972334e-02 1 0 2.92293724483583262e-02 1 0 3.30781252595730990e-02 1 0 5.52237529216972334e-02 1 3 0 0 0 0 2 0 1 0 2.40377492109523416e-01 1 0 1.33003065933824161e-01 1 0 2.40377492109523416e-01 1 2 0 +707 1 0 479 0 0 +708 2 2 7.50000000000000000e-01 7.50000000000000000e-01 +709 2 2 1.00000000000000000e+00 1.00000000000000000e+00 0 0 0 0 4 0 0 +710 1 0 707 0 0 +711 2 2 7.50000000000000000e-01 7.50000000000000000e-01 +712 2 2 8.75000000000000000e-01 8.75000000000000000e-01 0 0 1 0 1 +713 2 2 8.75000000000000000e-01 8.75000000000000000e-01 4 0 0 +714 0 0 710 0 0 +715 2 2 7.50000000000000000e-01 7.50000000000000000e-01 +716 2 2 8.12500000000000000e-01 8.12500000000000000e-01 4 0 1 485 1 576 1 649 1 +717 2 2 8.12500000000000000e-01 8.12500000000000000e-01 3 0 1 649 1 576 1 717 0 0 4 0 1 485 8 504 1 649 8 650 1 576 8 577 1 717 8 +718 1 0 3.74121986158442832e+00 0 1 0 1.61295179213980511e-02 0 0 3 0 1 0 8.26953131489327475e-03 1 0 6.87948136980898184e-03 1 0 1.61295179213980511e-02 1 0 1.61295179213980511e-02 1 4 0 +719 0 0 710 0 0 +720 2 2 8.12500000000000000e-01 7.50000000000000000e-01 +721 2 2 8.75000000000000000e-01 8.12500000000000000e-01 4 0 1 576 1 560 1 717 1 +722 2 2 8.75000000000000000e-01 8.12500000000000000e-01 1 0 1 722 0 0 4 0 1 576 8 577 1 717 8 718 1 560 8 581 1 722 8 +723 1 0 3.98250781225006500e+00 0 1 0 9.37060661705890752e-03 0 0 3 0 1 0 6.87948136980898184e-03 1 0 1.61295179213980511e-02 1 0 9.37060661705890752e-03 1 0 1.61295179213980511e-02 1 4 0 +724 0 0 710 0 0 +725 2 2 7.50000000000000000e-01 8.12500000000000000e-01 +726 2 2 8.12500000000000000e-01 8.75000000000000000e-01 4 0 1 649 1 717 1 638 1 +727 2 2 8.12500000000000000e-01 8.75000000000000000e-01 1 0 1 727 0 0 4 0 1 649 8 650 1 638 8 659 1 717 8 718 1 727 8 +728 1 0 3.97889735867332117e+00 0 1 0 7.77381394788445590e-03 0 0 3 0 1 0 8.26953131489327475e-03 1 0 1.61295179213980511e-02 1 0 7.77381394788445590e-03 1 0 1.61295179213980511e-02 1 4 0 +729 0 0 710 0 0 +730 2 2 8.12500000000000000e-01 8.12500000000000000e-01 +731 2 2 8.75000000000000000e-01 8.75000000000000000e-01 4 0 1 717 1 722 1 727 1 713 0 0 0 0 4 0 1 717 8 718 1 727 8 728 1 722 8 723 1 713 8 +732 1 0 4.23551419091065728e+00 0 1 0 7.09087023563430563e-02 0 0 3 0 1 0 1.61295179213980511e-02 1 0 7.77381394788445590e-03 1 0 9.37060661705890752e-03 1 0 1.61295179213980511e-02 1 4 0 0 0 0 1 0 1 0 7.09087023563430563e-02 1 0 7.09087023563430563e-02 1 3 0 +733 1 0 707 0 0 +734 2 2 8.75000000000000000e-01 7.50000000000000000e-01 +735 2 2 1.00000000000000000e+00 8.75000000000000000e-01 0 0 1 0 1 +736 2 2 1.00000000000000000e+00 8.75000000000000000e-01 4 0 0 +737 0 0 733 0 0 +738 2 2 8.75000000000000000e-01 7.50000000000000000e-01 +739 2 2 9.37500000000000000e-01 8.12500000000000000e-01 4 0 1 560 1 598 1 722 1 +740 2 2 9.37500000000000000e-01 8.12500000000000000e-01 2 0 1 598 1 740 0 0 4 0 1 560 8 581 1 722 8 723 1 598 8 599 1 740 8 +741 1 0 4.23935749873728085e+00 0 1 0 1.82771382813458061e-02 0 0 2 0 1 0 7.79547367210087216e-03 1 0 1.82771382813458061e-02 1 0 1.82771382813458061e-02 1 4 0 +742 0 0 733 0 0 +743 2 2 9.37500000000000000e-01 7.50000000000000000e-01 +744 2 2 1.00000000000000000e+00 8.12500000000000000e-01 4 0 1 598 1 508 1 740 1 +745 2 2 1.00000000000000000e+00 8.12500000000000000e-01 1 0 1 745 0 0 4 0 1 598 8 599 1 740 8 741 1 508 8 603 1 745 8 +746 1 0 4.51277256677771188e+00 0 1 0 1.06182883924175187e-02 0 0 3 0 1 0 7.79547367210087216e-03 1 0 1.82771382813458061e-02 1 0 1.06182883924175187e-02 1 0 1.82771382813458061e-02 1 4 0 +747 0 0 733 0 0 +748 2 2 8.75000000000000000e-01 8.12500000000000000e-01 +749 2 2 9.37500000000000000e-01 8.75000000000000000e-01 4 0 1 722 1 740 1 713 1 +750 2 2 9.37500000000000000e-01 8.75000000000000000e-01 1 0 1 750 0 0 4 0 1 722 8 723 1 713 8 732 1 740 8 741 1 750 8 +751 1 0 4.50868138689235476e+00 0 1 0 8.80888524947476270e-03 0 0 2 0 1 0 1.82771382813458061e-02 1 0 8.80888524947476270e-03 1 0 1.82771382813458061e-02 1 4 0 +752 0 0 733 0 0 +753 2 2 9.37500000000000000e-01 8.12500000000000000e-01 +754 2 2 1.00000000000000000e+00 8.75000000000000000e-01 4 0 1 740 1 745 1 750 1 736 0 0 0 0 4 0 1 740 8 741 1 750 8 751 1 745 8 746 1 736 8 +755 1 0 4.79946635337300176e+00 0 1 0 4.24731535696727391e-02 0 0 3 0 1 0 1.82771382813458061e-02 1 0 8.80888524947476270e-03 1 0 1.06182883924175187e-02 1 0 1.82771382813458061e-02 1 4 0 0 0 0 2 0 1 0 7.09087023563430563e-02 1 0 4.24731535696727391e-02 1 0 7.09087023563430563e-02 1 3 0 +756 1 0 707 0 0 +757 2 2 7.50000000000000000e-01 8.75000000000000000e-01 +758 2 2 8.75000000000000000e-01 1.00000000000000000e+00 0 0 1 0 1 +759 2 2 8.75000000000000000e-01 1.00000000000000000e+00 4 0 0 +760 0 0 756 0 0 +761 2 2 7.50000000000000000e-01 8.75000000000000000e-01 +762 2 2 8.12500000000000000e-01 9.37500000000000000e-01 4 0 1 638 1 727 1 696 1 +763 2 2 8.12500000000000000e-01 9.37500000000000000e-01 2 0 1 696 1 763 0 0 4 0 1 638 8 659 1 696 8 697 1 727 8 728 1 763 8 +764 1 0 4.23418059628731758e+00 0 1 0 1.70926453131716727e-02 0 0 2 0 1 0 8.26953131489327475e-03 1 0 1.70926453131716727e-02 1 0 1.70926453131716727e-02 1 4 0 +765 0 0 756 0 0 +766 2 2 8.12500000000000000e-01 8.75000000000000000e-01 +767 2 2 8.75000000000000000e-01 9.37500000000000000e-01 4 0 1 727 1 713 1 763 1 +768 2 2 8.75000000000000000e-01 9.37500000000000000e-01 1 0 1 768 0 0 4 0 1 727 8 728 1 763 8 764 1 713 8 732 1 768 8 +769 1 0 4.50726178280536782e+00 0 1 0 9.37060661705846343e-03 0 0 2 0 1 0 1.70926453131716727e-02 1 0 9.37060661705846343e-03 1 0 1.70926453131716727e-02 1 4 0 +770 0 0 756 0 0 +771 2 2 7.50000000000000000e-01 9.37500000000000000e-01 +772 2 2 8.12500000000000000e-01 1.00000000000000000e+00 4 0 1 696 1 763 1 607 1 +773 2 2 8.12500000000000000e-01 1.00000000000000000e+00 1 0 1 773 0 0 4 0 1 696 8 697 1 607 8 706 1 763 8 764 1 773 8 +774 1 0 4.50706957442641709e+00 0 1 0 8.80573615335578097e-03 0 0 3 0 1 0 8.26953131489327475e-03 1 0 1.70926453131716727e-02 1 0 8.80573615335578097e-03 1 0 1.70926453131716727e-02 1 4 0 +775 0 0 756 0 0 +776 2 2 8.12500000000000000e-01 9.37500000000000000e-01 +777 2 2 8.75000000000000000e-01 1.00000000000000000e+00 4 0 1 763 1 768 1 773 1 759 0 0 0 0 4 0 1 763 8 764 1 773 8 774 1 768 8 769 1 759 8 +778 1 0 4.79775058793419618e+00 0 1 0 3.75312571375232551e-02 0 0 3 0 1 0 1.70926453131716727e-02 1 0 8.80573615335578097e-03 1 0 9.37060661705846343e-03 1 0 1.70926453131716727e-02 1 4 0 0 0 0 2 0 1 0 7.09087023563430563e-02 1 0 3.75312571375232551e-02 1 0 7.09087023563430563e-02 1 3 0 +779 1 0 707 0 0 +780 2 2 8.75000000000000000e-01 8.75000000000000000e-01 +781 2 2 1.00000000000000000e+00 1.00000000000000000e+00 0 0 0 0 4 0 0 +782 0 0 779 0 0 +783 2 2 8.75000000000000000e-01 8.75000000000000000e-01 +784 2 2 9.37500000000000000e-01 9.37500000000000000e-01 4 0 1 713 1 750 1 768 1 +785 2 2 9.37500000000000000e-01 9.37500000000000000e-01 1 0 1 785 0 0 4 0 1 713 8 732 1 768 8 769 1 750 8 751 1 785 8 +786 1 0 4.79795519268854598e+00 0 1 0 1.93685045954401502e-02 0 0 1 0 1 0 1.93685045954401502e-02 1 0 1.93685045954401502e-02 1 4 0 +787 0 0 779 0 0 +788 2 2 9.37500000000000000e-01 8.75000000000000000e-01 +789 2 2 1.00000000000000000e+00 9.37500000000000000e-01 4 0 1 750 1 736 1 785 1 +790 2 2 1.00000000000000000e+00 9.37500000000000000e-01 1 0 1 790 0 0 4 0 1 750 8 751 1 785 8 786 1 736 8 755 1 790 8 +791 1 0 5.10739671675312756e+00 0 1 0 1.06182883924184068e-02 0 0 2 0 1 0 1.93685045954401502e-02 1 0 1.06182883924184068e-02 1 0 1.93685045954401502e-02 1 4 0 +792 0 0 779 0 0 +793 2 2 8.75000000000000000e-01 9.37500000000000000e-01 +794 2 2 9.37500000000000000e-01 1.00000000000000000e+00 4 0 1 768 1 785 1 759 1 +795 2 2 9.37500000000000000e-01 1.00000000000000000e+00 1 0 1 795 0 0 4 0 1 768 8 769 1 759 8 778 1 785 8 786 1 795 8 +796 1 0 5.10717891612585362e+00 0 1 0 9.97820630028911637e-03 0 0 2 0 1 0 1.93685045954401502e-02 1 0 9.97820630028911637e-03 1 0 1.93685045954401502e-02 1 4 0 +797 0 0 779 0 0 +798 2 2 9.37500000000000000e-01 9.37500000000000000e-01 +799 2 2 1.00000000000000000e+00 1.00000000000000000e+00 4 0 1 785 1 790 1 795 1 6 0 0 0 0 4 0 1 785 8 786 1 795 8 796 1 790 8 791 1 6 8 +800 1 0 5.43656365691809018e+00 0 0 0 0 0 3 0 1 0 1.93685045954401502e-02 1 0 9.97820630028911637e-03 1 0 1.06182883924184068e-02 1 0 1.93685045954401502e-02 1 4 0 0 0 0 3 0 1 0 7.09087023563430563e-02 1 0 3.75312571375232551e-02 1 0 4.24731535696727391e-02 1 0 7.09087023563430563e-02 1 3 0 0 0 0 3 0 1 0 2.40377492109523416e-01 1 0 1.33003065933824161e-01 1 0 1.69892614278690068e-01 1 0 2.40377492109523416e-01 1 2 0 0 0 0 3 0 1 0 7.27809782969123020e-01 1 0 4.20839287058788702e-01 1 0 6.79570457114761606e-01 1 0 7.27809782969123020e-01 1 1 153 0 1 3 8 25 1 23 8 26 1 16 8 39 1 78 8 80 1 10 8 91 1 389 8 391 1 384 8 402 1 438 8 440 1 4 8 451 1 22 8 27 1 24 8 28 1 38 8 40 1 79 8 81 1 90 8 92 1 390 8 392 1 401 8 403 1 439 8 441 1 450 8 452 1 17 8 33 1 32 8 34 1 18 8 44 1 85 8 86 1 74 8 96 1 396 8 397 1 385 8 407 1 445 8 446 1 434 8 456 1 52 8 54 1 53 8 55 1 65 8 66 1 103 8 104 1 113 8 114 1 415 8 416 1 425 8 426 1 463 8 464 1 473 8 474 1 11 8 60 1 59 8 61 1 48 8 70 1 108 8 109 1 12 8 118 1 420 8 421 1 411 8 430 1 616 8 618 1 468 8 469 1 667 8 669 1 380 8 478 1 615 8 619 1 617 8 620 1 629 8 630 1 668 8 670 1 679 8 680 1 131 8 133 1 132 8 134 1 144 8 145 1 263 8 264 1 273 8 274 1 489 8 490 1 499 8 500 1 624 8 625 1 611 8 634 1 674 8 675 1 663 8 684 1 642 8 644 1 643 8 645 1 654 8 655 1 691 8 692 1 701 8 702 1 126 8 139 1 163 8 165 1 138 8 140 1 216 8 218 1 127 8 149 1 289 8 291 1 268 8 269 1 336 8 338 1 259 8 278 1 516 8 518 1 494 8 495 1 564 8 566 1 485 8 504 1 649 8 650 1 638 8 659 1 696 8 697 1 607 8 706 1 162 8 166 1 164 8 167 1 177 8 178 1 217 8 219 1 228 8 229 1 290 8 292 1 301 8 302 1 337 8 339 1 348 8 349 1 517 8 519 1 528 8 529 1 565 8 567 1 576 8 577 1 717 8 718 1 727 8 728 1 763 8 764 1 773 8 774 1 157 8 172 1 171 8 173 1 158 8 182 1 223 8 224 1 212 8 233 1 296 8 297 1 285 8 306 1 343 8 344 1 353 8 354 1 523 8 524 1 512 8 533 1 571 8 572 1 560 8 581 1 722 8 723 1 713 8 732 1 768 8 769 1 759 8 778 1 190 8 192 1 191 8 193 1 203 8 204 1 240 8 241 1 250 8 251 1 314 8 315 1 324 8 325 1 361 8 362 1 371 8 372 1 541 8 542 1 551 8 552 1 588 8 589 1 598 8 599 1 740 8 741 1 750 8 751 1 785 8 786 1 795 8 796 1 5 8 198 1 197 8 199 1 186 8 208 1 245 8 246 1 153 8 255 1 319 8 320 1 310 8 329 1 366 8 367 1 122 8 376 1 546 8 547 1 537 8 556 1 593 8 594 1 508 8 603 1 745 8 746 1 736 8 755 1 790 8 791 1 6 8 800 0 0 0 0 0 0 0 0 diff --git a/test/data/legacy_lookup_tables/ParameterBox2d_legacy_interpolated.dat b/test/data/legacy_lookup_tables/ParameterBox2d_legacy_interpolated.dat new file mode 100644 index 0000000..5039123 --- /dev/null +++ b/test/data/legacy_lookup_tables/ParameterBox2d_legacy_interpolated.dat @@ -0,0 +1,441 @@ +0 0.029999999999999999 1.0037499999999999 +0 0.076999999999999999 1.009625 +0 0.124 1.0155000000000001 +0 0.17100000000000001 1.032875 +0 0.218 1.0505 +0 0.26500000000000001 1.0718749999999999 +0 0.31200000000000006 1.1012500000000001 +0 0.35899999999999999 1.130625 +0 0.40600000000000003 1.1677500000000001 +0 0.45299999999999996 1.2088749999999999 +0 0.5 1.25 +0 0.54700000000000004 1.302875 +0 0.59400000000000008 1.35575 +0 0.64100000000000001 1.412625 +0 0.68800000000000006 1.4772500000000002 +0 0.73499999999999999 1.5418749999999999 +0 0.78200000000000003 1.6145 +0 0.82900000000000007 1.6908750000000001 +0 0.876 1.7675000000000001 +0 0.92300000000000004 1.8556250000000001 +0 0.96999999999999997 1.9437500000000001 +0.050000000000000003 0.029999999999999999 1.0572091039063307 +0.050000000000000003 0.076999999999999999 1.0633970027710378 +0.050000000000000003 0.124 1.0695849016357448 +0.050000000000000003 0.17100000000000001 1.0878852833845594 +0.050000000000000003 0.218 1.1064489799786803 +0.050000000000000003 0.26500000000000001 1.1289623992524018 +0.050000000000000003 0.31200000000000006 1.1599018935759369 +0.050000000000000003 0.35899999999999999 1.1908413878994721 +0.050000000000000003 0.40600000000000003 1.2299436424275145 +0.050000000000000003 0.45299999999999996 1.2732589344804639 +0.050000000000000003 0.5 1.3165742265334131 +0.050000000000000003 0.54700000000000004 1.3722653163157765 +0.050000000000000003 0.59400000000000008 1.42795640609814 +0.050000000000000003 0.64100000000000001 1.4878605334054102 +0.050000000000000003 0.68800000000000006 1.5559274209171878 +0.050000000000000003 0.73499999999999999 1.623994308428965 +0.050000000000000003 0.78200000000000003 1.7004872709905567 +0.050000000000000003 0.82900000000000007 1.7809299562317482 +0.050000000000000003 0.876 1.8616359563182463 +0.050000000000000003 0.92300000000000004 1.9544544392888517 +0.050000000000000003 0.96999999999999997 2.0472729222594577 +0.10000000000000001 0.029999999999999999 1.1106682078126615 +0.10000000000000001 0.076999999999999999 1.1171690055420755 +0.10000000000000001 0.124 1.1236698032714898 +0.10000000000000001 0.17100000000000001 1.1428955667691187 +0.10000000000000001 0.218 1.1623979599573608 +0.10000000000000001 0.26500000000000001 1.1860497985048035 +0.10000000000000001 0.31200000000000006 1.2185537871518741 +0.10000000000000001 0.35899999999999999 1.2510577757989445 +0.10000000000000001 0.40600000000000003 1.2921372848550292 +0.10000000000000001 0.45299999999999996 1.3376428689609277 +0.10000000000000001 0.5 1.3831484530668263 +0.10000000000000001 0.54700000000000004 1.4416556326315531 +0.10000000000000001 0.59400000000000008 1.50016281219628 +0.10000000000000001 0.64100000000000001 1.5630960668108205 +0.10000000000000001 0.68800000000000006 1.6346048418343755 +0.10000000000000001 0.73499999999999999 1.7061136168579303 +0.10000000000000001 0.78200000000000003 1.786474541981113 +0.10000000000000001 0.82900000000000007 1.8709849124634963 +0.10000000000000001 0.876 1.9557719126364925 +0.10000000000000001 0.92300000000000004 2.0532838785777039 +0.10000000000000001 0.96999999999999997 2.1507958445189148 +0.15000000000000002 0.029999999999999999 1.1676863102127257 +0.15000000000000002 0.076999999999999999 1.1745208378067478 +0.15000000000000002 0.124 1.18135536540077 +0.15000000000000002 0.17100000000000001 1.2015681172213888 +0.15000000000000002 0.218 1.2220717000034553 +0.15000000000000002 0.26500000000000001 1.246937747207238 +0.15000000000000002 0.31200000000000006 1.2811103851773489 +0.15000000000000002 0.35899999999999999 1.3152830231474599 +0.15000000000000002 0.40600000000000003 1.3584714209224511 +0.15000000000000002 0.45299999999999996 1.4063131140806064 +0.15000000000000002 0.5 1.4541548072387618 +0.15000000000000002 0.54700000000000004 1.5156655555849614 +0.15000000000000002 0.59400000000000008 1.5771763039311608 +0.15000000000000002 0.64100000000000001 1.6433403476605248 +0.15000000000000002 0.68800000000000006 1.7185201511947685 +0.15000000000000002 0.73499999999999999 1.7936999547290124 +0.15000000000000002 0.78200000000000003 1.8781863490295849 +0.15000000000000002 0.82900000000000007 1.9670352077518733 +0.15000000000000002 0.876 2.0561748974356093 +0.15000000000000002 0.92300000000000004 2.1586928113459418 +0.15000000000000002 0.96999999999999997 2.2612107252562739 +0.20000000000000001 0.029999999999999999 1.2282634111065232 +0.20000000000000001 0.076999999999999999 1.2354524995650544 +0.20000000000000001 0.124 1.2426415880235857 +0.20000000000000001 0.17100000000000001 1.2639029347413697 +0.20000000000000001 0.218 1.2854702001169638 +0.20000000000000001 0.26500000000000001 1.3116262453597054 +0.20000000000000001 0.31200000000000006 1.3475716876523622 +0.20000000000000001 0.35899999999999999 1.3835171299450189 +0.20000000000000001 0.40600000000000003 1.4289460506297809 +0.20000000000000001 0.45299999999999996 1.4792696698394998 +0.20000000000000001 0.5 1.5295932890492194 +0.20000000000000001 0.54700000000000004 1.5942950851760012 +0.20000000000000001 0.59400000000000008 1.6589968813027833 +0.20000000000000001 0.64100000000000001 1.7285933759545227 +0.20000000000000001 0.68800000000000006 1.8076733489983674 +0.20000000000000001 0.73499999999999999 1.8867533220422119 +0.20000000000000001 0.78200000000000003 1.9756226921359716 +0.20000000000000001 0.82900000000000007 2.0690808420968789 +0.20000000000000001 0.876 2.1628449107155956 +0.20000000000000001 0.92300000000000004 2.2706812375935659 +0.20000000000000001 0.96999999999999997 2.3785175644715357 +0.25 0.029999999999999999 1.2888405120003203 +0.25 0.076999999999999999 1.2963841613233609 +0.25 0.124 1.3039278106464014 +0.25 0.17100000000000001 1.3262377522613509 +0.25 0.218 1.3488687002304722 +0.25 0.26500000000000001 1.3763147435121728 +0.25 0.31200000000000006 1.4140329901273752 +0.25 0.35899999999999999 1.4517512367425776 +0.25 0.40600000000000003 1.4994206803371102 +0.25 0.45299999999999996 1.5522262255983934 +0.25 0.5 1.6050317708596769 +0.25 0.54700000000000004 1.6729246147670411 +0.25 0.59400000000000008 1.7408174586744054 +0.25 0.64100000000000001 1.8138464042485207 +0.25 0.68800000000000006 1.8968265468019661 +0.25 0.73499999999999999 1.9798066893554114 +0.25 0.78200000000000003 2.0730590352423586 +0.25 0.82900000000000007 2.1711264764418852 +0.25 0.876 2.2695149239955827 +0.25 0.92300000000000004 2.3826696638411904 +0.25 0.96999999999999997 2.4958244036867971 +0.30000000000000004 0.029999999999999999 1.3574833601694001 +0.30000000000000004 0.076999999999999999 1.3654287795875772 +0.30000000000000004 0.124 1.3733741990057542 +0.30000000000000004 0.17100000000000001 1.3968723543063204 +0.30000000000000004 0.218 1.4207086125608517 +0.30000000000000004 0.26500000000000001 1.4496164151248574 +0.30000000000000004 0.31200000000000006 1.489343512215743 +0.30000000000000004 0.35899999999999999 1.5290706093066282 +0.30000000000000004 0.40600000000000003 1.579278897970428 +0.30000000000000004 0.45299999999999996 1.6348968338976673 +0.30000000000000004 0.5 1.6905147698249068 +0.30000000000000004 0.54700000000000004 1.7620235445885004 +0.30000000000000004 0.59400000000000008 1.833532319352094 +0.30000000000000004 0.64100000000000001 1.9104507413791274 +0.30000000000000004 0.68800000000000006 1.9978503549790751 +0.30000000000000004 0.73499999999999999 2.0852499685790225 +0.30000000000000004 0.78200000000000003 2.1834688767058497 +0.30000000000000004 0.82900000000000007 2.2867593291421517 +0.30000000000000004 0.876 2.3903878845324185 +0.30000000000000004 0.92300000000000004 2.5095691758050744 +0.30000000000000004 0.96999999999999997 2.62875046707773 +0.35000000000000003 0.029999999999999999 1.4261262083384798 +0.35000000000000003 0.076999999999999999 1.4344733978517934 +0.35000000000000003 0.124 1.4428205873651072 +0.35000000000000003 0.17100000000000001 1.4675069563512899 +0.35000000000000003 0.218 1.4925485248912307 +0.35000000000000003 0.26500000000000001 1.5229180867375423 +0.35000000000000003 0.31200000000000006 1.5646540343041107 +0.35000000000000003 0.35899999999999999 1.6063899818706786 +0.35000000000000003 0.40600000000000003 1.6591371156037458 +0.35000000000000003 0.45299999999999996 1.7175674421969411 +0.35000000000000003 0.5 1.7759977687901367 +0.35000000000000003 0.54700000000000004 1.8511224744099595 +0.35000000000000003 0.59400000000000008 1.9262471800297822 +0.35000000000000003 0.64100000000000001 2.0070550785097336 +0.35000000000000003 0.68800000000000006 2.0988741631561836 +0.35000000000000003 0.73499999999999999 2.1906932478026335 +0.35000000000000003 0.78200000000000003 2.2938787181693412 +0.35000000000000003 0.82900000000000007 2.4023921818424183 +0.35000000000000003 0.876 2.5112608450692542 +0.35000000000000003 0.92300000000000004 2.636468687768958 +0.35000000000000003 0.96999999999999997 2.7616765304686623 +0.40000000000000002 0.029999999999999999 1.4993389010314664 +0.40000000000000002 0.076999999999999999 1.5081146081732446 +0.40000000000000002 0.124 1.5168903153150228 +0.40000000000000002 0.17100000000000001 1.5428440023938987 +0.40000000000000002 0.218 1.5691711238192332 +0.40000000000000002 0.26500000000000001 1.6010997604414476 +0.40000000000000002 0.31200000000000006 1.6449782961503385 +0.40000000000000002 0.35899999999999999 1.6888568318592296 +0.40000000000000002 0.40600000000000003 1.7443118323083386 +0.40000000000000002 0.45299999999999996 1.8057417823007857 +0.40000000000000002 0.5 1.8671717322932333 +0.40000000000000002 0.54700000000000004 1.9461530965692373 +0.40000000000000002 0.59400000000000008 2.0251344608452406 +0.40000000000000002 0.64100000000000001 2.1100907746645832 +0.40000000000000002 0.68800000000000006 2.2066235532241434 +0.40000000000000002 0.73499999999999999 2.3031563317837036 +0.40000000000000002 0.78200000000000003 2.4116390094299405 +0.40000000000000002 0.82900000000000007 2.5257232022730571 +0.40000000000000002 0.876 2.6401808294626323 +0.40000000000000002 0.92300000000000004 2.771816436589305 +0.40000000000000002 0.96999999999999997 2.9034520437159772 +0.45000000000000001 0.029999999999999999 1.57712143824836 +0.45000000000000001 0.076999999999999999 1.5863524105519309 +0.45000000000000001 0.124 1.5955833828555015 +0.45000000000000001 0.17100000000000001 1.6228834924341469 +0.45000000000000001 0.218 1.6505764093448589 +0.45000000000000001 0.26500000000000001 1.6841614362365736 +0.45000000000000001 0.31200000000000006 1.7303162977544275 +0.45000000000000001 0.35899999999999999 1.7764711592722811 +0.45000000000000001 0.40600000000000003 1.8348030480842068 +0.45000000000000001 0.45299999999999996 1.8994198542092018 +0.45000000000000001 0.5 1.9640366603341968 +0.45000000000000001 0.54700000000000004 2.0471154110663337 +0.45000000000000001 0.59400000000000008 2.1301941617984701 +0.45000000000000001 0.64100000000000001 2.2195578298436756 +0.45000000000000001 0.68800000000000006 2.3210985251829541 +0.45000000000000001 0.73499999999999999 2.4226392205222318 +0.45000000000000001 0.78200000000000003 2.5367497504876484 +0.45000000000000001 0.82900000000000007 2.6567523904340682 +0.45000000000000001 0.876 2.7771478377125547 +0.45000000000000001 0.92300000000000004 2.9156124222661153 +0.45000000000000001 0.96999999999999997 3.0540770068196759 +0.5 0.029999999999999999 1.6549039754652537 +0.5 0.076999999999999999 1.6645902129306167 +0.5 0.124 1.6742764503959799 +0.5 0.17100000000000001 1.702922982474395 +0.5 0.218 1.7319816948704847 +0.5 0.26500000000000001 1.7672231120316999 +0.5 0.31200000000000006 1.8156542993585163 +0.5 0.35899999999999999 1.8640854866853322 +0.5 0.40600000000000003 1.925294263860075 +0.5 0.45299999999999996 1.9930979261176176 +0.5 0.5 2.0609015883751605 +0.5 0.54700000000000004 2.1480777255634296 +0.5 0.59400000000000008 2.2352538627516987 +0.5 0.64100000000000001 2.3290248850227684 +0.5 0.68800000000000006 2.4355734971417644 +0.5 0.73499999999999999 2.5421221092607604 +0.5 0.78200000000000003 2.6585630490039565 +0.5 0.82900000000000007 2.7830415049418167 +0.5 0.876 2.914011800883058 +0.5 0.92300000000000004 3.0544622441308253 +0.5 0.96999999999999997 3.2016106175408114 +0.55000000000000004 0.029999999999999999 1.7430431371881894 +0.55000000000000004 0.076999999999999999 1.7532452576673732 +0.55000000000000004 0.124 1.7634473781465569 +0.55000000000000004 0.17100000000000001 1.7936196067977599 +0.55000000000000004 0.218 1.8242259682353108 +0.55000000000000004 0.26500000000000001 1.8613443214680852 +0.55000000000000004 0.31200000000000006 1.9123549238640041 +0.55000000000000004 0.35899999999999999 1.9633655262599223 +0.55000000000000004 0.40600000000000003 2.0278342450326363 +0.55000000000000004 0.45299999999999996 2.0992490883869221 +0.55000000000000004 0.5 2.1706639317412075 +0.55000000000000004 0.54700000000000004 2.2624830160538605 +0.55000000000000004 0.59400000000000008 2.3543021003665139 +0.55000000000000004 0.64100000000000001 2.4530673092607387 +0.55000000000000004 0.68800000000000006 2.5652906345317588 +0.55000000000000004 0.73499999999999999 2.6775139598027788 +0.55000000000000004 0.78200000000000003 2.7957331172795765 +0.55000000000000004 0.82900000000000007 2.9266341097475506 +0.55000000000000004 0.876 3.0643618923856257 +0.55000000000000004 0.92300000000000004 3.2120589559070236 +0.55000000000000004 0.96999999999999997 3.3667995330959863 +0.60000000000000009 0.029999999999999999 1.8311822989111253 +0.60000000000000009 0.076999999999999999 1.8419003024041296 +0.60000000000000009 0.124 1.8526183058971337 +0.60000000000000009 0.17100000000000001 1.8843162311211246 +0.60000000000000009 0.218 1.9164702416001367 +0.60000000000000009 0.26500000000000001 1.9554655309044708 +0.60000000000000009 0.31200000000000006 2.0090555483694912 +0.60000000000000009 0.35899999999999999 2.062645565834512 +0.60000000000000009 0.40600000000000003 2.1303742262051975 +0.60000000000000009 0.45299999999999996 2.205400250656226 +0.60000000000000009 0.5 2.2804262751072546 +0.60000000000000009 0.54700000000000004 2.3768883065442914 +0.60000000000000009 0.59400000000000008 2.4733503379813282 +0.60000000000000009 0.64100000000000001 2.577109733498709 +0.60000000000000009 0.68800000000000006 2.6950077719217536 +0.60000000000000009 0.73499999999999999 2.8129058103447981 +0.60000000000000009 0.78200000000000003 2.9395382175550679 +0.60000000000000009 0.82900000000000007 3.0771724100669489 +0.60000000000000009 0.876 3.2219845447379751 +0.60000000000000009 0.92300000000000004 3.3772787536731519 +0.60000000000000009 0.96999999999999997 3.5399787759471302 +0.65000000000000002 0.029999999999999999 1.9251892571530691 +0.65000000000000002 0.076999999999999999 1.9364574881725205 +0.65000000000000002 0.124 1.9477257191919719 +0.65000000000000002 0.17100000000000001 1.9810509130580087 +0.65000000000000002 0.218 2.0148556061163627 +0.65000000000000002 0.26500000000000001 2.0558527870594729 +0.65000000000000002 0.31200000000000006 2.1121939421567295 +0.65000000000000002 0.35899999999999999 2.1685350972539865 +0.65000000000000002 0.40600000000000003 2.2397407273130723 +0.65000000000000002 0.45299999999999996 2.3186183444492316 +0.65000000000000002 0.5 2.397495961585391 +0.65000000000000002 0.54700000000000004 2.4989100407604532 +0.65000000000000002 0.59400000000000008 2.6003241199355154 +0.65000000000000002 0.64100000000000001 2.7094101861876507 +0.65000000000000002 0.68800000000000006 2.8333607274016153 +0.65000000000000002 0.73499999999999999 2.9573112686155798 +0.65000000000000002 0.78200000000000003 3.0902636316962533 +0.65000000000000002 0.82900000000000007 3.2349550451493188 +0.65000000000000002 0.876 3.3871924511914089 +0.65000000000000002 0.92300000000000004 3.5504494019666804 +0.65000000000000002 0.96999999999999997 3.7214919006512628 +0.70000000000000007 0.029999999999999999 2.0250640119140204 +0.70000000000000007 0.076999999999999999 2.0369168149725461 +0.70000000000000007 0.124 2.0487696180310717 +0.70000000000000007 0.17100000000000001 2.0838236526084128 +0.70000000000000007 0.218 2.1193820617839889 +0.70000000000000007 0.26500000000000001 2.1625060899330917 +0.70000000000000007 0.31200000000000006 2.221770105225719 +0.70000000000000007 0.35899999999999999 2.2810341205183464 +0.70000000000000007 0.40600000000000003 2.3559337483562617 +0.70000000000000007 0.45299999999999996 2.4389033697659395 +0.70000000000000007 0.5 2.5218729911756173 +0.70000000000000007 0.54700000000000004 2.6285482187023463 +0.70000000000000007 0.59400000000000008 2.7352234462290745 +0.70000000000000007 0.64100000000000001 2.8499686673275662 +0.70000000000000007 0.68800000000000006 2.9803495009713452 +0.70000000000000007 0.73499999999999999 3.1107303346151243 +0.70000000000000007 0.78200000000000003 3.2482038411187446 +0.70000000000000007 0.82900000000000007 3.4002902845323661 +0.70000000000000007 0.876 3.5603083884882545 +0.70000000000000007 0.92300000000000004 3.7319092348233269 +0.70000000000000007 0.96999999999999997 3.9116935404480384 +0.75 0.029999999999999999 2.1209693916438237 +0.75 0.076999999999999999 2.1310251417227337 +0.75 0.124 2.1496812043691329 +0.75 0.17100000000000001 2.1805100171110552 +0.75 0.218 2.2196745174183898 +0.75 0.26500000000000001 2.2671747052911364 +0.75 0.31200000000000006 2.323142893230334 +0.75 0.35899999999999999 2.3914161437660928 +0.75 0.40600000000000003 2.468025081867264 +0.75 0.45299999999999996 2.5529697075338471 +0.75 0.5 2.6462500207658435 +0.75 0.54700000000000004 2.7519677090954389 +0.75 0.59400000000000008 2.8660210849904466 +0.75 0.64100000000000001 2.9884101484508672 +0.75 0.68800000000000006 3.1191348994767001 +0.75 0.73499999999999999 3.2621647130990934 +0.75 0.78200000000000003 3.4136625267879381 +0.75 0.82900000000000007 3.5734960280421952 +0.75 0.876 3.7416652168618643 +0.75 0.92300000000000004 3.9220071557770568 +0.75 0.96999999999999997 4.1109494072597377 +0.80000000000000004 0.029999999999999999 2.2304020102801516 +0.80000000000000004 0.076999999999999999 2.2409765924873439 +0.80000000000000004 0.124 2.2605952252664765 +0.80000000000000004 0.17100000000000001 2.2930146680858949 +0.80000000000000004 0.218 2.3341998829981176 +0.80000000000000004 0.26500000000000001 2.3841508700031437 +0.80000000000000004 0.31200000000000006 2.4430067683405428 +0.80000000000000004 0.35899999999999999 2.5148026159577954 +0.80000000000000004 0.40600000000000003 2.5953642356678519 +0.80000000000000004 0.45299999999999996 2.6846916274707127 +0.80000000000000004 0.5 2.7827847913663777 +0.80000000000000004 0.54700000000000004 2.8939570437814641 +0.80000000000000004 0.59400000000000008 3.0138950682893553 +0.80000000000000004 0.64100000000000001 3.1425988648900502 +0.80000000000000004 0.68800000000000006 3.280068433583549 +0.80000000000000004 0.73499999999999999 3.4304779515569015 +0.80000000000000004 0.78200000000000003 3.5897923808626269 +0.80000000000000004 0.82900000000000007 3.7578725822611565 +0.80000000000000004 0.876 3.9347185557524891 +0.80000000000000004 0.92300000000000004 4.1243653392841075 +0.80000000000000004 0.96999999999999997 4.3230561733876671 +0.85000000000000009 0.029999999999999999 2.3451279770616651 +0.85000000000000009 0.076999999999999999 2.3562464877451763 +0.85000000000000009 0.124 2.3768742509869543 +0.85000000000000009 0.17100000000000001 2.4109612640035087 +0.85000000000000009 0.218 2.4542649371919212 +0.85000000000000009 0.26500000000000001 2.506785270552192 +0.85000000000000009 0.31200000000000006 2.5686685602775245 +0.85000000000000009 0.35899999999999999 2.6441573959708382 +0.85000000000000009 0.40600000000000003 2.7288628918360103 +0.85000000000000009 0.45299999999999996 2.8227850478730399 +0.85000000000000009 0.5 2.9259238640819278 +0.85000000000000009 0.54700000000000004 3.0428145224520016 +0.85000000000000009 0.59400000000000008 3.1689218409939324 +0.85000000000000009 0.64100000000000001 3.3042458197077216 +0.85000000000000009 0.68800000000000006 3.4487864585933687 +0.85000000000000009 0.73499999999999999 3.6069326434469966 +0.85000000000000009 0.78200000000000003 3.7744417846656875 +0.85000000000000009 0.82900000000000007 3.9511675860562354 +0.85000000000000009 0.876 4.1371100476186422 +0.85000000000000009 0.92300000000000004 4.336511758955826 +0.85000000000000009 0.96999999999999997 4.5454227228512751 +0.90000000000000002 0.029999999999999999 2.4653748864046898 +0.90000000000000002 0.076999999999999999 2.4770635009628155 +0.90000000000000002 0.124 2.4987489569193384 +0.90000000000000002 0.17100000000000001 2.5345837883935927 +0.90000000000000002 0.218 2.5801078661462924 +0.90000000000000002 0.26500000000000001 2.6353211901774394 +0.90000000000000002 0.31200000000000006 2.7003775580470082 +0.90000000000000002 0.35899999999999999 2.7797370989942833 +0.90000000000000002 0.40600000000000003 2.8687858862200053 +0.90000000000000002 0.45299999999999996 2.9675239197241727 +0.90000000000000002 0.5 3.075951199506787 +0.90000000000000002 0.54700000000000004 3.198835449927083 +0.90000000000000002 0.59400000000000008 3.3314089466258259 +0.90000000000000002 0.64100000000000001 3.4736716896030146 +0.90000000000000002 0.68800000000000006 3.6256236788586498 +0.90000000000000002 0.73499999999999999 3.7918788411919917 +0.90000000000000002 0.78200000000000003 3.967977047363755 +0.90000000000000002 0.82900000000000007 4.1537644998139651 +0.90000000000000002 0.876 4.3492411985426198 +0.90000000000000002 0.92300000000000004 4.5588672727890094 +0.90000000000000002 0.96999999999999997 4.7784901884337936 +0.95000000000000007 0.029999999999999999 2.5913776720149171 +0.95000000000000007 0.076999999999999999 2.603663679692592 +0.95000000000000007 0.124 2.6264574570945953 +0.95000000000000007 0.17100000000000001 2.6641237701064155 +0.95000000000000007 0.218 2.7119745368510455 +0.95000000000000007 0.26500000000000001 2.7700097573284848 +0.95000000000000007 0.31200000000000006 2.8383910895344924 +0.95000000000000007 0.35899999999999999 2.9218066153460769 +0.95000000000000007 0.40600000000000003 3.0154065948904711 +0.95000000000000007 0.45299999999999996 3.1191910281676742 +0.95000000000000007 0.5 3.233159915177688 +0.95000000000000007 0.54700000000000004 3.3623246537890368 +0.95000000000000007 0.59400000000000008 3.5016738461331953 +0.95000000000000007 0.64100000000000001 3.6512074922101636 +0.95000000000000007 0.68800000000000006 3.8109255920199407 +0.95000000000000007 0.73499999999999999 3.9856778854352952 +0.95000000000000007 0.78200000000000003 4.1707762905792176 +0.95000000000000007 0.82900000000000007 4.3660591494559506 +0.95000000000000007 0.876 4.5715264620654921 +0.95000000000000007 0.92300000000000004 4.7918663102848509 +0.95000000000000007 0.96999999999999997 5.0227139282285389 +1 0.029999999999999999 2.7233786068874055 +1 0.076999999999999999 2.7362904455725863 +1 0.124 2.7602453041858821 +1 0.17100000000000001 2.7998302833128168 +1 0.218 2.850118497139309 +1 0.26500000000000001 2.9111099456653586 +1 0.31200000000000006 2.9829745215052448 +1 0.35899999999999999 3.0706391104730488 +1 0.40600000000000003 3.1690069341404108 +1 0.45299999999999996 3.2780779925073293 +1 0.5 3.3978522855738063 +1 0.54700000000000004 3.5335964843824801 +1 0.59400000000000008 3.6800439178907114 +1 0.64100000000000001 3.8371945860984997 +1 0.68800000000000006 4.005048489005846 +1 0.73499999999999999 4.1887024050411101 +1 0.78200000000000003 4.3832294483902103 +1 0.82900000000000007 4.5884597264388685 +1 0.876 4.8043932391870836 +1 0.92300000000000004 5.0359568724489385 +1 0.96999999999999997 5.2785635256389085