From b7df7f9eb153748c9d99365f25065ed6e882e4b1 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 3 Nov 2017 11:39:26 +0800 Subject: [PATCH 01/43] remove usused ProtoDataProvider related codes --- paddle/gserver/CMakeLists.txt | 1 - paddle/gserver/dataproviders/DataProvider.cpp | 4 +- .../dataproviders/ProtoDataProvider.cpp | 932 ------------------ .../gserver/dataproviders/ProtoDataProvider.h | 179 ---- paddle/gserver/tests/CMakeLists.txt | 11 - paddle/gserver/tests/proto_files.txt | 2 - .../gserver/tests/proto_files_compressed.txt | 2 - .../gserver/tests/test_ProtoDataProvider.cpp | 732 -------------- 8 files changed, 1 insertion(+), 1862 deletions(-) delete mode 100644 paddle/gserver/dataproviders/ProtoDataProvider.cpp delete mode 100644 paddle/gserver/dataproviders/ProtoDataProvider.h delete mode 100644 paddle/gserver/tests/proto_files.txt delete mode 100644 paddle/gserver/tests/proto_files_compressed.txt delete mode 100644 paddle/gserver/tests/test_ProtoDataProvider.cpp diff --git a/paddle/gserver/CMakeLists.txt b/paddle/gserver/CMakeLists.txt index 5f39167afc..b02902543b 100644 --- a/paddle/gserver/CMakeLists.txt +++ b/paddle/gserver/CMakeLists.txt @@ -73,7 +73,6 @@ if(MOBILE_INFERENCE) list(REMOVE_ITEM GSERVER_SOURCES dataproviders/DataProvider.cpp dataproviders/MultiDataProvider.cpp - dataproviders/ProtoDataProvider.cpp dataproviders/PyDataProvider2.cpp dataproviders/PyDataProvider.cpp) diff --git a/paddle/gserver/dataproviders/DataProvider.cpp b/paddle/gserver/dataproviders/DataProvider.cpp index 0478256f9c..106cf5b622 100644 --- a/paddle/gserver/dataproviders/DataProvider.cpp +++ b/paddle/gserver/dataproviders/DataProvider.cpp @@ -16,8 +16,8 @@ limitations under the License. */ #include #include -#include "ProtoDataProvider.h" #include "paddle/utils/Logging.h" +#include "paddle/utils/Stat.h" #include "paddle/utils/StringUtil.h" #include "paddle/utils/Util.h" @@ -164,8 +164,6 @@ DataProvider* DataProvider::create(const DataConfig& config, REGISTER_DATA_PROVIDER(simple, SimpleDataProvider); REGISTER_DATA_PROVIDER(dummy, DummyDataProvider); -REGISTER_DATA_PROVIDER(proto, ProtoDataProvider); -REGISTER_DATA_PROVIDER(proto_sequence, ProtoSequenceDataProvider); int64_t DataProvider::getNextBatch(int64_t size, DataBatch* batch) { int64_t batchSize = doubleBuffer_ ? getNextBatchFromBuffer(size, batch) diff --git a/paddle/gserver/dataproviders/ProtoDataProvider.cpp b/paddle/gserver/dataproviders/ProtoDataProvider.cpp deleted file mode 100644 index c6f5cab191..0000000000 --- a/paddle/gserver/dataproviders/ProtoDataProvider.cpp +++ /dev/null @@ -1,932 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "ProtoDataProvider.h" -#include -#include -#include -#include "paddle/utils/StringUtil.h" -#include "paddle/utils/Util.h" - -#include "DataProviderGroup.h" -#include "paddle/utils/Logging.h" - -DEFINE_double(memory_threshold_on_load_data, - 1.0, - "stop loading data when memory is not sufficient"); - -namespace paddle { - -REGISTER_DATA_PROVIDER(proto_group, DataProviderGroup); -REGISTER_DATA_PROVIDER(proto_sequence_group, - DataProviderGroup); - -ProtoDataProvider::ProtoDataProvider(const DataConfig& config, - bool useGpu, - bool loadDataAll) - : DataProvider(config, useGpu), sampleNums_(0), currentSequenceIndex_(0) { - if (loadDataAll) { - loadData(config_.files()); - } -} - -void ProtoDataProvider::loadData(const std::vector& fileList) { - for (auto& file : fileList) { - if (FLAGS_memory_threshold_on_load_data < 1.0) { - double memUsage = getMemoryUsage(); - if (memUsage > FLAGS_memory_threshold_on_load_data) { - LOG(INFO) << "memUsage is " << memUsage << ", > " - << FLAGS_memory_threshold_on_load_data - << " therefore SKIP ALL REMAINING file."; - break; - } - } - LOG(INFO) << "load data file " << file; - loadDataFile(file); - } - - if (sequenceStartPositions_.size() == sampleNums_) { - // This means that each sample is one sequence - shuffledSequenceIds_.swap(sequenceStartPositions_); - } else { - sequenceStartPositions_.push_back(sampleNums_); - shuffledSequenceIds_.reserve(sequenceStartPositions_.size() - 1); - for (size_t i = 0; i < sequenceStartPositions_.size() - 1; ++i) { - shuffledSequenceIds_.push_back(i); - } - } - - LOG(INFO) << "read done, num of instance=" << sampleNums_; - showDataStats(); -} - -void ProtoDataProvider::loadData(const std::string& fileName) { - std::vector fileList; - loadFileList(fileName, fileList); - loadData(fileList); -} - -void ProtoDataProvider::checkDataHeader(const DataHeader& header) { - if (header_.slot_defs_size()) { - // header_ is already set. Need to check consistency. - CHECK_EQ(header_.slot_defs_size(), header.slot_defs_size()) - << "Different header"; - for (int i = 0; i < header.slot_defs_size(); ++i) { - CHECK_EQ(header_.slot_defs(i).type(), header.slot_defs(i).type()); - CHECK_EQ(header_.slot_defs(i).dim(), header.slot_defs(i).dim()); - } - return; - } - - // header_ is not set before - CHECK(header.slot_defs_size()) << "Invalid header: no slot is defined"; - int i; - for (i = 0; i < header.slot_defs_size(); ++i) { - if (header.slot_defs(i).type() == SlotDef::INDEX || - header.slot_defs(i).type() == SlotDef::VAR_MDIM_INDEX) { - break; - } - constexpr int kBufLen = 100; - char buf[kBufLen]; - snprintf(buf, kBufLen, "slot%d_nnz", i); - nnzStats_.push_back(getStat(buf)); - } - numVecSlots_ = i; - - // Check that INDEX slots are after VECTOR slots - for (int i = numVecSlots_; i < header.slot_defs_size(); ++i) { - CHECK(header.slot_defs(i).type() == SlotDef::INDEX || - header.slot_defs(i).type() == SlotDef::VAR_MDIM_INDEX); - } - - slots_.clear(); - slots_.reserve(header.slot_defs_size()); - for (int i = 0; i < header.slot_defs_size(); ++i) { - slots_.emplace_back(); - slots_.back().type = header.slot_defs(i).type(); - slots_.back().dim = header.slot_defs(i).dim(); - if (SlotDef::VECTOR_SPARSE_NON_VALUE == header.slot_defs(i).type() || - SlotDef::VECTOR_SPARSE_VALUE == header.slot_defs(i).type()) { - slots_.back().indices.push_back(0); - } - } - - header_ = header; -} - -void ProtoDataProvider::checkSample(const DataSample& sample) { - CHECK_EQ(numVecSlots_, sample.vector_slots_size()); - CHECK(header_.slot_defs_size() == numVecSlots_ + sample.id_slots_size() || - header_.slot_defs_size() == numVecSlots_ + sample.var_id_slots_size()); - for (int i = 0; i < numVecSlots_; ++i) { - uint32_t dim = header_.slot_defs(i).dim(); - switch (header_.slot_defs(i).type()) { - case SlotDef::VECTOR_DENSE: { - CHECK_EQ(static_cast(dim), sample.vector_slots(i).values_size()); - CHECK_EQ(0, sample.vector_slots(i).ids_size()); - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - if (0 == sample.vector_slots(i).ids_size()) { - break; - } - CHECK_LT(0, sample.vector_slots(i).ids_size()); - CHECK_EQ(0, sample.vector_slots(i).values_size()); - auto maxId = *std::max_element(sample.vector_slots(i).ids().begin(), - sample.vector_slots(i).ids().end()); - CHECK_GT(dim, maxId); - break; - } - case SlotDef::VECTOR_SPARSE_VALUE: { - if (0 == sample.vector_slots(i).ids_size()) { - CHECK_EQ(0, sample.vector_slots(i).values_size()); - break; - } - CHECK_LT(0, sample.vector_slots(i).values_size()); - CHECK_GE(static_cast(dim), sample.vector_slots(i).values_size()); - CHECK_EQ(sample.vector_slots(i).values_size(), - sample.vector_slots(i).ids_size()); - auto maxId = *std::max_element(sample.vector_slots(i).ids().begin(), - sample.vector_slots(i).ids().end()); - CHECK_GT(dim, maxId); - break; - } - case SlotDef::VAR_MDIM_DENSE: { - if (static_cast(dim) != 0) { - CHECK_EQ(static_cast(dim), sample.vector_slots(i).values_size()); - if (sample.vector_slots(i).dims_size() != 0) { - int totalDim = sample.vector_slots(i).dims(0); - for (int j = 1; j < sample.vector_slots(i).dims_size(); ++j) { - totalDim *= sample.vector_slots(i).dims(j); - } - CHECK_EQ(static_cast(dim), totalDim); - } - } else { - CHECK_NE(sample.vector_slots(i).dims_size(), 0); - int totalDim = sample.vector_slots(i).dims(0); - for (int j = 1; j < sample.vector_slots(i).dims_size(); ++j) { - totalDim *= sample.vector_slots(i).dims(j); - } - CHECK_EQ(totalDim, sample.vector_slots(i).values_size()); - } - break; - } - case SlotDef::STRING: { - CHECK_EQ(static_cast(1), sample.vector_slots(i).strs_size()); - CHECK_EQ(0, sample.vector_slots(i).ids_size()); - CHECK_EQ(0, sample.vector_slots(i).values_size()); - break; - } - default: - LOG(FATAL) << "BUG: Should not reach here"; - } - } - for (int i = numVecSlots_; i < header_.slot_defs_size(); ++i) { - if (header_.slot_defs(i).type() != SlotDef::VAR_MDIM_INDEX) { - uint32_t id = sample.id_slots(i - numVecSlots_); - if (id == -1U) continue; - CHECK_LT(id, header_.slot_defs(i).dim()); - } else { - for (int j = 0; j < sample.var_id_slots(i - numVecSlots_).ids_size(); - ++j) { - uint32_t id = sample.var_id_slots(i - numVecSlots_).ids(j); - CHECK_LT(id, header_.slot_defs(i).dim()); - } - } - } -} - -void ProtoDataProvider::loadDataFile(const std::string& fileName) { - std::ifstream is(fileName); - CHECK(is) << "Fail to open " << fileName; - bool dataCompression = str::endsWith(fileName, ".gz"); - std::unique_ptr reader(new ProtoReader(&is, dataCompression)); - CHECK(reader) << "Fail to create proto data input stream"; - - DataHeader header; - CHECK(reader->read(&header)); - checkDataHeader(header); - - DataSample sample; - do { - if (!reader->read(&sample)) { - break; - } - checkSample(sample); - if (sample.is_beginning()) { - sequenceStartPositions_.push_back(sampleNums_); - } - fillSlots(sample); - ++sampleNums_; - } while (true); - - CHECK(is.eof()) << "Fail to read file"; - reader.reset(nullptr); - is.close(); -} - -// checkSample has done before, no check here -void ProtoDataProvider::fillSlots(const DataSample& sample) { - for (size_t i = 0; i < slots_.size(); ++i) { - auto& slot = slots_[i]; - int dim = slot.dim; - switch (slot.type) { - case SlotDef::VECTOR_DENSE: { - size_t oldSize = slot.denseData.size(); - slot.denseData.resize(oldSize + dim); - const float* values = sample.vector_slots(i).values().data(); -#ifdef PADDLE_TYPE_DOUBLE - std::copy(values, values + dim, slot.denseData.begin() + oldSize); -#else - memcpy(slot.denseData.data() + oldSize, values, sizeof(real) * dim); -#endif - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - int slotSize = sample.vector_slots(i).ids_size(); - int subSlotSize = 0; - int id = 0; // the slot id - // find whether this vector_slots has subseq. If not has subseq, - // subSlotSize = 0. - for (id = 0; id < sample.subseq_slots_size(); id++) { - if (sample.subseq_slots(id).slot_id() == i) { - subSlotSize = sample.subseq_slots(id).lens_size(); - break; - } - } - if (subSlotSize && slot.subIndices.size() == 0UL) { - // If has subSeq, the first element of subIndices = 0. - slot.subIndices.push_back(0); - } - if (slotSize == 0UL) { - // if has no id, new indices = old indices. - slot.indices.push_back(slot.indices.back()); - // if has subSeq, new subIndices = old subIndices. - if (slot.subIndices.size()) { - slot.subIndices.push_back(slot.subIndices.back()); - } - break; - } - slot.sparseNonValueData.resize(slot.indices.back() + slotSize); - const unsigned int* ids = sample.vector_slots(i).ids().data(); - memcpy(slot.sparseNonValueData.data() + slot.indices.back(), - ids, - sizeof(*ids) * slotSize); - slot.indices.push_back(slot.indices.back() + slotSize); - if (subSlotSize) { - for (int ii = 0; ii < subSlotSize; ++ii) { - slot.subIndices.push_back(slot.subIndices.back() + - sample.subseq_slots(id).lens(ii)); - } - } - break; - } - case SlotDef::VECTOR_SPARSE_VALUE: { - if (0 == sample.vector_slots(i).ids_size()) { - slot.indices.push_back(slot.indices.back()); - break; - } - int slotSize = sample.vector_slots(i).ids_size(); - slot.sparseFloatValueData.resize(slot.indices.back() + slotSize); - const unsigned int* ids = sample.vector_slots(i).ids().data(); - const float* values = sample.vector_slots(i).values().data(); - for (int ii = 0; ii < slotSize; ++ii) { - slot.sparseFloatValueData[slot.indices.back() + ii].col = ids[ii]; - slot.sparseFloatValueData[slot.indices.back() + ii].value = - values[ii]; - } - slot.indices.push_back(slot.indices.back() + slotSize); - break; - } - case SlotDef::INDEX: { - slot.indexData.push_back(sample.id_slots(i - numVecSlots_)); - break; - } - case SlotDef::VAR_MDIM_DENSE: { - size_t oldSize = slot.varDenseData.size(); - slot.varDenseData.resize(oldSize + 1); - size_t varDim = sample.vector_slots(i).values_size(); - slot.varDenseData[oldSize].data.resize(varDim); - const float* values = sample.vector_slots(i).values().data(); -#ifdef PADDLE_TYPE_DOUBLE - std::copy( - values, values + varDim, slot.varDenseData[oldSize].data.data()); -#else - memcpy(slot.varDenseData[oldSize].data.data(), - values, - sizeof(real) * varDim); -#endif - slot.varDenseData[oldSize].dims.resize( - sample.vector_slots(i).dims_size()); - memcpy(slot.varDenseData[oldSize].dims.data(), - sample.vector_slots(i).dims().data(), - sizeof(uint32_t) * sample.vector_slots(i).dims_size()); - break; - } - case SlotDef::VAR_MDIM_INDEX: { - size_t oldSize = slot.varIndices.size(); - slot.varIndices.resize(oldSize + 1); - size_t varDim = sample.var_id_slots(i - numVecSlots_).ids_size(); - slot.varIndices[oldSize].resize(varDim); - memcpy(slot.varIndices[oldSize].data(), - sample.var_id_slots(i - numVecSlots_).ids().data(), - sizeof(uint32_t) * varDim); - break; - } - case SlotDef::STRING: { - slot.strData.push_back(sample.vector_slots(i).strs(0)); - break; - } - } - } -} - -void ProtoDataProvider::showDataStats() { - std::ostringstream oss; - for (size_t i = 0; i < slots_.size(); ++i) { - auto& slot = slots_[i]; - if (slot.type == SlotDef::VECTOR_SPARSE_NON_VALUE) { - size_t nnz = slot.sparseNonValueData.size(); - oss << "slot" << i << ":avgNNZ=" << ((double)nnz / sampleNums_) << "; "; - } else if (slot.type == SlotDef::VECTOR_SPARSE_VALUE) { - size_t nnz = slot.sparseFloatValueData.size(); - oss << "slot" << i << ":avgNNZ=" << ((double)nnz / sampleNums_) << "; "; - } - } - LOG(INFO) << oss.str(); -} - -void ProtoDataProvider::reset() { - currentSequenceIndex_ = 0; - if (!skipShuffle_) { - shuffle(); - } - - DataProvider::reset(); -} - -void ProtoDataProvider::shuffle() { - std::shuffle(shuffledSequenceIds_.begin(), - shuffledSequenceIds_.end(), - ThreadLocalRandomEngine::get()); -} - -/* - Loop through sequences starting from currentSequenceIndex_ - for at most size samples. For each sequence ranging from [begin, end), - op(begin, end) will be called. - - return the number of sequences scanned -*/ -template -int64_t ProtoDataProvider::sequenceLoop(Op op, int64_t size) { - int64_t sz = 0; - size_t i; - size_t sequenceCount = shuffledSequenceIds_.size(); - if (usageRatio_ < 1.0f) { - sequenceCount = static_cast(sequenceCount * usageRatio_); - } - for (i = currentSequenceIndex_; i < sequenceCount; ++i) { - size_t id = shuffledSequenceIds_[i]; - int64_t begin = sequenceStartPositions_[id]; - int64_t end = sequenceStartPositions_[id + 1]; - int64_t len = end - begin; - if (sz + len > size && sz > 0) break; - sz += len; - op(begin, end); - } - return i - currentSequenceIndex_; -} - -/* - Loop through sequences starting from currentSequenceIndex_ - for at most size samples. For each sample of each sequence at position - pos, op(pos) will be called. - - return the number of sequences scanned -*/ -template -int64_t ProtoDataProvider::sampleLoop(Op op, int64_t size) { - if (iidData()) { - size = std::min(sampleNums_ - currentSequenceIndex_, size); - for (int64_t i = currentSequenceIndex_; i < currentSequenceIndex_ + size; - ++i) { - size_t pos = shuffledSequenceIds_[i]; - op(pos); - } - return size; - } else { - auto f = [op](int64_t begin, int64_t end) { - for (int64_t pos = begin; pos < end; ++pos) { - op(pos); - } - }; - return sequenceLoop(f, size); - } -} - -/* - Loop through sub-sequences starting from currentSequenceIndex_ - for at most size samples. For each sample of each sub-sequence at position - pos, op(pos) will be called. - - return the number of sub-sequences scanned -*/ -template -int64_t ProtoDataProvider::subSampleLoop(Op op, int64_t size, int slot) { - CHECK(iidData()) << "subSampleLoop only accepts iid data"; - size = std::min(sampleNums_ - currentSequenceIndex_, size); - int subSize = 0; - for (int64_t i = currentSequenceIndex_; i < currentSequenceIndex_ + size; - ++i) { - size_t pos = shuffledSequenceIds_[i]; - int64_t* indexs = slots_[slot].indices.data(); - int64_t* subIndexs = slots_[slot].subIndices.data(); - int64_t subSeqStart = 0; - int64_t subSeqEnd = 0; - for (int j = 0; j < (int)slots_[slot].subIndices.size(); j++) { - if (subIndexs[j] == indexs[pos]) { - subSeqStart = j; - if (subIndexs[pos] == subIndexs[pos + 1]) { - subSeqEnd = j + 1; - break; - } - } else if (subIndexs[j] == indexs[pos + 1]) { - subSeqEnd = j; - break; - } - } - for (int j = subSeqStart; j < subSeqEnd; j++) { - op(j); - } - subSize += subSeqEnd - subSeqStart; - } - return subSize; -} - -int64_t ProtoDataProvider::getNextBatchInternal(int64_t size, - DataBatch* batch) { - int64_t numSequences = 0; // actual number of sequences in the batch - - // the number of sequences scanned, including those skipped because too long - int64_t numScannedSeqs = 0; - std::lock_guard guard(lock_); - if (iidData()) { - size = std::min(getSize() - currentSequenceIndex_, size); - numScannedSeqs = numSequences = size; - } else { - int64_t sz = 0; - auto op = [&sz, &numSequences](int64_t begin, int64_t end) { - ++numSequences; - sz += end - begin; - }; - numScannedSeqs = sequenceLoop(op, size); - VLOG_IF(1, numScannedSeqs > numSequences) - << numScannedSeqs - numSequences - << " sequences are skipped because longer than " << size; - size = sz; - } - if (size <= 0) return 0; - - DataBatch& cpuBatch = *cpuBatch_; - std::vector& cpuArguments = cpuBatch.getStreams(); - cpuBatch.setSize(size); - cpuArguments.resize(header_.slot_defs_size()); - - if (!iidData()) { - ICpuGpuVector::resizeOrCreate(cpuArguments[0].sequenceStartPositions, - numSequences + 1, - /* useGpu= */ false); - int* buf = cpuArguments[0].sequenceStartPositions->getMutableData(false); - int pos = 0; - int i = 0; - auto op = [buf, &pos, &i](int64_t begin, int64_t end) { - buf[i] = pos; - pos += end - begin; - ++i; - }; - sequenceLoop(op, size); - buf[i] = size; - for (size_t slot = 1; slot < cpuArguments.size(); ++slot) { - cpuArguments[slot].sequenceStartPositions = - cpuArguments[0].sequenceStartPositions; - } - } - - for (int slot = 0; slot < header_.slot_defs_size(); ++slot) { - size_t dim = header_.slot_defs(slot).dim(); - SlotDef::SlotType slotType = header_.slot_defs(slot).type(); - - std::vector dataPos; - dataPos.reserve(size); - auto op = [this, &dataPos](int64_t pos) { dataPos.push_back(pos); }; - sampleLoop(op, size); - - switch (slotType) { - case SlotDef::VECTOR_DENSE: { - Matrix::resizeOrCreate(cpuArguments[slot].value, - size, - dim, - false, // trans = false - false); // useGpu = false - real* buf = cpuArguments[slot].value->getData(); - for (int i = 0; i < size; ++i) { - memcpy(buf + i * dim, - slots_[slot].denseData.data() + dataPos[i] * dim, - sizeof(real) * dim); - } - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - if (!(cpuArguments[slot].value)) { - cpuArguments[slot].value = - Matrix::createSparseMatrix(size, - dim, - size /*DEFAULT_AVG_WIDTH = 1*/, - NO_VALUE, - SPARSE_CSR, - false, - useGpu_); - } - auto mat = cpuArguments[slot].value; - mat->resize(size, dim); - if (std::dynamic_pointer_cast(mat)) { - std::dynamic_pointer_cast(mat)->copyFrom( - dataPos.data(), - slots_[slot].indices.data(), - slots_[slot].sparseNonValueData.data(), - HPPL_STREAM_1); - } else if (std::dynamic_pointer_cast(mat)) { - std::dynamic_pointer_cast(mat)->copyFrom( - dataPos.data(), - slots_[slot].indices.data(), - slots_[slot].sparseNonValueData.data()); - } else { - LOG(FATAL) << "Not Supported"; - } - size_t numElements = 0; - for (auto pos : dataPos) { - numElements += - slots_[slot].indices[pos + 1] - slots_[slot].indices[pos]; - } - nnzStats_[slot]->addSample(numElements); - - break; - } - case SlotDef::VECTOR_SPARSE_VALUE: { - if (!(cpuArguments[slot].value)) { - cpuArguments[slot].value = - Matrix::createSparseMatrix(size, - dim, - size /*DEFAULT_AVG_WIDTH = 1*/, - FLOAT_VALUE, - SPARSE_CSR, - false, - useGpu_); - } - auto mat = cpuArguments[slot].value; - mat->resize(size, dim); - if (std::dynamic_pointer_cast(mat)) { - std::dynamic_pointer_cast(mat)->copyFrom( - dataPos.data(), - slots_[slot].indices.data(), - slots_[slot].sparseFloatValueData.data(), - HPPL_STREAM_1); - } else if (std::dynamic_pointer_cast(mat)) { - std::dynamic_pointer_cast(mat)->copyFrom( - dataPos.data(), - slots_[slot].indices.data(), - slots_[slot].sparseFloatValueData.data()); - } else { - LOG(FATAL) << "Not Supported"; - } - break; - } - case SlotDef::INDEX: { - IVector::resizeOrCreate(cpuArguments[slot].ids, - size, - /* useGpu= */ false); - int* buf = cpuArguments[slot].ids->getData(); - for (int i = 0; i < size; ++i) { - buf[i] = slots_[slot].indexData[dataPos[i]]; - } - break; - } - case SlotDef::VAR_MDIM_DENSE: { - CHECK_EQ(size, 1); - auto mat = cpuArguments[slot].value; - size_t totalDim = slots_[slot].varDenseData[dataPos[0]].data.size(); - - CHECK_EQ(slots_[slot].varDenseData[dataPos[0]].dims.size(), size_t(3)); - size_t height, width, depth, oldWidth; - /* dims[2] is depth, will be changed to dims[0] in future */ - depth = slots_[slot].varDenseData[dataPos[0]].dims[2]; - height = slots_[slot].varDenseData[dataPos[0]].dims[1]; - width = slots_[slot].varDenseData[dataPos[0]].dims[0]; - oldWidth = width; - /* process the undesirable sample */ - if (oldWidth < height) { - width = height; - } - cpuArguments[slot].setFrameHeight(height); - cpuArguments[slot].setFrameWidth(width); - - if (oldWidth < height) { - totalDim = width * height * depth; - } - Matrix::resizeOrCreate(cpuArguments[slot].value, - size, - totalDim, - false, // trans = false - false); // useGpu = false - real* buf = cpuArguments[slot].value->getData(); - cpuArguments[slot].value->zeroMem(); - if (oldWidth < height) { - real* srcBuf = slots_[slot].varDenseData[dataPos[0]].data.data(); - for (size_t i = 0; i < depth; i++) { - for (size_t j = 0; j < height; j++) { - for (size_t k = 0; k < oldWidth; k++) { - buf[i * height * width + j * width + k] = - srcBuf[i * height * oldWidth + j * oldWidth + k]; - } - } - } - } else { - memcpy(buf, - slots_[slot].varDenseData[dataPos[0]].data.data(), - sizeof(real) * totalDim); - } - ICpuGpuVector::resizeOrCreate(cpuArguments[slot].sequenceStartPositions, - size + 1, /* size == 1 currently */ - /* useGpu= */ false); - int* bufStarts = - cpuArguments[slot].sequenceStartPositions->getMutableData(false); - bufStarts[0] = 0; - bufStarts[1] = 1; - break; - } - case SlotDef::VAR_MDIM_INDEX: { - CHECK_EQ(size, 1); - size_t totalDim = slots_[slot].varIndices[dataPos[0]].size(); - IVector::resizeOrCreate(cpuArguments[slot].ids, - totalDim, - /* useGpu= */ false); - int* buf = cpuArguments[slot].ids->getData(); - memcpy(buf, - slots_[slot].varIndices[dataPos[0]].data(), - sizeof(int) * totalDim); - - ICpuGpuVector::resizeOrCreate(cpuArguments[slot].sequenceStartPositions, - size + 1, /* size == 1 currently */ - /* useGpu= */ false); - int* bufStarts = - cpuArguments[slot].sequenceStartPositions->getMutableData(false); - bufStarts[0] = 0; - /* we expand the convolutinal feature map to a sequence data, - * so there should be a corresponding sequence labels */ - bufStarts[1] = totalDim; - break; - } - case SlotDef::STRING: { - if (cpuArguments[slot].strs) { - cpuArguments[slot].strs->resize(size); - } else { - cpuArguments[slot].strs = - std::make_shared>(size); - } - for (int i = 0; i < size; ++i) { - (*cpuArguments[slot].strs)[i] = slots_[slot].strData[dataPos[i]]; - } - break; - } - } - } - - if (useGpu_) { - std::vector& cpuArguments = cpuBatch.getStreams(); - DataBatch& gpuBatch = *gpuBatch_; - std::vector& gpuArguments = gpuBatch.getStreams(); - gpuArguments.resize(cpuArguments.size()); - gpuBatch.setSize(size); - for (int i = 0; i < header_.slot_defs_size(); ++i) { - SlotDef::SlotType slotType = header_.slot_defs(i).type(); - if (SlotDef::VECTOR_SPARSE_VALUE == slotType || - SlotDef::VECTOR_SPARSE_NON_VALUE == slotType) { - gpuArguments[i] = cpuArguments[i]; - gpuArguments[i].sequenceStartPositions = - cpuArguments[i].sequenceStartPositions; - } else { - gpuArguments[i].resizeAndCopyFrom( - cpuArguments[i], useGpu_, HPPL_STREAM_1); - } - } - hl_stream_synchronize(HPPL_STREAM_1); - *batch = gpuBatch; - } else { - *batch = cpuBatch; - } - - currentSequenceIndex_ += numScannedSeqs; - - return batch->getSize(); -} - -ProtoSequenceDataProvider::ProtoSequenceDataProvider(const DataConfig& config, - bool useGpu, - bool loadDataAll) - : ProtoDataProvider(config, useGpu, loadDataAll) {} - -int64_t ProtoSequenceDataProvider::getNextBatchInternal(int64_t size, - DataBatch* batch) { - CHECK(iidData()) << "ProtoSequenceDataProvider only accepts iid data"; - int64_t numSequences = 0; // actual number of sequences in the batch - - // the number of sequences scanned, including those skipped because too long - int64_t numScannedSeqs = 0; - std::lock_guard guard(lock_); - size = std::min(getSize() - currentSequenceIndex_, size); - numScannedSeqs = numSequences = size; - if (size <= 0) return 0; - - DataBatch& cpuBatch = *cpuBatch_; - std::vector& cpuArguments = cpuBatch.getStreams(); - cpuBatch.setSize(size); - cpuArguments.resize(header_.slot_defs_size()); - - for (int slot = 0; slot < header_.slot_defs_size(); ++slot) { - SlotDef::SlotType slotType = header_.slot_defs(slot).type(); - - std::vector dataPos; - dataPos.reserve(size); - auto op = [this, &dataPos](int64_t pos) { dataPos.push_back(pos); }; - sampleLoop(op, size); - - // current slot: sequenceStartPositions - ICpuGpuVector::resizeOrCreate(cpuArguments[slot].sequenceStartPositions, - size + 1, - /* useGpu= */ false); - - switch (slotType) { - case SlotDef::VECTOR_SPARSE_VALUE: - case SlotDef::VAR_MDIM_DENSE: - case SlotDef::VAR_MDIM_INDEX: { - LOG(FATAL) << "ProtoSequenceDataProvider only support" - << " VECTOR_DENSE, VECTOR_SPARSE_NON_VALUE and INDEX slots"; - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - // copy to IDS, not value - // pointers used in current slot - sparse_non_value_t* data = slots_[slot].sparseNonValueData.data(); - int64_t* indexs = slots_[slot].indices.data(); - int64_t* seqs = dataPos.data(); - - // current slot: i need size instances. what is the total length? - int totalFeatureInCurrentSlot = 0; - for (int ins = 0; ins < size; ins++) { - int64_t currInsId = seqs[ins]; - totalFeatureInCurrentSlot += - indexs[currInsId + 1] - indexs[currInsId]; - // special: if current instance has NO feature in current slot - if (indexs[currInsId + 1] == indexs[currInsId]) { - totalFeatureInCurrentSlot++; - } - } - // done - - // current slot: ids - IVector::resizeOrCreate(cpuArguments[slot].ids, - totalFeatureInCurrentSlot, - /* useGpu= */ false); - - // where to write - int* currPosOfArgumentId = cpuArguments[slot].ids->getData(); - int* currPosOfArgumentSeqStart = - cpuArguments[slot].sequenceStartPositions->getMutableData(false); - int allSequenceLength = 0; - currPosOfArgumentSeqStart[0] = 0; - // for each instance, copy data and fill sequence positions - for (int instance = 0; instance < size; instance++) { - int64_t currInstanceId = seqs[instance]; - int64_t currInstanceLength = - indexs[currInstanceId + 1] - indexs[currInstanceId]; - sparse_non_value_t* currInstanceData = data + indexs[currInstanceId]; - // write sequenceStartPositions - allSequenceLength += currInstanceLength; - currPosOfArgumentSeqStart[instance + 1] = allSequenceLength; - // copy features - for (int featCopier = 0; featCopier < currInstanceLength; - featCopier++) { - currPosOfArgumentId[featCopier] = currInstanceData[featCopier].col; - } - currPosOfArgumentId += currInstanceLength; - // special: if current instance has NO feature in current slot - if (currInstanceLength == 0) { - allSequenceLength++; - currPosOfArgumentSeqStart[instance + 1] = allSequenceLength; - currPosOfArgumentId[0] = -1; - currPosOfArgumentId++; - } - // done - } - if (slots_[slot].subIndices.size()) { - std::vector dataSubPos; - auto op = [this, &dataSubPos](int64_t pos) { - dataSubPos.push_back(pos); - }; - int subSize = subSampleLoop(op, size, slot); - ICpuGpuVector::resizeOrCreate( - cpuArguments[slot].subSequenceStartPositions, subSize + 1, false); - int* currPosOfArgumentSubSeqStart = - cpuArguments[slot].subSequenceStartPositions->getMutableData( - false); - int64_t* subSeqs = dataSubPos.data(); - int64_t* subIndexs = slots_[slot].subIndices.data(); - int allSubSequenceLength = 0; - currPosOfArgumentSubSeqStart[0] = 0; - // for each instance, compute sub-sequence number - for (int instance = 0; instance < subSize; instance++) { - int64_t currSubInstanceId = subSeqs[instance]; - int64_t currSubInstanceLength = - subIndexs[currSubInstanceId + 1] - subIndexs[currSubInstanceId]; - // write subSequenceStartPositions - allSubSequenceLength += currSubInstanceLength; - currPosOfArgumentSubSeqStart[instance + 1] = allSubSequenceLength; - // special: if current instance has NO feature in current slot - if (currSubInstanceLength == 0) { - allSubSequenceLength++; - currPosOfArgumentSubSeqStart[instance + 1] = allSubSequenceLength; - } - } - cpuArguments[slot].checkSubset(); - } - break; - } - case SlotDef::INDEX: { - // label slot - IVector::resizeOrCreate(cpuArguments[slot].ids, - size, - /* useGpu= */ false); - // fill labels - int* buf = cpuArguments[slot].ids->getData(); - for (int i = 0; i < size; ++i) { - buf[i] = slots_[slot].indexData[dataPos[i]]; - } - // label HAS sequence structure - cpuArguments[slot].sequenceStartPositions->fillSequence(false); - break; - } - case SlotDef::VECTOR_DENSE: { - // copy values - size_t dim = header_.slot_defs(slot).dim(); - Matrix::resizeOrCreate(cpuArguments[slot].value, - size, - dim, - false, // trans = false - false); // useGpu = false - real* buf = cpuArguments[slot].value->getData(); - for (int i = 0; i < size; ++i) { - memcpy(buf + i * dim, - slots_[slot].denseData.data() + dataPos[i] * dim, - sizeof(real) * dim); - } - // sequence structure - cpuArguments[slot].sequenceStartPositions->fillSequence(false); - break; - } - default: { LOG(FATAL) << "should not reach here"; } - } - } - - if (useGpu_) { - std::vector& cpuArguments = cpuBatch.getStreams(); - DataBatch& gpuBatch = *gpuBatch_; - std::vector& gpuArguments = gpuBatch.getStreams(); - gpuArguments.resize(cpuArguments.size()); - gpuBatch.setSize(size); - for (size_t i = 0; i < cpuArguments.size(); ++i) { - gpuArguments[i].resizeAndCopyFrom( - cpuArguments[i], useGpu_, HPPL_STREAM_1); - } - hl_stream_synchronize(HPPL_STREAM_1); - *batch = gpuBatch; - } else { - *batch = cpuBatch; - } - - currentSequenceIndex_ += numScannedSeqs; - return batch->getSize(); -} - -} // namespace paddle diff --git a/paddle/gserver/dataproviders/ProtoDataProvider.h b/paddle/gserver/dataproviders/ProtoDataProvider.h deleted file mode 100644 index 7dd45e0622..0000000000 --- a/paddle/gserver/dataproviders/ProtoDataProvider.h +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include - -#include "DataFormat.pb.h" -#include "paddle/utils/Stat.h" - -#include "DataProvider.h" -#include "ProtoReader.h" - -namespace paddle { - -/** - * @brief Provider data from protobuf data file with each sample - * specified by proto message - * - * DataSample defined in DataFormat.proto. - * - * The file format is - * - * header - * - * sample1 - * - * sample2 - * - * ... - * - * sampleN - * - * @note: In the data file, each message is prefixed with its length. - * The read/write of the protbuf are implemented in ProtoReader.h - */ -class ProtoDataProvider : public DataProvider { -public: - ProtoDataProvider(const DataConfig& config, - bool useGpu, - bool loadDataAll = true); - virtual void reset(); - - /** - * @note this size includes the sequences which are skipped because they - * are longer than the batch size. - */ - virtual int64_t getSize() { - int64_t size = sampleNums_; - if (usageRatio_ < 1.0f) { - size = static_cast(size * usageRatio_); - } - return size; - } - virtual void shuffle(); - - void loadData(const std::vector& fileList); - - virtual int64_t getNextBatchInternal(int64_t size, DataBatch* batch); - -protected: - /** - * @brief load protobuf data from a list of file - * @param[in] fileName file name of a file which contains - * a list of file names - */ - void loadData(const std::string& fileName); - - /** - * @brief load protobuf data from file - * @param[in] fileName data file name - */ - void loadDataFile(const std::string& fileName); - /** @brief check data header of each data sample - * @param[in] header data header read from protobuf data - */ - void checkDataHeader(const DataHeader& header); - /** - * @brief fill protobuf data into slot_, - * slot_ is a vector of ProtoSlot in memory. - * @param[in] sample data sample read from protobuf data - */ - void fillSlots(const DataSample& sample); - - /** - * @brief return true if each sample is one sequence, i.e., independent - * of other samples. - */ - inline bool iidData() const { return sequenceStartPositions_.empty(); } - - /** - * @brief check that sample is consistent with header_ - */ - void checkSample(const DataSample& sample); - - template - int64_t sequenceLoop(Op op, int64_t size); - - template - int64_t sampleLoop(Op op, int64_t size); - - template - int64_t subSampleLoop(Op op, int64_t size, int slot); - - void showDataStats(); - -protected: - struct ProtoVarSlot { - std::vector data; - std::vector dims; - }; - - struct ProtoSlot { - SlotDef::SlotType type; - int dim; - std::vector indexData; - std::vector denseData; - std::vector sparseNonValueData; - std::vector sparseFloatValueData; - std::vector indices; - std::vector subIndices; - - std::vector varDenseData; - std::vector> varIndices; - std::vector strData; - }; - DataHeader header_; - int numVecSlots_; - - std::vector slots_; - size_t sampleNums_; - - /** - * The starting position of each sequence in samples. - * The last element should be num of samples. - * If empty, each sample is one sequence. - */ - std::vector sequenceStartPositions_; - - int64_t currentSequenceIndex_; - - // The size should be the number of sequences. - std::vector shuffledSequenceIds_; - - ThreadLocalD cpuBatch_; - ThreadLocalD gpuBatch_; - - RWLock lock_; - std::vector nnzStats_; // stats for number of none-zeros entries -}; - -/** - * @brief Special use for Proto data: instances should contain sparse-non-value - * slots - * and label. - * - * @note ProtoSequenceDataProvider treats each SPARSE SLOT as a SEQUENCE - */ -class ProtoSequenceDataProvider : public ProtoDataProvider { -public: - ProtoSequenceDataProvider(const DataConfig& config, - bool useGpu, - bool loadDataAll = true); - ~ProtoSequenceDataProvider() {} - virtual int64_t getNextBatchInternal(int64_t size, DataBatch* batch); -}; - -} // namespace paddle diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index aa94ee406e..232fa01568 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -58,17 +58,6 @@ if(NOT WITH_DOUBLE) endif() if(NOT MOBILE_INFERENCE) -################### test_ProtoDataProvider ############ - add_unittest_without_exec(test_ProtoDataProvider - test_ProtoDataProvider.cpp) - - # test_ProtoDataProvider will mkdir as same name, - # so if WORKING_DIRECTORY is default directory, then - # mkdir will get error. - add_test(NAME test_ProtoDataProvider - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_ProtoDataProvider - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle) - ################## test_Evaluator ####################### add_unittest(test_Evaluator test_Evaluator.cpp) diff --git a/paddle/gserver/tests/proto_files.txt b/paddle/gserver/tests/proto_files.txt deleted file mode 100644 index 691b38c794..0000000000 --- a/paddle/gserver/tests/proto_files.txt +++ /dev/null @@ -1,2 +0,0 @@ -./test_ProtoDataProvider/data1.bin -./test_ProtoDataProvider/data2.bin diff --git a/paddle/gserver/tests/proto_files_compressed.txt b/paddle/gserver/tests/proto_files_compressed.txt deleted file mode 100644 index 7413c81e18..0000000000 --- a/paddle/gserver/tests/proto_files_compressed.txt +++ /dev/null @@ -1,2 +0,0 @@ -./test_ProtoDataProvider/data1.bin.gz -./test_ProtoDataProvider/data2.bin.gz diff --git a/paddle/gserver/tests/test_ProtoDataProvider.cpp b/paddle/gserver/tests/test_ProtoDataProvider.cpp deleted file mode 100644 index af6472619d..0000000000 --- a/paddle/gserver/tests/test_ProtoDataProvider.cpp +++ /dev/null @@ -1,732 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include -#include - -#include - -#include "paddle/gserver/dataproviders/ProtoDataProvider.h" -#include "paddle/utils/Util.h" - -#include "paddle/testing/TestUtil.h" - -using namespace std; // NOLINT - -std::vector protoFiles{ - "./test_ProtoDataProvider/data1.bin", "./test_ProtoDataProvider/data2.bin", -}; -std::vector protoFilesCompressed{ - "./test_ProtoDataProvider/data1.bin.gz", - "./test_ProtoDataProvider/data2.bin.gz", -}; - -const char* kTestDir = "./test_ProtoDataProvider"; -const char kProtoFileList[] = "gserver/tests/proto_files.txt"; -const char kProtoFileListCompressed[] = - "gserver/tests/proto_files_compressed.txt"; -const int kSpraseMatrixDim = 1024; - -using namespace paddle; // NOLINT - -void prepareData(DataBatch* batch, - const int* numPerSlotType, - bool iid, - bool useGpu) { - batch->clear(); - int64_t size = uniformRandom(100) + 10; - batch->setSize(size); - - ICpuGpuVectorPtr sequenceStartPositions; - ICpuGpuVectorPtr subSequenceStartPositions; - if (!iid) { - int numSeqs = uniformRandom(10) + 1; - sequenceStartPositions = - ICpuGpuVector::create(numSeqs + 1, /* useGpu= */ false); - int* buf = sequenceStartPositions->getMutableData(false); - subSequenceStartPositions = - ICpuGpuVector::create(numSeqs + 1, /* useGpu= */ false); - int* subBuf = subSequenceStartPositions->getMutableData(false); - int64_t pos = 0; - int maxLen = 2 * size / numSeqs; - for (int i = 0; i < numSeqs; ++i) { - int len = - uniformRandom(min(maxLen, size - pos - numSeqs + i)) + 1; - buf[i] = pos; - subBuf[i] = pos; - pos += len; - VLOG(1) << " len=" << len; - } - buf[numSeqs] = size; - subBuf[numSeqs] = size; - } - - vector& arguments = batch->getStreams(); - for (int i = 0; i < numPerSlotType[SlotDef::VECTOR_DENSE]; ++i) { - int64_t dim = rand() % 10 + 4; // NOLINT rand_r - MatrixPtr mat = Matrix::create(size, dim, /* trans= */ false, false); - mat->randomizeUniform(); - Argument arg; - arg.value = mat; - arg.sequenceStartPositions = sequenceStartPositions; - arguments.push_back(arg); - } - for (int i = 0; i < numPerSlotType[SlotDef::VECTOR_SPARSE_NON_VALUE]; ++i) { - MatrixPtr mat = - makeRandomSparseMatrix(size, kSpraseMatrixDim, false, useGpu); - Argument arg; - arg.value = mat; - arg.sequenceStartPositions = sequenceStartPositions; - arg.subSequenceStartPositions = subSequenceStartPositions; - arguments.push_back(arg); - } - for (int i = 0; i < numPerSlotType[SlotDef::VECTOR_SPARSE_VALUE]; ++i) { - MatrixPtr mat = - makeRandomSparseMatrix(size, kSpraseMatrixDim, true, useGpu); - Argument arg; - arg.value = mat; - arg.sequenceStartPositions = sequenceStartPositions; - arguments.push_back(arg); - } - for (int i = 0; i < numPerSlotType[SlotDef::STRING]; ++i) { - int64_t dim = rand() % 10 + 4; // NOLINT rand_r - SVectorPtr vec = std::make_shared>(); - for (int j = 0; j < size; ++j) { - vec->push_back(randStr(dim)); - } - Argument arg; - arg.strs = vec; - arg.sequenceStartPositions = sequenceStartPositions; - arguments.push_back(arg); - } - for (int i = 0; i < numPerSlotType[SlotDef::INDEX]; ++i) { - int64_t dim = rand() % 10 + 4; // NOLINT rand_r - IVectorPtr vec = IVector::create(size, /* useGpu= */ false); - int* buf = vec->getData(); - for (int j = 0; j < size; ++j) { - buf[j] = uniformRandom(dim); - } - Argument arg; - arg.ids = vec; - arg.sequenceStartPositions = sequenceStartPositions; - arguments.push_back(arg); - } -} - -inline int getSlotDim(const Argument& arg) { - if (arg.value) { - return arg.value->getWidth(); - } else if (arg.ids) { - return arg.ids->getMax() + 1; - } else if (arg.strs) { - return 1; - } - LOG(FATAL) << "Invalid argument"; - return 0; -} - -inline SlotDef::SlotType getSlotType(const Argument& arg) { - if (arg.value) { - auto& m = *arg.value; - auto& type = typeid(m); - if (type == typeid(CpuMatrix) || type == typeid(GpuMatrix)) { - return SlotDef::VECTOR_DENSE; - } - if (type == typeid(CpuSparseMatrix)) { - auto valueType = - std::dynamic_pointer_cast(arg.value)->getValueType(); - if (NO_VALUE == valueType) { - return SlotDef::VECTOR_SPARSE_NON_VALUE; - } else { - return SlotDef::VECTOR_SPARSE_VALUE; - } - } - if (type == typeid(GpuSparseMatrix)) { - auto valueType = - std::dynamic_pointer_cast(arg.value)->getValueType(); - if (NO_VALUE == valueType) { - return SlotDef::VECTOR_SPARSE_NON_VALUE; - } else { - return SlotDef::VECTOR_SPARSE_VALUE; - } - } - - LOG(FATAL) << "Unknown matrix type"; - } - if (arg.ids) return SlotDef::INDEX; - if (arg.strs) return SlotDef::STRING; - LOG(FATAL) << "Invalid argument"; - return SlotDef::VECTOR_DENSE; -} - -void getColRow(const Argument& arg, - int64_t pos, - bool useGpu, - int* colNum, - const int** rowCols, - const real** rowValues) { - SlotDef::SlotType type = getSlotType(arg); - GpuSparseMatrixPtr matGpu; - CpuSparseMatrixPtr matCpu; - if (useGpu) { - matGpu = dynamic_pointer_cast(arg.value); - ASSERT_TRUE(matGpu != NULL); - } else { - matCpu = dynamic_pointer_cast(arg.value); - ASSERT_TRUE(matCpu != NULL); - } - *colNum = useGpu ? matGpu->getColNum(pos) : matCpu->getColNum(pos); - *rowCols = useGpu ? matGpu->getRowCols(pos) : matCpu->getRowCols(pos); - if (type == SlotDef::VECTOR_SPARSE_VALUE) { - *rowValues = useGpu ? matGpu->getRowValues(pos) : matCpu->getRowValues(pos); - } else { - *rowValues = NULL; - } -} - -void makeSample(const vector& arguments, - int64_t pos, - bool isBeginning, - DataSample* sample, - bool useGpu) { - sample->set_is_beginning(isBeginning); - int slotid = 0; - for (auto& arg : arguments) { - SlotDef::SlotType type = getSlotType(arg); - int64_t dim = getSlotDim(arg); - switch (type) { - case SlotDef::VECTOR_DENSE: { - VectorSlot* vecSlot = sample->add_vector_slots(); - auto values = vecSlot->mutable_values(); - values->Reserve(dim); - for (int i = 0; i < dim; ++i) { - values->AddAlreadyReserved( - static_cast(arg.value->getElement(pos, i))); - } - break; - } - case SlotDef::INDEX: { - sample->add_id_slots(arg.ids->get(pos)); - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - VectorSlot* vecSlot = sample->add_vector_slots(); - auto ids = vecSlot->mutable_ids(); - int colNum; - const int* rowCols; - const real* rowValues; // nullptr - getColRow(arg, pos, useGpu, &colNum, &rowCols, &rowValues); - ids->Reserve(colNum); - for (int i = 0; i < colNum; ++i) { - ids->AddAlreadyReserved(rowCols[i]); - } - SubseqSlot* subseqSlot = sample->add_subseq_slots(); // subseq - subseqSlot->set_slot_id(slotid); - auto lens = subseqSlot->mutable_lens(); - lens->Add(colNum); - break; - } - case SlotDef::VECTOR_SPARSE_VALUE: { - VectorSlot* vecSlot = sample->add_vector_slots(); - auto values = vecSlot->mutable_values(); - auto ids = vecSlot->mutable_ids(); - int colNum; - const int* rowCols; - const real* rowValues; - getColRow(arg, pos, useGpu, &colNum, &rowCols, &rowValues); - ids->Reserve(colNum); - values->Reserve(colNum); - for (int i = 0; i < colNum; ++i) { - ids->AddAlreadyReserved(rowCols[i]); - values->AddAlreadyReserved(rowValues[i]); - } - break; - } - case SlotDef::VAR_MDIM_DENSE: - case SlotDef::VAR_MDIM_INDEX: { - LOG(FATAL) << "Not implemented"; - break; - } - case SlotDef::STRING: { - VectorSlot* vecSlot = sample->add_vector_slots(); - vecSlot->add_strs((*arg.strs)[pos]); - break; - } - } - slotid++; - } -} - -void writeData(const DataBatch& batch, bool useGpu, bool dataCompression) { - DataHeader header; - const vector& arguments = batch.getStreams(); - for (auto& argument : arguments) { - SlotDef* slotDef = header.add_slot_defs(); - slotDef->set_type(getSlotType(argument)); - slotDef->set_dim(getSlotDim(argument)); - } - VLOG(1) << "header=" << header.DebugString(); - - int64_t totalSeqs = batch.getNumSequences(); - int64_t seq = 0; - ICpuGpuVectorPtr sequenceStartPositions = arguments[0].sequenceStartPositions; - int64_t numWritten = 0; - vector curProtoFiles = - dataCompression ? protoFilesCompressed : protoFiles; - for (size_t i = 0; i < curProtoFiles.size(); ++i) { - int64_t numSeqs = totalSeqs * (i + 1) / curProtoFiles.size() - - totalSeqs * i / curProtoFiles.size(); - ofstream os(curProtoFiles[i]); - CHECK(os) << "Fail to open " << curProtoFiles[i]; - unique_ptr writer(new ProtoWriter(&os, dataCompression)); - CHECK(writer->write(header)); - for (int j = 0; j < numSeqs; ++j, ++seq) { - int64_t begin = seq; - int64_t end = seq + 1; - if (sequenceStartPositions) { - begin = sequenceStartPositions->getElement(seq); - end = sequenceStartPositions->getElement(seq + 1); - } - for (int pos = begin; pos < end; ++pos) { - DataSample sample; - makeSample(arguments, pos, pos == begin, &sample, useGpu); - CHECK(writer->write(sample)); - ++numWritten; - } - } - - writer.reset(nullptr); - os.close(); - } - CHECK_EQ(arguments[0].getBatchSize(), numWritten); -} - -// check that the sample at pos1 in args1 is same as the sample at pos2 in args2 -void checkSample(const vector& args1, - int64_t pos1, - const vector& args2, - int64_t pos2, - bool useGpu) { - EXPECT_EQ(args1.size(), args2.size()); - VLOG(1) << " pos1=" << pos1 << " pos2=" << pos2; - - for (size_t i = 0; i < args1.size(); ++i) { - auto type = getSlotType(args1[i]); - int dim = getSlotDim(args1[i]); - EXPECT_EQ(type, getSlotType(args2[i])); - if (type == SlotDef::INDEX) { - EXPECT_GE(dim, getSlotDim(args2[i])); - } else { - EXPECT_EQ(dim, getSlotDim(args2[i])); - } - switch (type) { - case SlotDef::VECTOR_DENSE: { - for (int j = 0; j < dim; ++j) { - EXPECT_EQ(static_cast(args1[i].value->getElement(pos1, j)), - static_cast(args2[i].value->getElement(pos2, j))); - } - break; - } - case SlotDef::INDEX: { - EXPECT_EQ(args1[i].ids->get(pos1), args2[i].ids->get(pos2)); - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: - case SlotDef::VECTOR_SPARSE_VALUE: { - int colNum1, colNum2; - const int *rowCols1, *rowCols2; - const real *rowValues1, *rowValues2; - getColRow(args1[i], pos1, useGpu, &colNum1, &rowCols1, &rowValues1); - getColRow(args2[i], pos2, useGpu, &colNum2, &rowCols2, &rowValues2); - EXPECT_EQ(colNum1, colNum2); - for (int j = 0; j < colNum1; ++j) { - EXPECT_EQ(rowCols1[j], rowCols2[j]); - if (type == SlotDef::VECTOR_SPARSE_VALUE) { - EXPECT_EQ(rowValues1[j], rowValues2[j]); - } - } - break; - } - case SlotDef::VAR_MDIM_DENSE: - case SlotDef::VAR_MDIM_INDEX: { - LOG(FATAL) << "Not implemented"; - break; - } - case SlotDef::STRING: { - EXPECT_EQ((*args1[i].strs)[pos1], (*args2[i].strs)[pos2]); - break; - } - } - } -} - -void testProtoDataProvider(int* numPerSlotType, - bool iid, - bool async, - bool useGpu, - bool dataCompression, - int numConstantSlots = 0) { - mkDir(kTestDir); - DataBatch data; - - prepareData(&data, numPerSlotType, iid, useGpu); - writeData(data, useGpu, dataCompression); - - DataConfig config; - config.set_type("proto"); - config.set_files(dataCompression ? kProtoFileListCompressed : kProtoFileList); - config.set_async_load_data(async); - - for (int i = 0; i < numConstantSlots; ++i) { - config.add_constant_slots(i + 11); - MatrixPtr w = Matrix::create(data.getSize(), - 1, - /* trans= */ false, - /* useGpu= */ false); - w->assign(config.constant_slots(i)); - data.appendData(w); - } - - unique_ptr dataProvider(DataProvider::create(config, useGpu)); - dataProvider->setSkipShuffle(); - - EXPECT_EQ(data.getSize(), dataProvider->getSize()); - - int64_t batchSize = 10; - DataBatch batch; - - size_t seq1 = 0; - vector& args1 = data.getStreams(); - ICpuGpuVectorPtr sequenceStartPositions1 = args1[0].sequenceStartPositions; - - dataProvider->reset(); - - while (dataProvider->getNextBatch(batchSize, &batch) > 0) { - CHECK_EQ(data.getNumStreams(), batch.getNumStreams()); - vector& args2 = batch.getStreams(); - ICpuGpuVectorPtr sequenceStartPositions2 = args2[0].sequenceStartPositions; - for (auto& arg : args2) { - EXPECT_EQ(iid, !arg.sequenceStartPositions); - } - size_t numSeqs = batch.getNumSequences(); - VLOG(1) << "numSeqs=" << numSeqs; - for (size_t seq2 = 0; seq2 < numSeqs; ++seq1, ++seq2) { - int64_t begin1 = seq1; - int64_t end1 = seq1 + 1; - if (sequenceStartPositions1) { - begin1 = sequenceStartPositions1->getElement(seq1); - end1 = sequenceStartPositions1->getElement(seq1 + 1); - EXPECT_LT(seq1, sequenceStartPositions1->getSize() - 1); - } - - int64_t begin2 = seq2; - int64_t end2 = seq2 + 1; - if (sequenceStartPositions2) { - begin2 = sequenceStartPositions2->getElement(seq2); - end2 = sequenceStartPositions2->getElement(seq2 + 1); - } - VLOG(1) << " begin1=" << begin1 << " end1=" << end1 - << " begin2=" << begin2 << " end2=" << end2; - EXPECT_EQ(end1 - begin1, end2 - begin2); - for (int i = 0; i < end1 - begin1; ++i) { - checkSample(args1, begin1 + i, args2, begin2 + i, useGpu); - } - } - } - - EXPECT_EQ(seq1, (size_t)data.getNumSequences()); - rmDir(kTestDir); -} - -TEST(ProtoDataProvider, test) { - int numSlotsArray[] = {0, 3}; - int numTwoArray[] = {0, 1}; - int numSlotsArraySize = sizeof(numSlotsArray) / sizeof(numSlotsArray[0]); - const int numSlot = 5; - int combination[numSlot] = {0}; - int k = numSlot - 1; - while (k >= 0) { - int numDenseVecSlots = numSlotsArray[combination[0]]; - int numSparseNonValueVecSlots = numSlotsArray[combination[1]]; - int numSparseValueVectorSlots = numSlotsArray[combination[2]]; - int numStrSlots = numSlotsArray[combination[3]]; - int numIdSlots = numSlotsArray[combination[4]]; - // while loop : traverse all cases - k = numSlot - 1; - while (k >= 0) { - if (combination[k] < (numSlotsArraySize - 1)) { - ++combination[k]; - break; - } else { - combination[k] = 0; - --k; - } - } - if (numDenseVecSlots + numSparseNonValueVecSlots + - numSparseValueVectorSlots + numStrSlots + numIdSlots < - 1) - continue; - for (int iid : numTwoArray) { - for (int async : numTwoArray) { - for (int useGpu : numTwoArray) { - for (int dataCompression : numTwoArray) { - if (async && useGpu) { - // Currently in async mode, useGpu is not supported - continue; - } -#ifndef PADDLE_WITH_CUDA - if (useGpu) { - continue; - } -#endif - LOG(INFO) << " numDenseVecSlots=" << numDenseVecSlots - << " numSparseNonValueVecSlots=" - << numSparseNonValueVecSlots - << " numSparseValueVectorSlots=" - << numSparseValueVectorSlots - << " numStrSlots=" << numStrSlots - << " numIdSlots=" << numIdSlots << " iid=" << iid - << " async=" << async << " useGpu=" << useGpu - << " dataCompression=" << dataCompression; - int numPerSlotType[SlotDef::SlotType_ARRAYSIZE] = {0}; - numPerSlotType[SlotDef::VECTOR_DENSE] = numDenseVecSlots; - numPerSlotType[SlotDef::VECTOR_SPARSE_NON_VALUE] = - numSparseNonValueVecSlots; - numPerSlotType[SlotDef::VECTOR_SPARSE_VALUE] = - numSparseValueVectorSlots; - numPerSlotType[SlotDef::INDEX] = numIdSlots; - numPerSlotType[SlotDef::STRING] = numStrSlots; - testProtoDataProvider( - numPerSlotType, iid, async, useGpu, dataCompression); - } // end for (int dataCompression : numTwoArray) - } // end for (int useGpu : numTwoArray) - } // end for (int async : numTwoArray) - } // end for (int iid : numTwoArray) - } // end for (while, traverse all slots) -} - -TEST(ProtoDataProvider, constant_slots) { - int numSlotsArray[] = {0, 3}; - int numTwoArray[] = {0, 1}; - for (int numDenseVecSlots : numSlotsArray) { - for (int numSparseNonValueVecSlots : numSlotsArray) { - if (numDenseVecSlots + numSparseNonValueVecSlots < 1) continue; - for (int numConstantSlots : {1, 2}) { - for (int useGpu : numTwoArray) { - for (int dataCompression : numTwoArray) { -#ifndef PADDLE_WITH_CUDA - if (useGpu) { - continue; - } -#endif - LOG(INFO) << " numDenseVecSlots=" << numDenseVecSlots - << " numSparseNonValueVecSlots=" - << numSparseNonValueVecSlots - << " numConstantSlogs=" << numConstantSlots - << " useGpu=" << useGpu - << " dataCompression=" << dataCompression; - int numPerSlotType[SlotDef::SlotType_ARRAYSIZE] = {0}; - numPerSlotType[SlotDef::VECTOR_DENSE] = numDenseVecSlots; - numPerSlotType[SlotDef::VECTOR_SPARSE_NON_VALUE] = - numSparseNonValueVecSlots; - numPerSlotType[SlotDef::VECTOR_SPARSE_VALUE] = 1; - numPerSlotType[SlotDef::INDEX] = 1; - testProtoDataProvider(numPerSlotType, - /* iid= */ true, - /* async= */ false, - useGpu, - dataCompression, - numConstantSlots); - } // end for (int dataCompression : numTwoArray) - } // end for (int useGpu : numTwoArray) - } // end for (int numConstantSlots : {1, 2}) - } // end for (int numSparseNonValueVecSlots : numSlotsArray) - } // end for (int numDenseVecSlots : numSlotsArray) -} - -void checkSampleSequence(const vector& args1, - const vector& args2, - int64_t offset, - int64_t numSeqs, - bool useGpu) { - // check slot num are equal - EXPECT_EQ(args1.size(), args2.size()); - for (size_t i = 0; i < args1.size(); i++) { - auto type = getSlotType(args1[i]); - // check for args2: sequenceStartPositions vs numSeqs - // (1) size - EXPECT_EQ(args2[i].sequenceStartPositions->getSize(), (size_t)numSeqs + 1); - // (2) content - auto checkArgContent = [&](const Argument& args, int numSeqs) { - for (int j = 0; j <= numSeqs; j++) { - int start_pos = args.sequenceStartPositions->getElement(j); - EXPECT_EQ(start_pos, j); - } - }; - switch (type) { - case SlotDef::INDEX: { - // args1: for label - checkArgContent(args2[i], numSeqs); - // check for args2: ids are equal to args1[offset] - // (1) size - EXPECT_EQ(args2[i].ids->getSize(), (size_t)numSeqs); - // (2) content - for (int j = 0; j < numSeqs; j++) { - EXPECT_EQ(args2[i].ids->get(j), args1[i].ids->get(offset + j)); - } - break; - } - case SlotDef::VECTOR_SPARSE_NON_VALUE: { - // args1: for sparse_non_value - // args2 should put sparse indexes in ids - int colNum1; - const int* rowCols1; - const real* rowValues1; // nullptr - int totalLength = 0; - for (int j = 0; j < numSeqs; j++) { - getColRow( - args1[i], offset + j, useGpu, &colNum1, &rowCols1, &rowValues1); - // (1) lengths - EXPECT_EQ(totalLength, - args2[i].sequenceStartPositions->getElement(j)); - EXPECT_EQ(totalLength, - args2[i].subSequenceStartPositions->getElement(j)); - // (2) content - for (int k = 0; k < colNum1; k++) { - EXPECT_EQ(rowCols1[k], args2[i].ids->get(totalLength + k)); - } - totalLength += colNum1; - if (colNum1 == 0) { - // special case here: we will put a "-1" into ids when column num is - // zero. see ProtoSequenceDataProvider::getNextBatchInternal. - EXPECT_EQ(-1, args2[i].ids->get(totalLength)); - totalLength++; - } - } - EXPECT_EQ(totalLength, - args2[i].sequenceStartPositions->getElement(numSeqs)); - EXPECT_EQ(totalLength, - args2[i].subSequenceStartPositions->getElement(numSeqs)); - break; - } - case SlotDef::VECTOR_DENSE: { - // args1: for dense vector - checkArgContent(args2[i], numSeqs); - // check for args2: values are equal to args1[offset] - // (1) size - EXPECT_EQ(args2[i].value->getHeight(), (size_t)numSeqs); - EXPECT_EQ(args2[i].value->getWidth(), (size_t)getSlotDim(args1[i])); - // (2) content - for (int j = 0; j < numSeqs; j++) { - for (size_t k = 0; k < args2[i].value->getWidth(); k++) { - EXPECT_EQ( - static_cast(args1[i].value->getElement(j + offset, k)), - static_cast(args2[i].value->getElement(j, k))); - } - } - break; - } - default: { EXPECT_EQ(true, false) << "should not reach here"; } - } - } -} - -void testProtoSequenceDataProvider(int* numPerSlotType, - bool async, - bool useGpu) { - mkDir(kTestDir); - DataBatch data; - - prepareData(&data, - numPerSlotType, - /* iid */ true, - useGpu); - writeData(data, useGpu, /* dataCompression */ false); - - DataConfig config; - config.set_type("proto_sequence"); - config.set_files(kProtoFileList); - config.set_async_load_data(async); - - unique_ptr dataProvider(DataProvider::create(config, useGpu)); - dataProvider->setSkipShuffle(); - - EXPECT_EQ(data.getSize(), dataProvider->getSize()); - - int64_t batchSize = 10; - DataBatch batch; - - vector& args1 = data.getStreams(); - ICpuGpuVectorPtr sequenceStartPositions1 = args1[0].sequenceStartPositions; - - dataProvider->reset(); - - size_t args1Offset = 0; - while (dataProvider->getNextBatch(batchSize, &batch) > 0) { - CHECK_EQ(data.getNumStreams(), batch.getNumStreams()); - vector& args2 = batch.getStreams(); - ICpuGpuVectorPtr sequenceStartPositions2 = args2[0].sequenceStartPositions; - for (auto& arg : args1) { - // args1 should not has sequence - EXPECT_EQ(true, !arg.sequenceStartPositions); - } - for (auto& arg : args2) { - // args2 should has sequence - EXPECT_NE(true, !arg.sequenceStartPositions); - } - size_t numSeqs = batch.getNumSequences(); - checkSampleSequence(args1, args2, args1Offset, numSeqs, useGpu); - args1Offset += numSeqs; - } - - EXPECT_EQ(args1Offset, (size_t)data.getNumSequences()); - rmDir(kTestDir); -} - -TEST(ProtoSequenceDataProvider, test) { - int numSlotsArray[] = {0, 3}; - int numTwoArray[] = {0, 1}; - for (int numSparseNonValueVecSlots : numSlotsArray) { - for (int numIdSlots : numSlotsArray) { - for (int numDenseVecSlots : numSlotsArray) { - if (numDenseVecSlots + numSparseNonValueVecSlots + numIdSlots < 1) - continue; - for (int async : numTwoArray) { - for (int useGpu : numTwoArray) { - if (async && useGpu) { - // Currently in async mode, useGpu is not supported - continue; - } -#ifndef PADDLE_WITH_CUDA - if (useGpu) { - continue; - } -#endif - LOG(INFO) << " numDenseVecSlots=" << numDenseVecSlots - << " numSparseNonValueVecSlots=" - << numSparseNonValueVecSlots - << " numIdSlots=" << numIdSlots << " async=" << async - << " useGpu=" << useGpu; - int numPerSlotType[SlotDef::SlotType_ARRAYSIZE] = {0}; - numPerSlotType[SlotDef::VECTOR_DENSE] = numDenseVecSlots; - numPerSlotType[SlotDef::VECTOR_SPARSE_NON_VALUE] = - numSparseNonValueVecSlots; - numPerSlotType[SlotDef::INDEX] = numIdSlots; - testProtoSequenceDataProvider(numPerSlotType, async, useGpu); - } // end for (int useGpu : numTwoArray) - } // end for (int async : numTwoArray) - } // end for (int numDenseVecSlots : numSlotsArray) - } // end for (int numIdSlots : numSlotsArray) - } // end for (int numSparseNonValueVecSlots : numSlotsArray) -} From bbeb826f42aa02796347019159301d696a0e7da8 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 3 Nov 2017 16:03:55 +0800 Subject: [PATCH 02/43] remove ProtoDataProvider in test_PyDataProvider.cpp --- .../test_pydata_provider_wrapper.proto_data | Bin 121 -> 0 bytes .../test_pydata_provider_wrapper.protolist | 1 - paddle/trainer/tests/testPyDataWrapper.py | 24 ----- .../tests/test_PyDataProviderWrapper.cpp | 96 ------------------ 4 files changed, 121 deletions(-) delete mode 100644 paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.proto_data delete mode 100644 paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.protolist diff --git a/paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.proto_data b/paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.proto_data deleted file mode 100644 index f189b21e86a50d70d317b5e43aa2d6e05af5e774..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121 zcmb2+V&PyE-~y5i0!%=X3Cv~_U<9(61(MmaDr2sjAwadD)j zwK*6Y8#@Rwv9JlTC^01_N-;8k6|*yQfE0ri2JM(<4^nJwEbPE_o!L={iIEkgI4Mbr GkqH2v@C}Oq diff --git a/paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.protolist b/paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.protolist deleted file mode 100644 index 6b406dff0b..0000000000 --- a/paddle/trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.protolist +++ /dev/null @@ -1 +0,0 @@ -./trainer/tests/pydata_provider_wrapper_dir/test_pydata_provider_wrapper.proto_data diff --git a/paddle/trainer/tests/testPyDataWrapper.py b/paddle/trainer/tests/testPyDataWrapper.py index 2c29a27433..a76eeeacb9 100644 --- a/paddle/trainer/tests/testPyDataWrapper.py +++ b/paddle/trainer/tests/testPyDataWrapper.py @@ -20,28 +20,6 @@ import random import json import string - -@provider(slots=[ - SparseNonValueSlot(10), DenseSlot(2), SparseValueSlot(10), StringSlot(1), - IndexSlot(3) -]) -def processNonSequenceData(obj, filename): - with open(filename, "rb") as f: - for line in f: - slots_str = line.split(';') - index = int(slots_str[0]) - non_values = map(int, slots_str[1].split()[1:]) - dense = map(float, slots_str[2].split()[1:]) - strs = slots_str[4].strip().split(' ', 1)[1] - - def __values_mapper__(s): - s = s.split(":") - return int(s[0]), float(s[1]) - - values = map(__values_mapper__, slots_str[3].split()[1:]) - yield [non_values, dense, values, strs, index] - - SPARSE_ID_LIMIT = 1000 SPARSE_ID_COUNT = 100 SEQUENCE_LIMIT = 50 @@ -146,8 +124,6 @@ def processSubSeqAndGenerateData(obj, name): if __name__ == "__main__": - pvd = processNonSequenceData("test.txt") - print pvd.getNextBatch(100) pvd = processSeqAndGenerateData("_") print pvd.getNextBatch(100) pvd = processSubSeqAndGenerateData("_") diff --git a/paddle/trainer/tests/test_PyDataProviderWrapper.cpp b/paddle/trainer/tests/test_PyDataProviderWrapper.cpp index 66ec65e340..92dc8aa9ec 100644 --- a/paddle/trainer/tests/test_PyDataProviderWrapper.cpp +++ b/paddle/trainer/tests/test_PyDataProviderWrapper.cpp @@ -25,45 +25,9 @@ limitations under the License. */ #include #include "picojson.h" -void checkEqual(const paddle::Argument& expect, const paddle::Argument& actual); void checkValue(std::vector& arguments, picojson::array& arr); const std::string kDir = "./trainer/tests/pydata_provider_wrapper_dir/"; -TEST(PyDataProviderWrapper, NoSequenceData) { - paddle::DataConfig conf; - conf.set_type("py"); - conf.set_load_data_module(std::string("testPyDataWrapper")); - conf.set_load_data_object(std::string("processNonSequenceData")); - conf.set_async_load_data(false); - conf.clear_files(); - conf.set_files(kDir + "test_pydata_provider_wrapper.list"); - paddle::DataProviderPtr provider(paddle::DataProvider::create(conf, false)); - provider->setSkipShuffle(); - provider->reset(); - paddle::DataBatch batchFromPy; - provider->getNextBatch(100, &batchFromPy); - - paddle::DataConfig conf2; - conf2.set_type("proto"); - conf2.set_async_load_data(false); - conf2.clear_files(); - conf2.set_files(kDir + "test_pydata_provider_wrapper.protolist"); - - provider.reset(paddle::DataProvider::create(conf2, false)); - provider->setSkipShuffle(); - provider->reset(); - paddle::DataBatch batchFromProto; - provider->getNextBatch(100, &batchFromProto); - - std::vector& pyArguments = batchFromPy.getStreams(); - std::vector& protoArguments = batchFromProto.getStreams(); - EXPECT_EQ(pyArguments.size(), protoArguments.size()); - - for (size_t i = 0; i < pyArguments.size(); ++i) { - checkEqual(protoArguments[i], pyArguments[i]); - } -} - TEST(PyDataProviderWrapper, SequenceData) { paddle::DataConfig conf; conf.set_type("py"); @@ -148,66 +112,6 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } -void checkEqual(const paddle::Argument& expect, - const paddle::Argument& actual) { - if (expect.value) { - EXPECT_TRUE(actual.value != nullptr); - paddle::Matrix* e = expect.value.get(); - paddle::Matrix* a = actual.value.get(); - EXPECT_EQ(e->getWidth(), a->getWidth()); - EXPECT_EQ(e->getHeight(), a->getHeight()); - if (dynamic_cast(e)) { - paddle::CpuSparseMatrix* se = dynamic_cast(e); - paddle::CpuSparseMatrix* sa = dynamic_cast(a); - EXPECT_EQ(se->getFormat(), sa->getFormat()); - EXPECT_EQ(se->getElementCnt(), sa->getElementCnt()); - size_t rowSize = se->getFormat() == paddle::SPARSE_CSC - ? se->getElementCnt() - : se->getHeight() + 1; - size_t colSize = se->getFormat() == paddle::SPARSE_CSC - ? se->getWidth() + 1 - : se->getElementCnt(); - for (size_t i = 0; i < rowSize; ++i) { - EXPECT_EQ(se->getRows()[i], sa->getRows()[i]); - } - for (size_t i = 0; i < colSize; ++i) { - EXPECT_EQ(se->getCols()[i], sa->getCols()[i]); - } - if (se->getValueType() == paddle::FLOAT_VALUE) { - EXPECT_EQ(paddle::FLOAT_VALUE, sa->getValueType()); - for (size_t i = 0; i < se->getElementCnt(); ++i) { - EXPECT_EQ(se->getValue()[i], sa->getValue()[i]); - } - } - } else if (dynamic_cast(e)) { - EXPECT_EQ(e->getElementCnt(), a->getElementCnt()); - for (size_t i = 0; i < e->getElementCnt(); ++i) { - EXPECT_EQ(e->getData()[i], a->getData()[i]); - } - } - } - - if (expect.ids) { - EXPECT_TRUE(actual.ids != nullptr); - paddle::VectorT* e = expect.ids.get(); - paddle::VectorT* a = actual.ids.get(); - EXPECT_EQ(e->getSize(), a->getSize()); - for (size_t i = 0; i < e->getSize(); ++i) { - EXPECT_EQ(e->getData()[i], a->getData()[i]); - } - } - - if (expect.strs) { - EXPECT_TRUE(actual.strs != nullptr); - std::vector* e = expect.strs.get(); - std::vector* a = actual.strs.get(); - EXPECT_EQ(e->size(), a->size()); - for (size_t i = 0; i < e->size(); ++i) { - EXPECT_EQ((*e)[i], (*a)[i]); - } - } -} - void checkValue(std::vector& arguments, picojson::array& arr) { // CHECK SLOT 0, Sparse Value. From ebb22e5cb7a9548f5015f46323bb611cc7c328c8 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 3 Nov 2017 16:49:17 +0800 Subject: [PATCH 03/43] remove ProtoData in sample_trainer_config_opt_*.conf --- paddle/trainer/tests/mnist.list | 1 - paddle/trainer/tests/mnist_bin_part | Bin 3861383 -> 0 bytes .../tests/sample_trainer_config_opt_a.conf | 8 ++++++-- .../tests/sample_trainer_config_opt_b.conf | 8 ++++++-- 4 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 paddle/trainer/tests/mnist.list delete mode 100644 paddle/trainer/tests/mnist_bin_part diff --git a/paddle/trainer/tests/mnist.list b/paddle/trainer/tests/mnist.list deleted file mode 100644 index 703e87753d..0000000000 --- a/paddle/trainer/tests/mnist.list +++ /dev/null @@ -1 +0,0 @@ -trainer/tests/mnist_bin_part diff --git a/paddle/trainer/tests/mnist_bin_part b/paddle/trainer/tests/mnist_bin_part deleted file mode 100644 index 08b93a0ebb5698bdafbc36c3c757918a50bab621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3861383 zcmeF)54?9tc@XwDnzR99j9N9-)WrYRq}BMZ*z9|6s?kQ9)>KoqZQ90aTU*m6QEP2Y zb%ob-yP>%3uE?%5ECdsXxGaQhR%Br%KnVh&xIqdVia;q@2n{r$%WJ}?^PT&9e(!VV z&N*k!@7~?K@9w#u&$)Acb7tn5d1lU>=l}e|r+?n(U;3ik{<)`r?&tsfFM9e9fAKH* zp)Y>=LtlJp``a+EVc>?xz_;9U&t-#n`CGpB=x3;E(^8$DjP< zw;g}#V}JJebAIM8A3t{Wf2hCj`d|O02A9?S4kKju?Z-2+XbNr(3`qjs`-}x_`f&=FFe)OZ4^+CDs{d@n) zYIu*l``t$$`|zKx^&frozbZJpdi2fr{kFO`@|fIz`1KFn^o}?Gfm-%6KlK-m;k_+0 z_5140moC5Vb+0@6)c^A>$F8@(y!WQpFjSQhaQ?N zn_+76zL$T+@pt}D|9q_rFE4x1ubITlZ7=!tN4Gxr7au?TPyYBG)-#{;oFQ1?)^%23 zfa7}jfB$!nU-9Z!AN}k<{OX&2`ks4kdhQEeaP+o!zVqnb*S_}X2fzP!9DmN$|9)Tg zeUE(O@g2{8_Ao5)?s)bW9LIbGOsO><>pbVXH&@U8>z{Z0%9p;hJFnWE`}^^q{K?DK z#dm(^FF1bFKl-=!{lE0t|FMq8d+@r4zWVs?yZ*&vWA#qXwYTQkw|w$jFF*aK|H6^$ zhd=zMkKXy}-+A=sZ@#n&XKUbXWj73L7%5*S)cK*!sqxkPP95)I zyMEWL%}bZO554sk<9y-weczF*9oNY_+v(I`*5Y`Z`C2=!scy`TH})Iv$W;A9^QXoO z*2g~ZfrD7HL;JGT+c0pWV_;(?ZuA-4`u`Ni07>(ozxn3<^%H;f=9?}GB|m-7mt8I- zLzM=<`5*op$F5KP#D9IlUKCBhKmI@d{o^N8kVaFFtzi3%;bL?tkL1Ub+d%>|PmKk=H-)jAK&ukuet%;ifyD^M%Jh z{*&KYrROv^#yC<}8PZ=Q^wU1`*y1<~j3Z;AwK+tq5Pf>9G2>( zfA`DloZNZKEsOn&dTsi$_q^vaZ9$vg{=WBt2QJeJkX(QHXC52IVcQN;@-v_Qi;M80 z-h*%Wy~mG!^rHul??o^EwMAp_1;g-ezw^$cAAS43ef*01e%or8FTVTkqqo29>yCf& zp|4(rpV!}i|Ir8kum7yTyl)uhgKv1l(T{)Pzd641%9Y_dfm7NB_2Bltd;itr7rpq! zRr?apbNZ7HJ#_hjSAOO3hu`-fp3&OnawGRN6UO-PKmMb~@MM26H~;L>M~__C74Iso z&%F;kWA(VE`tSp{+*0?D`(FO?quX!)B~|l6`$FqvZyDWBrrO`~Hw--0FtD)_Pqi7} z#``3~07<0JZB9wjFKUQBwIrZ(5@C`<#xW=54uc&1ZzZ}*zxVZ4fJM5-c-pj;^iKUY z-Vw0IvnR=41lRO0&sT5$V?FlpLIbr3uNj{{$CcsDX`tZeA~Y}XIX9lr z*cjKEJ%{F|U7t(8jmJJ$wLZ&8JX6BFO}$sf;TcU@%#_closX48HxXf9S+0D3NhUmVDj+ z_kT~7h_WM}YCGqD+ihQT`QH0~ca;d{cG~+OGur(4{kqrQbbPhg>*uz6_tgj{_4(0{ ze{+#S{?0Sk5B|XKI{wV3KYfIJ2^eiULKYT0R;lXUf9qEq^I7TnLqtE>i}RnzaA;fJ z^uvGfT3!^MNy~f{UyT2N1ieeP@>3v9CcO?=AN$CEe*Dp&`YA>oW!~RdS&{l|22MPB z`CHy{Sy@v1$dl4!ef*I}j^H@v&-ws|zetq5`)~fT+K)QU$ec;5;Zz6SU4Q#m?Z*Qi zpNe)JzVan6IeP48zP*;aa^-VR^>+#+c-nj0JHP%ow0-(4&o$)_aoauj)OyO$){RQJ z1D|ku$8EP&kpO?1*T3dpKjvf8j!D0N>|O7wJ~8TCx%pBV_c|>bslU1;^4gtMF(Pi#D~w%;i4u&UaqE`iV~*z3=`1@qSLd zlQgfn+~_^6Ex#dS1k;|AFMchaxx72@PHQpReytf$|*?0fCS)Za_ zyGwcXyHMa_yW-;GvQ%$-zG2|J7}!{e^I~wTzhU4B!GKRGvH>X*mrw2}nKIf*Zl#Pl zxU0q|Hm8gY9{uQ_sVHZ(R=RHI2pe?M=Joc9= znMacEs^stvKJw~q@A@N!r6`6sB@ao=tv32HVt7)GaCYs{Oqi>T7n%Shrz^Au&-#w< zsK2ps++j)g!Q_6O>-O%Et;*^rvbk4gDG&P-V_Yg5@VHtd|CY-4p09z&Ek62YuCQOd zHmCAP@^63MFW$@L#OAJ6Af?#$Lt)MdOkS3aIGE;H{@&-ydg(%uon zq9_+>!Cws*5SlObth36JM4^Qgi)5{4l3F`L3otxlXk@*^f2_yoAC~Rv02|-y!h>&q z^ATgJXlpKIND24$re0g4dFIsIxZvr(-~asQpTYGgjFEEKcJmqR z=}4We%!YyM83P+DaXrt@*0(1S2A=k_KXb%R|M46W67t91@s6V!@n|Q3CDHrt+ixEt zftLD4>W%CUzlk8VTQUGC{@mw3|6rJ;=d=h6iQ2ZOephl}X#nXt7KpY5DJ&9B+b%Nd zvCn?CN@z*@Tp~T4Dwo=uyCsPyT|QN}kS_dANMEs2#YX%TzL02t`B^`IUx(kt9E^B| zjLcf3fGMihFZhXXJ_RG{NI&}6U)qzf>LB&<^WHrl36XyS23z|gy*`4cDHazwclRB? z^u*_;z(K;^`Zs>jF)dXOM$UWfTi?3xqdDV8lApKt)@#Ng_Hm8R!vnAQt^2W61p1~(_vsdVS?|=VMUawe3YhMJUUirDa3=doIwuisr z2##qY43kt?@XLF?=Qm#aMDOgbeL@9uT+`lMS?o%6%Ps9P7-2NP z)|h=bLlAk@kYuirfoF_uw22hZ-rF927+HVHP>3}Dyzm}+)vJyk{?UK0;_X8p`cpL; z(5vqGvMyH}lQMM%Z36ay6V!4;dPfs1D@lkb&(JNLdVHntVmFtA}@5(676F^QV(!G?j0zyMMM$&1W#JzlPNq^8=g zw#?XwbbaUl@(a7<$syy5vE=sMgL@kGA+2AlqtBp{Cx$^W)@yaw+-a+7 zKN7zDmT&H&E%&GU6Hmrc?V10Kx4I@uey@d-F`kTVbp9%C3XFC<>Yw1M2k9j51jY%x zJnpF)C+j$Hf07!{5}ar+aCy>B+nwCyeZ&g)BC`tGW0vdm-r4mj4WIT*pk6zn_~`8|lXBM(e?7@6NG zSUF#xwdc}@_R3|q`wasd2G(O>Vmh85*8SOI|A<=gsJ>obp#u2@~&gD7k>q?O{SLt{;elB2q=*Rw} z>PE`|r%0hsk=N%)nWYrB2M_FUfs1|u6srQLbub0?2+i#8astbxZ+11y>x5_~rP zqpq=na+2Z}-;|;8F_L08&$xy`R`hu^ty1rTOYYGlb-0wnMfG@{OY5 zu_s15+VU>Np2Y>+Mf>NOpv&fLEvy#y(|-2bkGvBM&uICqaqYXXOW+Hn-0o*>JBR+Z z4)|ikb>8tD+K;W?QwIYZEAiBs>usb@I0k%r+v`vl)n4btgZDXR_~FV%1IQA;k>U&) zl#CeOJA+KsCRFP?FSUek7z+kGY1KjfaMD)z)S?M3}M1jbfp!@wK{HdbN|JKM7j0|#S(#7Sh7vb9(8 zRA4}oK-vR&huS!|9pdIkYUh)<|77LQSH5p6ulW6(mw!c#xDs~p0qe1%K|p%bjJRsF zp@6xf{V2*+R#YQ?3jsrl#6bF!E=O4@%@5yz>Ry>2EZk!!M9isD?p=Smt0Rp`Dm|NP zf}Q`|^ZblWfA|N)m`El?Y|7=P?vZUZn$T2%ly9Ee=v&<6k>SQXLRu7Dp13!sc6+yc zfGP{O!yj@JS&E$IdWuBDUi9il9*Lrg}{Qtydw?z6q);fn28xBY|3o11HJ~&I*)gHX|7ZgDD^x4=cn!cW3Ys4j^M+Vz`gZ6RgWJBt%snk5n0N+$zKB-x-_o* zTtp_C5HQUFu}Z!KZH+mqYoe6<(I5H4!+6iI;@h)=!dYDAzxc-25;R~GsA z((nCE$D5`}-HRqS+`v|F!@zZjfsK{84ku;n(v6ORU?s2<`6?3J3;kpu1781{*X*Om z1lbX(&!j+=hvJj8j23{6zl&{kv^|v~XW0Rhf{((*CW-D663Dh~(2ym zr4bo{2WKI@ZQ=M$LjLx*yyZ+=nU&brTL36W)cMZKCXz{{j| z)T{6p>c!(~G*fU8bt8W&?ZHDf*9cNml~Vqha|P0~-fa(_zB*E{S3dIYec%}Y#c7E4i&WlU>>R;?asxD4V{iON!?c4$+v4sjVnj3-j>BhB8a zh6aClhr*S-)pSPtv}sXB|3)-UBj6cu(NNH)<% zNJO8J*OdsAq}S~3{XUQT8>wWRLhYW$;YHu|t84sspY?159QRI4MrO^Ct1VjD+OoHI zMnJUVKHD(+1jn;ps{RxSl;M$1GLY!Ck@J?|DT`dpZTIex_PO2OJxM7;3WgE+B1NsI zHy2pyai#3R$ay5-Js9$kY{-_L+iA4%tXprbtVpyG`IBj9UiiwKkViK!8sum<`#wy< zi3bflWYg7^#uTgIiL$jLLm8o1Khc$uw7i4+`ds0c>f|+V4j99Xo)KVPtBdk(yIZBop2r2H-Bq~rD!JS>)aYC= z#9~w!b$jXD`%dGsb|1xtFe;t$o0x(}3o?iMrS(JeVGp_Wxxcsu`i-$>c=i<9H6o{r z)J`xG3j?Q|hGu4|@7wbY18Xp_u@Y;rwUym4FoXfpe5AvbqQK5qPue}yV5bmL{S~i% z^^s^Otz@x&s*0UANgvnI(a(GbOwD@X_kG_{7=~z@Z^VmU{A;V<0vFM^!j*PSnxds8XcWBd)3e3%PrxNQxth69X$oEAf0T#cIK8H71%XxO*0` z;7uJ8`w=!PH_f(M{hZgkN9INZEM(By6QQHcD`nk|z+*G79F{fe9+K{*@e7stLzrtv z2QCzye2l~n*ZRpqy9c2K5d_tm<}-mcTXQZ9TX>p@6F2F3hyf2y`I&HiUlXRmY2Mx+ z8n1asCr@j+0*k0%jVmxx9{For_Jt{*hqe#=paQ0T@_llI+Z=1a01n#-H#DRpEK&~Coa{KJF1s~0M#U6Nwl$nBwRK!X|n{26lav5v+?2`Ca z!>!#IYq_b$7V>FrkVM+>dH?pm_BmA&+1kQa1F9W763&HYyN_w80+y84p;wSAY~Yk{ z$DOy_(k<7koAWBkZ4DF+%A65UeYP9hv;X?%9W*vJ^i~Z9UegUZorf{#x?zYGH5V#OfAa z(Lk0X_Fh-V2#(*#UaYXP>h}6Hmq#2Bq7Cb-XINy7lBB~QSR4UA|+Kx zTlpNfX$1CkI`wJ*y9(ft+y&Vt7%Y+z3)5bFDUcn?j-E>p{UWVI>Tzi`4sDI}`;O;) z;eOoF)=~;qxOpOH*p@H3v__le6UkG_tH_*4k;o*jA#rCkvereIR66E$0?a-etRVZO zYwfY&g{EnAd@-(=uYE@bZ-QJ_UzmW!YJvBXT6>G-Qvqu!0-s7p>W8LJ{}>R$Pik`V zRs^dGtJH%_@6Tl6^nfdK#lvo#<|6MOajzdp*7&`juTszZxpv-l!F!IK^j^5U!?Esq zsqr~8VeNWO!##EDbb0J&L<7s~*!b3layZra_G81qhJnW&0~;&xxTAOj@YKTqQiV+~ z5+q+FvKNVsd%ov;D)|wgV4qzQH%BhHM-q^{9SW0FkU@`&Uj_*ui6IRLX)qFv{|3n( z;}h^m`)X94l|Yg6L8|LJDd?4A7(lZ#)zpJ;cta)a*_cn2>E)Bg1}g@ZW26M2?Ul`W zH6m6iFNkB#pSu@)G)N<`Tx_@J>cq2vSL;RQOv*xh+AjLJ7%Ph&|Ox5}*;8pF@ zLSY7C+~KV?>q4}nIlqrya|}GpGXuKb^V4}Z*C4D&^^=pYPd{DyMN5EHt?$ZG;qshe zQdxU9d4CUqS2ZPN4n~cQqD$b7h7gE4*btg0F5%dM$RA19jt?yM{npoupZB~~u=HmH zp1HDDx`Yj|-!N`E#88t4$DTw($9Ey)#POaCkg)mn9V=r3YwdoL+Y~IRA*$ASsXk zOzxk1?^r5wNjL?pO#dB4!nLIk>ICF z0FkDUw)U^7zF8aQ%XR0K&#juSx$;x(@N=<;n0G1P99m4S>?aca?>cfkog?1rS}f&G zj=_6qvTswcBA>P_!YowQp|nYB0A~RMTS|+de`~`c1e`W4`{NPqmf+Z#eRoeI(U#wS zZy2~yF|e@`H|or7{k{${Km#DCA7dGc{J2P?4*Bf%hUgPS@z1Ur$sLOMHhtY|Zz{Id zUw5w06|$QFO566-Z=dE&`uF-jmB$C88rBpegJ_@hlJP-%OkFnQS!)au@T*OEf;DxX zM3gs&(XYrGO}SemCtrN`-A4>wNDG$&^qvPEjTClbw13L2fybDFKMbj~K%fkqu88`y z;%^PS@cRk;zx}O$Xcenm-LDJjsQ!riwQ&V3DUy4XHDkOM$2($mM~oVsOq}7l7u|?f zXO0a)@`itlI-|y0rpd91Nck2n7O3^NU|8gAi5gC!z)=S&u8kE_e(4kM{Q4szh!!TJ zJU$w5<$M`piMEX6nyFLeHCR_x zuIF}aV|~2_&wP*7q1iFN*XoXq4N|YwiTk;6g42%cJpBxqk%B!1V+|Z5%YVH-2h6GT z%F6l9@Z76E0(X3_S_sp>4_^<{ZEjCC3@l+_VJ7X4h*(CRVyJ#x7KMP2xth${hC$ktxkj!P<^7fu2ug#0!BKx#9 z@>9TQajHEyo9WI)g6TA07Z>8VNPTnpQ6r}xd)K?p908^s+7QxR((9|m(8`tjOe$M# zkav9~MIABbszy|4T%`A0%SVjQ;7|a`8tcBaF=F4}&dVA@tQfDe2?Do~H=P!8w5$c= znl3Z_#QaA9*(x2LE?S?E=IR@x9Gafdbw<0!FfOmP8k%4MUb|kbZFADX^_yFJT*?Iw zrPfOg5wHM{aZT=Hv_V5*Ux|yBB%bwB#(@i<_K%$$-)kf1wd*QB)3*^{kN{{dZol)+ zVg!2Ukuzaxw5arsXglE1vIuZ(9Y>u*T(1MR)LtC>NTf6&?!Du=>~_ClV8g%|1~yh= z3{P9B4FfY6V65VE#OBz=b=rb}6 zsYU9ByomD7)EG4$Lq=e=)Mb3aH-jO-Qv7=EqU_k?^kc>tr5}s9t*)NCag7EAjqytL z@7g%uvb>IVEFSzgo+`IBa(}h3P=VW_vq5(7u(l)Z_sa0Kh%xq_pUyjBUC!g5!55*j zEv4_L+dlgXi>_~Q(y|X{OP|eQ*A+`Y5%==T2bOpDRVmFUp$o{I*8lcYU>QVEeIi z0hg#mRIa$RDO3f&2KUPGo4D(RU%Glf3;Yze@3KJC+O2+OrOkk!_iK13CUEvd)ID#? zSk$`n4Pscu)%{ArA$IA!tk`+`w$*=fVqjw>o}BZ#jp_--fKQ=-mUCAjMHWfVrA|j= zCM3PjlTXrA>eE|ou$LN5&qX?^pSj|ecxbvx12fZql;?*Oz9};KvzFcA2keo$+|l#At)2fG^t8Z) zpEfd5ucOv`9$&A_T;AIH?fB-}?mYu9ydR9`w(23|N0IN?r+v)?x!}aJGhE#ZXc@sZ4tNdyL}I%-%BYR9=dNEXMUf+ zYR4VFNAbQe%uD*(?w7saOYLmWHw>)Bz{X0f#n@JU!$8FVNw%mXt}N+sFORgHp+UYz zB-uUmf<+tQbL1ig<`($A_rJeHGx~~}7teCaaAz${(PQo^(zx7?dy>aCulJkL!YEAR zs_vA#arN5Ec{C%W6~soHbOdsGQy zmQNIS+sKQwvSYh>uWruqU?Mda`p39wO_ZW&P9W77-_(iKQT8O=8E+MMb5irubxwW> zx|_olh)C2ccRKF`o<3Gm`t-xeU&zPmZoR{$`Xic!>XyH&Lt@_=X7&Jr<^2vkGB~3t zoO2D2cCbmpP()q$an6U+p`%Pr!K&}|u8$=5G_ASJ(!I!F87A1YwxT&{E7oclTSecb zhn~kH3KjS9jNfRN@fsskeTY2cqJcec)F@hzs`=dEE85Sg_e}JyCA^J3*XQTH;7g9I zS6A%G;4l_D@0gbQA;bYq+~W;rsUO?(Eg0;!1~v>Fhyl?#*it)=h|vtAm%IPwFDu6( zefr2L#TOL>R`d;u>Kco^NE=DA^2K$~Z{#6`dLgwB{!}>1C;~MiJ83@{N-(^5+Ry&X zQN&%ZiuzqYkOYEe#%FmHojyS^CP6v{GM%dHvqVW>eK>Y-N$S@GRAhh#PugJgFa;~; z3xH|t$k&SV5>aX-B9Asc^r1gh>#RA>E?|?+*7Fxif8iId;@MOxUcEjtZp*CgRQWdF zv5GOT49wc+?Qbk{#Yuq)NBm4i@fvN_W9~}*5lJ)B63H8`vUM&Cdw_I{_LfpXN7bt& z&r;#?9NAU9HT#K?PwOjd71^A+78B6#mdK2lGf4w%NxM0za zvevmB?1wdgU1y}ZG?&#dt-#VZhEln$HTNRkf$6>MX+*4jY%jbU+o2KLa-aBjXv>~S zEJHMb?QmMS$W#^Y>h@o zSU)V1eeFDLZ*?~ev>4d55-moy-%kMykd`8S*N`D=(86w*RGE9Qs~X1?TUnnBzb`5{ zA999UHOX7p;r3E&!PUl2cXE!x zSI?4r*c@zQz>==@%c9YY`X}KMzUd51@4sXCNYA6Jlz3xQ`xtm`-Pqss!+)>_y|uRp zjA}fUT}=Cn_lC6nkiFX6Yd811cc1r{SKN2sxk!2}nR&3jvG+7aElgwdY~(zZz};x- z+!br}l`nZo4I1u#XT&*?kz!mQv5BPAqaZu-FoxC6cbTeWa7me;8W) z=#Tv2VG;_d-nN&22Ru=t*b}?L?ULk6*=l$-HJ~QnICA@OPqNPl!bKVh=ea8PLK02o zNx1W~se8v&F*G2N#Ex(SMHV5!mp-}18+}8XPr=CfRa;Z~_sF~7JybsGngf~%@UR@# zZ9JPxuM`pnnY~7uYR!^bt{t0o>3kFGY_3o3Gt%FH#Ta6AY}NRy;A0KEO1>5eGk93; zIo7T1XJyOEyH(e8`>^vH%N|1+2L#N8AESgRIy!(z{x`uF4`6!dX-~ZyVGJnWnx`?0I_F6lZDK9!`&Zi5 ze!jfRYu>NXzR-|)k7?UzFpbAIdvxCzh0`lHr!DW5f2!tfti)4w=AW|T=0eMWWUT~m z1YwK}wC}SK(T=LYP?6#}nYtPbmF*TeJJQ}<8LSA&F^QM$yp??Aya!(Sm5ZcF4;F(E zpT)v19an>$0;>m)L^B7dM5eDj*_olkJ{EiPiDJ}(e&%&BRsMATHH}6+*6sAe z?k_=Z_3_O=^!pa^7Y)!P1qrJz$$JqijJ@x>_jnYZcP921>k6#w{}O$gOn2}YM$H|` zW^FKN3RuQv-q(_id5^Fsd7pFNURREumuDZyYoqL|N2BX1x2MjTZ$4zd!=!iEB-n~1T(fSPJ=k}%ly!`SvN>+X=7o zjg>eMaa*+;5d)+cNRWIDf93}V%$A;1(G~W@A*qy7xDPhfEyg$s8AietQm)*7d+)O~ zYHY-Hmu+%fPPio@Y}?Gg`H7IgRs)`$4e+QGH*C9edsFw=Cbr8#QaC3dm+c}eWs*!* z@?_V?T$f~>8!$hY^4Y!YB7sZQ%jv-1x(XiSn+zjdE}eKi!b)P3a{b(w-aVt3D34U) zhS@9E%VR844a;`88DYzuj}h=j0*_|6+>)a%a^30K`FZa?!(#~3W4(IyNG~hfxEot_ zz3&xl@;*^q#j*d<&kH17{C`N*4>iuQFGioGf|a%gpZ1+x#>wi<^bG&xr*Qv+m#_Id?O@tRTr^~# z`q-bXeKIbvM%UI{C!BJvy%% zdyjqgvpd$}12?tg4WErpv)wGL!_*uZORz_|G9!ZM7krG`a5Jo6C=+!fMY3a+dZZ#U z8A}-C4y{`5%hWyAv-+!)c?md?inP_M$Cg~Ef+#0`e@jaC>dDLOMAVm>uO1H7!BRz8 zb1>pAt)4$)h`KrT-kp><`yXirU@E&!g)~NosY;82)41{`2WhqOG)SKHV z+e@($ncSsOxte^ch*|hU=!3BYELukfoV_-e@-n+*jMo1@d-TyG7xFug{{#oi!J6k_ z(q>j|TY=TK-TsC{--x;PMxF@=QT@sqnDKsr3!iJ}YYsM;Jr+XVsPX4q``fb(19KSI zScy68Y|ox#7$BiU#>C|_6E^-AIaCo?_AZg-vk~^VJ|U!V>y0zc_KtEq$1dmY>i3~T zlf=X28q$Nmktj!kTwNkJ3fJNg{eU7FIck;3R_96Zca}mvGjqKOfA?+&7%+5gZn(3-?ZK0GUYA0h^GRZp8!&th1p+1Q_FNF$Y!5B(&_AR^4a`{j4qgpJ zTa3rr_K*S5pZ3|vZ{$(+1u4>T^JQ+3JoTK0rUAYq$P@5J{12My^=E5G?Ni??CokKK zubDrRjCFCP^jzzNbX!^rp1|7SkuIs5)2VQE^FgL7K8+?Tm~ zS~6|M6*m1yP?so=Y8Oue~Be>V6(fMk}7xR4{L#$1^Dn743)#s){;d12|6 zZ%FJ%d4IrmnV*C4HZx}Zw%&XD<4m>f(UTkl8!Pc7pWtnHfdM49NDyq^Nuym{m**r_ zQTLFv+7c(SAU-im@!{9P;(A#P9d?n70#2k$B;+VO_QP>DL$oaw51vGtRLpOm%h5Kc z%5jO0qw7f7v@1FY_#yc&-uZ|qrVh7xtX7nn zOX2Wt8mnDDrFH%~l^EAyGjdt?uYb*Jx_#?3QMD3neecqXpZC0@d|9L6lNIGf7Q;SH z8D90^aR+C_6m3kU@xFU*{bO4WJ##^zU`C2lu;d3k##>WHa=r0P<@wIoqg=EO*gK-C z$cywM45_C2u;p(Uc%m_|u@X--#y5CBM;P$gqlC`a8U_7mw~-%Q+^VZ!`Bdf4m{{US z|IxOV%8|aZh0QS9dy>5ox7ImWjDHwQA?Mrnd%qovG}qqTvoa@_D|y-A4jd#T+kCzN zne62bI@jjZGpq^Q?RE_Bc=I2q#$s7=7?MS0Mf z(FXE00*hu?3}{+uqM)KJSvop@*h@i)8SrH|3{$MRUV!CFiZ^pR0*~=rK4BX?hE-fm zug(~xRO5~W&wMYPMFEdhQymPcYQ|+J4zZOAj4ZIVdgU?p2#o0Eea}F7wpwovKnd!v| z+whx@oIw)fV`M6S>Jlh+dIV!nhztalb**)~AA_)!+Ay$;fsK_|#?e;dNr3@m59y`t zo$o-t*4p7NAp|+h7$!a`ZOX`Hsg056+#%UyXX$ebYyW$s!HF`)f;WOe}LPLAy} zNBOGK^(d55?10n1qwJjfa;jEu;ULdJj0^ck+t`-J(r1Y@ob<}I_2PGp`=90tJQ}_p z`3F{>Gn$FD5svaYkFn99Mt>s3`sHyka2a96qJE?iur^>xvCx7)fB*BpxZ3$+wKHj~ z%%SHYAgAxR?Y5JE&kbG~#9<4yA9#R6d-Qq9V|%-!6!SRqJ$Tl}+W87R_EKxr{$a18 zU5yJ{D1Xg5?6tq;Z5X)TFtD)_*V~M2{dt@)Kq_n-cda3p)^x`w_!X~ybv39UO^Z(! zX`xS5kCa=}zOu9{-1|r*#d!}Hy_5tN>9H0@l*bMg_G=ObcfYWC5BT!1ZPCVPdS!R| zakLXD0~Q7X=8TjZ*;YRNrzHH(`i}27Lvl^Q<;Ow-%*`@jNaL)6s}mCL%EIhOpwS?X z$fs75!@y=l@@c%Mv=~zZ+U3(Wggv|QFb<&|gRh0E^)YQhtf!hTRAL~Q@h7AKvh5^} z{Rf;kQQoy_sVkN~>W>9Tz9!XX7f#yWlDV{gQ{4`OIWR|}IK|PD#z|`w?=yCWMgaV& zb?5vMBtBSNc}L!R?=^!<^H>eqN?mN^5-E+XjlO|55~?8MsJf$tn_EGF8Ot$1@;gK!V9l+g z-1gKxF-L|8UsD%Wc&eQ>`HUC2_}g#tK2Yb%$?%0O^jvVCPbda_Qjj@Z;~xGef4oKr;u^a2sb1Sj(JYYHV$#RwBP4Bp`^2!B zU)ye5o`%H<*kMl}ut=C0Vq^oL`mAkW@Hf)>N+I*Z#@|@j71IQS4X~yvd7M9)gY7GZ z)H(pa zN=sAud~2=7oQ}*NTm9BZ#`qYbyyJ~uQ_r#pElY+I_ZeO#;o`wWT77;pKFU3<%Mv*n z&54>7;~UW`A&>HW^MAm)M|*SWgKv1lQC?Rgk&A*n_dYR_Dk6v>60DMIJHH|0<2F1} zH?unCYi>O8jJ>@%&+B+px=#BL__tpzoqquq_iJ?m9D@fMU!&5KSZ1~)&7=${bOKbC9eND+In~) z82Cg9I7ymK8YPY2j885Ez^)PY^?OpaL_4XU-juRozo^I>B*F1%id6e9N6XdESNVLd zaW$R7h2W3jQYs3HBjfT3#YkCs`|(aSw2)7N>(t~to+&9lwk^snG~0cwekH}qc(~Lu z%$b{;8uSn;WjF-q8$d9AklwadpW4?l% z1+(HP8l6c66$Y!mD>LQVM-_*=_h}R_86R`=+ro-t0_&O{oCkYL#eFHi=DpKL@0Yp} z38wUhJou50~;%G zJlkWsFY>$5Z3XtYWN7=^fAc z!r^xUdD+gRaq5S)?}~S`CAsUnQ^q%PDt8`hycc<#wXNnoY@d5CdwxE-?Wy}pMkf{` z&ufgMO%H_?eFzbcy~Ks3fJ6HUoJT%r88u(1z#$f6PB8jY-lC-7*HfZ!p; zmQIgHLNO{~C{v?u6{c9tTSWU!K=o4XF;6GY2i5;dX5FDnw&hI za_=4FS2JRWmDjh(P3-vXRr!K9r?9*D_5>cJ*7TUqNeuIv?@4ZFmC@2jI_f*n z!sm`XYP6O6f%S9-2RvaHmM-Vx2>d%2SU9)8-ESD!FmMe68!K@Q7u(&F5(8{kk%?2+ zs9WkNa@b)bA)H<-x{-UIN2#kGLp9piL^)&O=fVEfCsMm$VPRT$q=%IZo@i?%Tf`XS zaY}d|ON^q>U&XHQm z=lR1Dj~F;{tN!-C_Bs1?9qp9-I#1j@V_Z__R(KZWqMxIuPfx84^ObQ37K0sQ+FxnC z#Jnmu_eW|SeTWv{dyu%vC7i~e2SwQ2-soyJo9Il%pUu7w`vCvdFv!IParM#e` zFmj#0bzj2{q+VVFH+-MlsScw~L#h z#DLFn7&ql~$rxh|7@vtBd)K?FM7bqf;?s!49*I;l2e%ljR91x)G}@g@k*(s#GnSZZ zxAzRJHmG30LS`^7iK{iNsV4;Y8lA}N!Sbmu0ZMDMtCrDkrWR*nj`k5u@bA9ye5O^Y??fX|? zl2$LliRU?D)X00`q~w;>>_zs9_H*i8kHEUxc*h&xI6K!TbA1(z>r-oxN`o>I<*N&{ zWWq_iXXUaqbXZf@MH}2^INSY(fhP(B8!PcdVS0n}bA$oXQ=bpg!F(Y@Mo)%a>iTnE z@Fhp$&kK%76VUq<%JVrJC4Z)13ZNKAip`N9(FdQ*skY|w8OtEQGHJ?K=BV~qtb z9(-Jny!X9FK@LmWMW3-g;P79Vi~k=i8*(N!IeCj!s4lP#DKoML z;39(SR&Bu~l#>TY`pkxjk|Ve!SR--xjXmZM`?~<{5)w}!R*{eIo6xEQk30Jgcoh5PCvU;0V|vQV@> zcH|)=E_0{O968x*uWIv3T`VUJ&(#^@D9=dH!Qzio&*|tjI+Lf3qIle|wR`XX-IK>5 z*10OMz-x`yWIBp_+_AM$Rr0ZM6i?4z_^f}4)Kqh|o6jN0E_-}T=*ipSj4!*2(y z$Ujd?G?L$8dz;_1VSLH-fwu6x|XmID&Qf_DMeHlaDbG%z|O@$^{ z+xAxe#=*eGO58Yewe@s01{eWQR=3wh#=J%viB0ZgR<5T#@*Wv^5lVAzmo2N(gXdF) zBtL{KBysh5MaqQsgMkh^@qA^Y9q?SnxfZv`^r+AGgYi+%^^xmpPm-#iJwJfnVnI?= z_xKXaohHKi=#`5+uDc|g;lB|595HqgUp?lk){jO9Qrd*dBJJ=x!k%RV8EoBJ(((*$ z7y9Ys@xX`FQEXGkjrE$!rr~3K zj$5Ou7%v~2B{I-^6cT)_UTS*s)_QyJ4ZrskUIQjFcW#`{cv04x)6OK2<^hA7U`Ya2 zqzsT~82JtbZ10TyF4lJO2O1h%1`Bt5kU|^~@T9Sy5&K|mxS+>#+N(8ea)#+%_>J~m z4tN1WdZLN>f}!(3S{Utb{!;c3-v{pM8J;sG>_c;gm2G=#erp$7Lu2l&p*8R48aP|o z4Fel1v8{n8F$NfI_Ud^uNiEf@?8s7u@|w%)(BHlmig%+$^AZ5}XGK?{Y)h{PK8@;_!*q_n)qtS?Ha9|!X&LERX{zv;* z&%W5r`5H3ao@Y-(b|aTZ;Sq0yWPXiy>48^%WyNLb23SH-}X#eZ|q6zZ&GdN-|l^< z>T!d;hF0d;0|cIC>=@sooHER-#*-`C-HP8>7}!{e8*9F{-mby`O#t$dkpt4_p;x`? zhWSYRMGjkmyN_Elw5e{J{1EK4=RVA$E#2S*QIM>TzRDiQZ3!M%lNfO1gUmn>;LF z^ietr@6nHbbgyYs?`fZTY+!7cE|LCccx_~==$`e+e*sy|KP8iom2@qB4N-P3ZQ)gM zNMjsF6VQ9#h%~(aYy7ZUc*d%2;VKw+!J9RdaS-=h%nOM=G_DwP51#fI7iFzjz{qJm z*CiSnOtHo^IriMS{TYjj4Lb>-{6IZ6b>tnL>IT-a1{*J9%VTg)+gOhdovM6bzHlbq zS?^L@Sp88A@qu8sYS$?SHdf*~otUj#7l8rxskh($OO8ptFQUD+4P-qn0Ew{6C&Kpa zQxbVn%KGytv3+JqEqkPP`{{_^BEazBKz6kKUi#f59irW(bnTfEd|6se3m9<5?Yehm za$^l{%|T1nGYs-c=Tyb@W zs}YUPq>NXx&HM1!VAN>neOc#on|AzC562@<4+K6GQP_FtOh;t za3XE(>jm0eBKH`zRsW(wW@mU@#wWn+v#m}3E>RDQj@-qP=gZn4>7@l^#2Dox<)bsV zmiGY54e?6UJ2bsQ^D_8nFA^NNvSexgM_|*kp3K4e{jB1)?1v+8lmV+6a1D;DS4@yp zVxy63#9*=qW6RqxaJ^t)V_=p@|@|d*t2ku2=bdT^=Df)`DlmQ>0K; zBFH9M#G$IFE~F{DW)id-^<;q5nS+gt&=wMfflc^7P-rvK0s}nKFk~_5zVe5#5TwbZ zkVuq~Q!*jR}hZN|3#&R_ufOftwOn&hs%*dK@c z;7l`%kGQWPD}6$%JvE!^&`9j=&DIp<#&jmf+>*cmJ(lu}HpTSU%UM zBzMJk!KaszSkHgKCs-_YWz?&rbI}6jHs|h1i*tXqG2)`ir8ojhN|6b3aQ4siG0hJb z*U|M*eNJ}uk@i)hyKlIEQ~f0pydDnwHBWMLeWcD@*^_72B=@V2EGrFEx6Cre{Tf&T znD9Azig#`jyqJ#~MJGS(-6wcN1id12c^O_c1e+`DS%PKL^xB1fj!)cEEI;AvEFK0Sm7EmPSm(^RdG;yU%|63dq$5vIBlN!Yxl>PpE{Pt zk73PuH7%_C+x|$LL?ds1j4|7%?Oj+~&vTuNx+h*L+PiwqoeqCd@V0iY4-9Oq#Pu-` zTQAOq0TNqEY!_)-EZN_3WpB$M&yd_B67+1qliNW; zkr;pConL>Z?}8l$Ns;SdAD6~=wMaI(J?3M%1l3(KT4Dd(Mk}HbS%MeOg9Xvw9E38S z_fkGRd3=vIyw)G4CFy#7j651Z+KJKev9ZnuM_fQjAwUc zKW8}R9gFKz_uV(!SI0F**^$Up8J2rw>t{aw7x(YhlCc^82IEtQ@l)I0$nShSp?7#{ z-@tZ94T;7H82OoUZXW@(<= zy1m~ON4x&pKXW(=T0dI0=+8yKt6?#A@U%+=^qEh8`skxS^;7!*;_`h9&!cULu^K}* zHmzgXogNn?oYDHs1V5A1VGB9EC*4)*AQKE`6ktg)+1-B%fhi zWh=GiQOuLnpCS<#+FE)H7Uh480U2HcP^OBsK=C|fA9R~FK8zuH!E`SI9w}Ol=(C$2 zEK*OxO2UZ!F}}6hoWa0JinE-m?@hdS@Yrkj$~Of z8d9D_7dssM;aq;|eVl{IZ8~5@ zcna(fa@StP?=0%Ohr=k}Bm1N5bb6vuB^lRV>q3vM$Ske-wWqCNqcdzGkScQ6WzwjS z{4;C|)-c8+wU!RiFmXjAGQSzAmMX zdT?vMjAwJ{U9`PrT*NVJ_HKcSvoxOOW{wrqAJN2OtyL}GT*o@kPVW&ncYU+Wtv$%v zsIbaDbg0O{9)Wd&Q*GIQ?Z@4ju9R4T4R?NaA3A+9wk zuSgkHQ&6^dTfB=qvp&q#U-RtV2cB_+gpc-nspE>)wAm}m*nlww>1Q2Bk*Lu^Fv#J; z=R!7B_(clVYhxtO=M9O%@IhNrK>!)5wDoH&{vBT1XCuFn+58s*XUJ3R##G&B-SYYS zvQuSx`7{iS-x$h(g`Ht^F?SL*mvl#FDPL{UdkuH;MB%~a#h4fzgzJ2#_&)1|wD~L5 zSdHP{{=vjek$_efvuXxPACW60_9Mn-n&e8ZkI0b;5T~-8+#6%^4pqakQzhnql)0|( z7)Z^RjHf3oruP^5yCyC^95CWp<0f9Udj%g{px5q6js$}nh9%Mar4*Z_j)2EYW%F}u znRW&{MpB)h&E10o9=wc_u+}8<+Ip_WM{@w0XAF4CK3m7==)WZc8{>NY1J9`CN!Od^ zhJIpk;(P~Mi7A-K@PLJF8GUDZoe-?rI)huJwY7lMj_btr`j6G;#0xz`&R)B7d0=88 z!0cTjgNgVx>K8T#E1toTa2e)OD2ll3$vrVs)Z5Z0Ck8fF;>kI$H_)iWvtJv*XLNYT zD_b=McRk^DCnc)Vnw?GOsGH#uOt2`qs}u<74pt{c3W?aU)ZW~4(N;*fLgJKD67wqc z3!Bw*NZ0g%afm**<8jOsvceI7ShYgNR2E9PstuVNKzfFB4QX0@4r4rE#3wja+210| zeL9ujXHMLhpF5uOg=fYQ$ST7&Xn~iq@VhypKIPD?|k4xI&xH*^%eaa!AEqzeJ_!BBNEd* zf9gbgb17K$JdQN)qZ$J99a_YZ^`#%RZLre3uh~o0k9jbkaBK|PMs~x22v6S6cE7QX zN7t72{MEiS{k2zlAK*BTBi_qM*TOo1SdNQxl%D--w;#rMr&9aP8XN|koZ8=)#uNKz zu6Le$^A>w$q;WLdA3C#PT@`dMsYGODUJ=ezMRojh4sfHGCd24EKUf z(vJ0{l68DLY2yey<=L-03nbt$N~1Mk;Kt6m2Pg0%YE9I0gjeKGrHtO9e5CcPiKz-r zen^ZN{*6d4V||A~pWmZKLo{chO&f8YKI!Vs)eorpbd&O>#{orG}h0d|yayg3+IC)aA8+{Kw23|`u2?Qib@gA%Z;t3ye~6PN98EdhY1 zHAG<14%D?(_JQ*lY1YVO=CH8uQ}4iDqq=ggTO9eF_d@4|WbF<3<~F_ySivdnMX&8% z9*v49Q}G^hzee8s)>Q3S8Ix!Bp&<8r?X1b82?(uez|E<^#v||b8kksek>czD@JBO2 zW6Q8}YF^j0KUKEO9}U;y`!Q92%fEOGY^=n^<8*6r2m>ddrVms@oS_N_6tdOv5n!Xv zPSn;adW8r%w);7QmQiqwPej;pg;7M_!t*_HeAE^>;KLJ}YZqmCHkQe4Pu=@m*?ObB zxfCpc0;k#<&94%`IY8wZ+%q^EZF8>N2s}8LYh%r`5qP3}t!Z=O-7UlykC(#ZJO zor@~N$cxX-{21pUF2Uo*j*RDD6UHi2@I(xnhj!^wgff_FgWE0=vHC*nNW-bJBd+Ca z*{ABS$!CK<^1j;x*R(rTmRs){eC=->CqJh2GFCwk4;>#H z@xUS0b4gnu;! zzOo&=@?ctLqX9g<<48R9oX|WXO>d*62~OJUSDObG4G>t|j7Qj)lQym$X{au?y=}Yw zonfi7CZbaGQ9XVkb0HPLb<(&wR=(CLk@(ga&R|_?kla$Aahp&9c|44$7zXm!aP8k~ z{aXnu+NC{?{(`ChaYazrc)nxHs-ftbaRn@?ZY2Ax*cEOm>AMT z9Q!IgFQhKGanFACqQLq5<+hRY+#8)G5%5L~EJpgH-|_iF%4s)ThvBzsV#-)A0-kYI z$yaF)8PL-dAeoHuJn(!{b3jZI{HdU@mHaI82<`~JdS$?hdEr`GNZamOAPKC-Wo3Mh zI_$Nzlvhc--MZs;>6oLbI@m@1S}HeHUw$0tr=2BfJUGg&iB6SaSKp38I>p*YHX+@5 z#uRlv3m%Zphe(2vG1WD>v{AK0(%sdv2*e5du{ES*?*H69xA0gav^PWsd?=7;@^f8m zjl4=W=LduLL=xEOfG5aiRHgBxfr#ItFoC0N5s6H$r6}J^XqbcU^;>u@+@c+07Scd8QQZ-a%{=kj*J^E0;e+eHK zaZ6l;g=a|so^LtU2!2Oc^o!`j*2X*rHdbODKikvC4FkvlZoP3mUVsF1^yx3rNcNv+ z+a3cFkwY%R6-}fQ(q^upNT}Wx8(XeODLyNQj2F)02&^EXNJ>d+!Tv(pb_`DW+ZTIr3p+>1s%Gi6J z^Qytu4i=aB{A}bNX-b<$yD;(&25E`i!`6+!$YtV)w7K=0%ac~aJ%gB>zjV((%e+@( zmJ-Z#seC*~7FC!0VssNmGVyFKt^P)39;$Y&kXv(Q+h;M~-k+*DYYU{`jAe)jyx-Q@ zA*Tx4Q;?dqjly<%7a3-`fbxFD?|eiZ_T$h!Q@i{n7-gEzxXe%7OZ|rbHCh&JPyJa3 zca<|70$&xz?tMXu(-;4pI4vs{E5o>-k7hEzVgF(xTi zrgwftfQ}=%)*A9SLMf9L3{gf%2!;e$f_nT^bYdZ8L!6B?tixh>aRz_AW~?bJ)~(O| z#X}^i^4UlX+^fm;5qsn6!ID?CBn9sy@JKnc(V*yP$R|=>k;Q0LM3mu=GzGJg^#$ID z|4?O(N?GedEc;kvb?$fh@k~E@W2_pNQa0-L%0ynUal0U?6=yVkL=XYTIH$%&^Wq(K zn)8_P%FV^7=9|wPpH`zll|Ii&%Ng?6y zOtz~=mNSk(iq4S*Bk+(f<&#+9Am_mCZP`_lTlz9;OWuR6uT?uy+TkZLCrM*)W$cH- z6a9k*37#nEm+(Z%eJDIqU?c_hr=B^|8+?<;JsCWjq2|%-&l*{%uzCW+3QRd$I6Y+d zPJ($gu>k+o9ZI5V#F_3+yeKz$wBFrJForN=1uFWT{ zx2~|^qZ$`&Ft&>T%viHgvN^&JIBTNq%hGRic=zA@W%V5bdkLmJlX2RK_~M&}=B@1m z(M#Z#QCj;veq&>>eS#DVGZFcRq}sACn-fQt_S$fqsJ&#=uc+5X&STVM-y}ZJc4+SS zwmk5P-&*%M-@Tr}VQy#5eT~*sLYNo_HW=&(s!Q8ACk9#rCzti@^Zwg+z{MJ2F@1k? zx$XXm#lXf&JhAxRpzSe$B=tGV*OWW&zS4m=q`5vQ{qyo^j>1!HDK+hfQG6bl&lqFF1^LrbxfG z+@bi)YoppUpG9@e$>sKv*@?1|jRb@f7rzB*taEFl$BuZfbGg975Zjpch^Pl1Snb+~ zJ_-h1ahK|1=SCtw#aIGXw3E-DVWe2ygNvk@w3**hltaD=;vDZs^l2(}u0w_u?KrVK z&iD~jcB)@QKvQi`s#+VX-D&%m5%Z|YH%0v98$FxJp!z;oOmSVgLQ8GCjS@Uovq zvA`(=zvT1nqs z4U)^M5s2~Z(v!IQw+%_RN&885UDZ$~wM7d+b3p4uQ$<^n`x5ugotPX%dVn-P!;U9- z(O#^V_}!+XnxUn3qTRW2qP5X7q+v|nit@FzPl4IOtA1d`wH=!_Wk@y!w2i(MW6EiU zXH5|UO|`d_uYcZO;Rggb?p@XjmQcB?9TCD3jP3b`f$J3m8!K_W&dig(U$#`F1^2qR zkaeS{(T*aBWC%$Uff@6s#69WMMZpvgK7XECSn+!#@a5e2e1;faP*`Vpl;v!+N!Y6P zBa`9RmWCXiMeuZRiwMM@XD9Gq#ijL{gqa{wiR~2ol_EJ z=gPI!tK_}3EuxY4EZSfcH)R0xnNNRuA5m?tTno$ZfEQ88z!Ns17j;N^u)+?WrfaI4 zvb{fhZHy_>o_0rFVjl*fQEuEH;~M0=_M@Hlq;qhDuE1JH8OAxiXmQtC?%xGI%|phM z_c*TV7m+j}v`B}33)Hi&t&QxL7vtEc1s-i7e-mtKzxV5M87FE_1S;=&T;65*L_zCX z+)8g4Scid)l~{+bt@MRq0BK;m_ldHFBQY+l@zfBKM&zMzLyj%oXN0jtDtfM4_Oa~y zu|=(-K4AKU6nF1)!_)@*^k^3e2bV}tq*i8|4)t3IWH^ta2DU7u4S3)p&$4euB?)%V zM9$j_z^ukd zf$xXEVKpnkZn%<3r43q;B{DO!9fz!!N?H!iKXyAvbL{RRY+10-*8Nf>p*wE-qCJZv z`cus5nly~*Kf=k4b-`^>C%JCtH^}EK$CHH}Qe6%q&zRKDr zSmy0elDmb+rr!JIvQDkV%;tb43SaK?ntO({nGMQgjeG0JJ->{4PRbepow8F}>41kN zW=J_2rLF_wR2EGLKZ$7HyHNRS}E<<)2{-CFB1u@Saen)@v( zj3`&R?PxFZM8tXU&2O&l`YdLcbN2Er+=!N=-B<_pez!KJ&N`?kCCB=yb=#KD{wB?( ziE77!jCkqy{-$HG->0O7_3VVahR!yBDo0!9I$%iycZ?lQ74H043oinmW_To&=4dJ4 zDuW)j@=>=&^6B&`XZ7IKHIoZX+zWRQu%sjFv#=c%e|c?8G~da-b{yNN)~uc;o#uNq z=xq(GTGnHXulBpsqc$r74M)8ZoczRl{zQ%QAM1){Er)w(VVwG%`ZzKNG=bO`Wx=CW zYR~aF)qeYXvEd5v=x_`dYk)zqFd2aijC zeun~2((0R0f_7K!V1oxy+NSoLIe4CVujBln49|UBu{Qk1P9J(MPV@y#dy2J|+s^Q; zjioRh(cWlkuBr=t8XK5=qVjntqwQ_wE)D}5D{*nS+*(?N0j_`H;wK$FZFm$28*OfP zNXH;?^C!iYE}t*Kd3`2)2C|VzhSR$zks2LGE_byUHj(0HSjIqW(7QtVYpjB`=C(%e z!OHz-?~J^is$0(^$e-@1f}Q*x4(ln_owAX2C_HN;<1og!1dntyOQ@qCb1CBjpSpk% zDVA@_z$w~ef1Ncakw2GumjXtl#`WME-Y_|D(O$+UG9dD&Qd0LY#0pr_QB6^XHxg~? z3W;Q1U%Pwc1bh0ae#)oGgY)Pgt<+q3&q7+=!t~BP{ipxJZ2fFJH~zj~V|OylrL!$a z-f4E#*##ROGMt$j-zR1wNP(5tWZ7G0@sM#$Bjw5lO!}M`ehk=ci zn8VKY>~X??NEuEUrmz%6!zax}xxMs>9_^AqgQPF)MKk%wZdSyTu%GpbS?ki*_JIT~ z$G5MNl4A4ArBpCJ<=Fv;^lRh{gBC{ohP#NaLgGzIGi69o72D-gEpT4cUy~v!;5jWfJr?@WN$1lS)sSOXc%(hBb~*T(iHFIk+i|7uUd}<*}yb<~R2(?(2J2#z)++Mx4W~pAlC+(K*_j zO9P%YIae+}^KSA{I>e6$KD|TF{_CGN1do=7mcTay>yrC1dhb1AaJICEIp4wFz^Lo=3b?MMR^6nJ7nVO-!fn+I5*DsqdftN9YQMcoR*m z&IZ%`&<+|a7$U-1`||nPt!{Lya3L7jScwZkbIR4VTqk}->!8stkWk0`V|#jTrOM?{0Ls^BTDfdHOd6n(NX9EK=9 zSvfE6ea;RUC!;THQM40j`RrFI6Y`Tki;G>hs4G#%Cp`Qo=Ef7xkglYuQD4g5*r#$z znoHW}r0b|N8h{oKeSV`J(vrbeNYSHDy;NWJcrIFnUYVT7^%+&ujLjxWS(o!Gexv^op)txhV zjPj8RbR6)oHGOA%y4&-4+xRLtwSsS!R$cd@(A=Q){ zXu4aZ!YcFCu0vz(83Dxnb{kvBWGoirlF}NGc25}(dLNY=@sDUhTi>B2Fds;wrT3U7 z5*`jQ>Zpcj)^_Rd$op%atrgPfL!`j=sd&$p#sxPq&vnmfku~t!I)s~wqk;1A-c@bJ zK>0I8;Ihoy2yU#aSid9X&%i1f=FNQcp5;9A1{b6Aoa^TM*e9mm>nQ8phlizoC(2Ky z<{yr#W_6;FcgDDu%1u;1m2FMd$bn@a@trZwJXZaP_j8Tm;wd1VA2H4tN1Iyzhpv$h zxJ$-qA2U|Qwy_SKALF`6SUvb#-aH02R$?AM+tbGd10pee{E^+SW(gnkuJ_d+-0UX6)*Wj*lNe;3U#Y zVw`RMY)*j{bw=RPbX7_C?){CrG>mMh&jM(2R>I5cJzzz;)Zg-s?n!Ra+It^(Mwk6Q z-NU}iwSLUOs~SM_Rt(0fkx)4X@pQZWU&1i(ckq7kuZXcpi^ih6KJD8v2m1BaN4{|g zR@(#bdp)i&&1Jg}^AoXGM!y&>jj(bptoZF*7uu|7&t8+s&)B!)Ircj09-3C)GIO(= zpPDwmeVZ-7srY1%Hj~L30to0WS(J#jk*$arqSHPl?au zMZt2W#MEiISe!lFa{%O$DH2f)2Dy`{F#@10=eNON%_SgW9b5f;t_R4C6G_khJNgYK zf0sL+{p>TJcb~HJz{{sXb4grsTXl9o^lNl<*cbIk2g8ONJF!G!T0_V>pXD0(b`T&p z%;+Oh`$}QEri{g+Nd`lVztm%37WqploBC`|KN^&=;<>;QacGUP5(5=b@o&$>Swho~w@>bF|u&8#Gs9T2QQmeu=2W=y0U{ zwhVY+wfXsXgo}tZ64mqCnxv=6+wzt}S#iqT&&w&cI!iZe~XN_K% z7JLbR;M>N(#&yxK0=DlmW4!pirPn70Hdf;LoR=qgudoufc@b=aEVziHfil`oNYZbu zq*y3lV@sF%+%pVf^uf0x*FEGu2N&(Z%dp3TL!Rvk&5bRcO>eZh1P_UP=qIcmZ&vUX zu%zIT;)#5OtRQ_Oea&U~Tb%9ve2yp_)r~sA^QlLkI`<&g&MW}9hrkPITJ(DkPv?P$ zm3Y~Ue$A;*JraM8l{gQ)%u2-A=H4&sAL$!q89J>Y@9P;EY0em?^jx81{@OK_-F>6X zREo67`vxgF+x_YCUwd>yDxdgTV0*awPL*%-Yowtxww^0=)Im-$wg@~**RX)v$*c=f zdWyWow%qr9zvUDx{s(LQaWXt&ff!3p`Ly*!6-#V)Zo@rRA_qim*S~cLUv<6`&f&4L zAuIRk2;UI%K{FAogb)Z3I63xDq&RlpySFwnmGMP80Z$~Z%t}Oll&kPnJn(NXLTPXn za3b}7UMb>)^ZFBrh#0|nqwO|5ZxG!U72bDyyNiDuc=ngbJ2c8x3upU%J`8NE#QE^I z)n1JOQe+W-T0;zZQ?}fzf$S>$z%94zNu*G`Mruo9XWWvghr%KytnSAc^0~8p%l$Uju@2Rr!(pyhU7$<(wMTOy&jK&?Q=c21xUa#Bckjc}nF(zZ)$gd( zMK+H}{TV*+M_~ZuXvem_rQcXU<9pFPU$$7+x)9z$c~asv^A`OGc#dboE<}Etdf(WM z2kz?`9c`h3-vLiMb73JIw-~V27!+)I8MwlFdPiIge&p*5mgSJMetJC;q9WkMeipx_#(zjOu^udWb+0eD zim_x^=BtV_rYawpk2a|5Ww&q8>iT`2wQ*oqwrU*=Y^+2FDO=X#iGk2ah;vOk6Bk8v z8}#$ge%M~LSIM^m!}i*{NWZFG^MQaw{-Gs6x<&gTaf;t<%2?&laZ=2;?JdDm{t$`1 zPfsh6)z%2SQIayy4u?5lZXP3eG%!vO4k==OzQ2#g7SgN5u#HA#U$0pkj9|i^SYXAT zgtXROfD?~vfa3;6z-HW|KT%f7cp#piN^_x!`>Gx41U=SHPt)-F*SzMGc55xXEYUty zx4`RZ8W?=E<@UcP#uV9Ygd^)Bu3oe%MnG+pDI}vhG@{Y&R7yj&?^~Kb+N2&Uja}@O zr!~3lo_iMI=~r8yl-wBwZ68V2gN<(MjGjT#gIC>iTVUlkch?q9{GMV5+GZ;Ik?^QK z4I~4(T(;F*1RN=8w0X_tr|xMTumv^3RDw%hG7}MO%^f z@a`F-!qKtiG4qcI-?@NsDlKb1|7OH!T!+94xcqahv+6$IjMez8dCy(FeIVLdn_^*z z=8VTUeAm3oBk$K*SX=pZ7}!{eb@#>kDi^m&* z7xkobFyfhY*shH!`}e35=^^l}`P@eme3I8_b1rS`&Xx6y3vM2pwXswuKlg4%Uq|ZJ z(|XH%nX~#{=f)Q9k@=Y=99f^)4VQn*oMBT2Pm=&vjLrKuBI$O_R@9MVS#rBm_X0G- zNz@&U!iAis366TwDHxH@Ff7i`P@X?i!R65U(#F~~EwF9v*KT}EzADkrNbB*4yEOwx zC+zg;y(`L|Vsp#)h}1itji%l;`)1%2Oe}(N=L>6AgU%KlDhsEFXt4Cl-oB6aw!>4v z&$x>`r_*DzM#t1ECyJVHSzdS1e&o>_e*0hhoHN?;UV9^X_M6b^1gscedB2YtUyZc0 zm3e$Hu(1-44_>!cyBMHs4rQ;jka3U8>+i%*$5Vv?i|x&2`<8zZN>TRNq`G(9_C?k2 zB4G4V5;?~Qb&)cWiJXtP=Rfk$L;J0}*M2V#OmWpGnm?0WkAm9aDYxg$2F4TZRSmV8 z^0xF*_x0@S0Upf`_Ca))Go33LB(bgSvwEYfSt|`3kz}zguCeBeUrpd7j@Ih+@pRs4 zX+1;l8XXYDC&tH?eMHin*NgXT4VgzOiqr_0HI2?pq#53b zu~FMh7)I@3wgsBsG(15rVF$c}O_AHZTyoV1adMil146Mq>aeb zsWzwb3)y@5*1z$K_U%QxBk9NySpk<@GnPevbK8z>V*Aau9huc$NPpu~2U#+5 zPMuZH=T*Q@Wa@}QytqwP5>ed|pO7l~-OY(3@dSX3agU}E@deD~IwuA$KINm*`nJt{ z^jQ0>{r%WyKUUW$FJvP2emjoKFK`(cBWe6}TQP+pH z@c59xr~bY4dwWRzZ1^^Oj|qsIt$-(qMD#5yV3K z2QxSPKftSO-AuXOBSviMI;OMtyqD)2qYlmEn(@JXXtb+lqt|#RPdE(lTtYgGxH+Fh zcl({S4&Rqfvy)j^g#4~!&+UiRb-l2ezEM$`KN(RCe-Zt|2Il8cZYiaac&r2*wy(rk zBejRicj-&yV^_~>=)@)8J1bNQqtU6$*(ZmLYiZou^LY$xti(Kiwx<`00g^qRyK1Pj z+j4C;Bu4VrL{^#W7*dTQSD?I*2K7?WTv~VKxpv!U+(+XGq+a{Iyio~dhQ**rP|%+J zHbX5WLB6t7n*?x8q!l*AJ^TJoe)8KY$|c9uay!+K zBTGy3!-;#Jx)xSpvxdl^%Koi@7eQYG9+G4(s!`kJI{p>#yhi~`>WH&7YnuTs(_QzDW5JPhStHdf*Qq-|9W#Q}e)d%0!Psh~r1XbK8GEq!3G`fL z84A!G1YDm&_OiV;M)Qhvr`o4$Q`-zZuro9UNDZHo7vFvN-Y|pP^r6w4ki9i%ZGi=z zEqh9Pvp*)Qlv^9!$k>f5!(wPa`uNDZ-`&Mml_>A}bqKsF0pHa(cQj&Gi~I8wDRUlp z3=G1c!?;GYZ|8xRNyE{oK8M0%Oc6$e+<-@q5UVUe!9U6E)SBwO6IJ?M>a8)TBBf+( zguJTm!@Y(l@&ewdL6tQWZlTqHYogt$?0{$OkG!{hd}4oD!&U|SfLGt|&Y3MyPO5B3 z>WM=RvA53#Rak-=$twfd-7|pOpDthKo3y`EY$z)mR1wFt>y4)TuD|`O_WQIoxrXKM zbpo827i^?;;Sv~@#NIR79DAzoN3V@FdG?eZs}y~Z64k9~bEzz`A76-I@d6gDhduec zIGloyZL$9NSNZM`eNC0!@}D>iY^=l+hwG;}Bnoj`4Cw^B@hU|XU7}p>eOi1nMht1@ zQ0Y@anM)!R>T#hLaPID|F^ z%(df0ZjXR=0^N%>Q^U_}7U zD=T3|o5;5jc-Ek~hmR_yjs{ky>JGsxpLnp4zBRJTN;pwm(8v*ZKC$3& zS>41-KkIog`N6_HqeOjVXgI>6X^sd03rdqIa?k>ZGmmL8%^_{^5=^kFOK~Yb5?$;J zu##aOBVwWN45L_kj=uASii5);Nqq+Q6}L!~B20|r1jB22X3!pr&MsShJ-R}kwp6(rT?pA9w^Oj@rd z5GV!kC`{73DajUG_DIvKMbcL7dkdb8^`(FJ%ZH5*sv>_^FXKsI!#Vd#pOJuxM32C& zOXI9k;vKyE|Nif(5^DGzu}SB)8GRfz;z7Eo!w{>Lgf8`!%XCQlM&Z@t#R|N&WLEUW zI?`X!V9NdCx{1KNhr)ZsefRBg%I7HhF_qFJd6y4`cM`?yL~Jnv4-Vi$fYc>pyI!A> zY_Vn-TlUJddG;=K%ssd7_W9iJ9GG*|ps^n9SKxt(Uki$eoko)r*Kox!$^j%RcqHNOv_r6L@Go-aAZBW7~n`- zILL5Xv$f$l@=%DeV&BkrSN{G*o^&ekZ*Fhoo^czt$-5Hp+}pSAD4H&j8Qqs_)p}ln zO=Imn<{rQ1IL)PYx95jpU}Gf?!`oJCH3mp#D!Eg%5KiAj#;tB-u!2up1Q@;l!OK-D zjFdd&voHcOY=3R#%2V8v)UY2d+K1H=tvQ$s4Sb&3vm3bE+RE3sbkD(wXGk}=Y$bCf zs0=YkYnRA@Xlp4YwZalj89r2d+cL)Ud~S0eNSV3kPm%hLTJmZ3NzC;Qxv%bmMH)L7 zw2*d;25rO$YN-#{Ixh2b<+vw$FQ)K4SGRoz-pIT#F1(1q&)CV4_nD8+c?}DV)t)tC zTW)+K9t{gk)QHP^ zt~YfbJ`*&dy)v|$C*P>v)5G$v6iuuBv(-WqWmzku5$Hyt)4USL`1bt&?7a=VT}N5( zf2ydo$h8+0E22~_coDp!yok{3T@)4daxDs1P*GI8;rr8-D{+X(GZg z^36u9BoI1(_XumT?3(kE&n={D=lKYjD3OngT}_SvJI%0U*paFdUyYmCIboMFW{0?H zVC+(0bR-5B?i0gj20BvfdH))p+}I$Gd34zs@wF5D*<*K?$UW?qW5>xc%2#!*V>hqK z=N#<#)1$jgu<>PLisUqKpL6ETJnLY~=U@Fi(0-utDPp&MTzotIIvG#JRnWZy%&o{j z!l$FhZk&?InCHYkS#OloWC3Rf5M2247n|onyHp$QG8RbNQsW;4Eh25jVCX-~vDgB; z=MmY$d*M0JzSt3De&W^~Hi$T%bUV%>f;gI0%|$G7?L?zd)wtlkzH5^1uAq%x#sG)4 z<~OC+>ehURV&50l2)3oi`tuvA9&7JA#X45E&2NFW7x6eIijB?>gSPzMs;{EWh&2|J z->Aa^|0I69)x8h4K$D4Rc-QT(k9rmwXf9dro`G6idON7XhJz28=?6zqel~IKwFB9; zW9CU4Xt8b6WLswKx^-FD3*4#sMxmMFUH1#`1~m9aFs~-GboThOAkKp=@3&v{8t>s?fwRK`1Vo|m-ue*Uh(^idFOTfR;NY4M2WO`T5p92VB6m_Ea({-s2IT~6t+9X z&M(Aq{hYY=MOk_}{}bmQW1r7%WmRO9!U?$>e7cAe?)O`D+{9*{Pd<@E@?ywW(Tg$W z2dmtXN*xBI(mLEgK^pXjiCEWOQJUok`rvE8vMg{-NFd;uO!>0zf_ zWiu}#kw*{zBG`iv_wXFoaD0q>YHp)2{!#Qu9&}~95_V=DgugV_^ z4FWsAiV52qvHl!+^r~F9B@&f~NuAsX*yc{P$kjN+Zdz|1$eq;2eb zsmq!7drSUR<%KMrdo)QE?VDM5wnVp-LH>;i4yfOvnOgJ zz-}zcc-R4U5GZ_hWB=^cA;_O1*n4)ZQY@Zka_yKfb%h@I5Gs~EoM(u!>1g-7&gWW%W z-a{olgXZ%tZhVd&#yNFGLL$L1FB!Aj;X<<`5rs++fu;r{W!I~u>A)@C4MY)LFIeAL z?TNVHx;e6@yvqX}=bW(K;wJ2#fgbNhPP4*G7_6zSb<7yUcO#Z%_6HyuH8)l=FZN-svk!Ex5YG^8 zsSexCw}juJu%1pk&F@4seL9hx8f}}3G47jM{aXn75SME=R{ajU->+<2`O%tKFEFG) zi@7njD)BKOC8romVOYdk@^?qS8+>zk=ef?i0Szh6Xp?qzG{AXu^%{Y$c@OAY=d83a znv0Djr~Sp$ymNdP!(x6DG3cdlW1>WQOs&Tu1lV}4>i?NzT;Leax2WH|>#P^XH2%E!(_J=hL{PtdY~1&}dBv~^%#(LU=NY?>dGm^0 ze|bN>_qPm>DO`KTF*;7hJ$0}A2n_W(YVrLke`L&m&sdMzzbkKkvYr+6bp8BsGKScB zhR;=y8)K~m_h6jN)Bhb5_7gl;*e;HT(VLQ!H9foe!b}-wHiv9Qi%PUu8YP zNZr-_?`8PAmAS@uUD`7IjzCN2rFCfs4Q}e+i=w(gm-a{l1Wc4@0Gd5g7XfTE*{$bu zBA*n;p6W5XXecM^_LDfth81BOP-likfaHu^lP zh9nYi-S4>M7P9-{%-)X@y zJ7*Z3;J3rN6U}Md?XF_KT=2-iW!@p4Wp^>g<{EopWgA#~)`R!PX|AGwU#jhiO_ilN zB5*C`{ioE)IY2Zm#u`gc{c6yH_`kOS;7U%gujZYJrN{fi_|O(qvGaLk!INk{v6%2~ z@lC87sR-Kq2Jw@r6POnxyr#bg(JDA>NN1Nf_q=PO@sjn9%WkfH5E?*zoa%-pV9mj_ zPE;%~8~KAHMlv)@MQ@jJt1- z8bk4SGWI(Z($aInMEFLx{N3PhE%f=l$nVB&GZ5f5mFqmed49Y2O{uXYp;gen5cUKg zK^>pf(}#eG64QtEJfwszI7Tr=rH#Q)2q&K^3|9Dj6_wR4f)1ZlShVo@*mLHD&946m zEuyE_F{;65p-Kb@#{eyI3TQuCV`PJmiC++n^uE^oiDJI7ILn_F4?mRRj5{wf55_|5 zAnKoYrAZy@$}A#epB{6HVV6n42kWALs-VX_%l=#|u84d(&Z3}@+BU3DO->F3=8Y3W zr`-LU#EWMqUJZK0-8&pqa1J}7^J_wn?grkYp#SjHr?=>mua9OY{eGI#gXNBW&zjN; zv%c50pGT{q@2R9<^aql2%x_AKGFeAh(K^NC`L~lQ?w1VFotWx za*Nxx>qpmxns=A)oqtV_W7n-{qKG1}_=r#1XI{;`m~-skh9Y`;oxiDI2d4OEDNX+N zF(-HcZJWAg4!nPqn7;$5>05teV&k}7 zpA~kT*cnwDO+ICM?Avf+m)ex=VGO+>er`8h?N0kOe}2#e5VgXmr|p`scFaq5-aT5_ zxccd*a98A<;@drgMcthsFx2r<$M{;HfzbJiOI+DP1@}a;-=7<_V%QNf=7KTn%Pw4% zUxkufd(~B0)?>z7XV+J=WAj{Wv>QoX*q zly$C%39y=eoA8)-Mcaq>V$ORaMGs%b4NhfZOj~-4iSMPXNB{SQC|mT~OhHd^@PD5& zSND7MUGjaAqt5%O{f9MuvvtPb3)TplXj+&PgIqRp}1Nv?#eP~HQ z@1~s>JjeQhSy)oetAMm!?jvBLguMzQM8N$i6(0$9Eog$2E+SFo??QtLhrA-TG&U zzR?u0({tuir#dKfvHj(o&$K@VSlO}|@kMm~C@uUMD#8jEQ4AyeIhXA$=FwX=F(C#@ z%nMq1ZTxG9onl1QH;^NONR0TQeJGBMIo8+#gA9L7>+FP!DCTIzYFrp6?3<*Ia?deN z%~hgvD60|C^&Q|HEoHU}emE}rjSxOS+to3o8X)LS$SL8!2lUB%$hMM( zQGgZgeTNpx_q0ls3TcGrh?6R|_-o2`7NG`Im~w%raS_ET#sa=(L}~Cj)nlz| zbe-Fj=o|DE&V@g{L<(^VWr;7Jf*u$$FKGGNr7(q7n_W?zB-vnj53+GPny}XVi3nL6 z)-`LV9n`3?efMe67p{s$5n_BTN)0m7R9G(Wy~FUOD&uS$dg349(W|0G#3_v=Q#bmP zXlg3njSw%1+Qc)i@UDjUi_Xy!5Idi>(sv&}l0fSfmo>*J2ylt^ywT6VwR4h-|gHP!d<5&i1Cw5DG@ z);hd~7{&V5^;y8+5&Y?wT8!ZwJ$w_C7PZpDKo%{~#yxxD)@yi*5*h#vmaaL+6n^V{ zUm`i@T3rz{hn!t91PT!_twbR-^ZABjd*stTKBC*+RRVqMO`*_ZP%M;eTx3`WpuP>A5*>?J&q5IRj(G;b=wd3R1Zsk z&4&9bzf$)-p|>W_@Z7kEWAB)&)&H8(Q(St_SJRnlQ4fFr{jX08s&Vw7Gwxy7jMmip z6+I7~-o4(q7++D_Da?wG!ZCAB327(}b$_3El*{^8`YGsywCP*diR19TozE`>Oq7^k z>m4y$Y#lK?L0iBl59OT z0+2KAl=X6S(7q6LXfiUi1V&)e5>AK7LK#SrNjHA%d^35kF z4#t~gl@2vZ>fIj^_oPDF5+6p06Dk}=Bk-wBL#pwlEjfxa*JnP7a$ z_k?^X6y~m)3dY3RVrVpe@x_H}Ta0Ay4q*X&V?kY$yjl!bt2&IhkGni;mq&wZXpqQ> zQ&+1KLE4(kU<6E*n8AxP{X@f^pHCj8ZmO}^(c(({iN%W-sExmVl3GZ=Akt?U`S7iKROUk$xkP>W%V_n!E!t_6?CWyD)pJsl^tp9Hl0vDJ+(B|GCDF7yS94_l3q7JmyUL=NMc z&P!>=xPkjnDVwd&*Qs~j{AzZwjCim8f=>{JX?m^5_r>qWjtzmP2$(3*6uCM19PAo94wO+u zT2Rn%3l4Q@aWwp+ysv?AD^7j>gKB^Vb`wTjvmT}Rwfw`ktS{aasA+K1SUJ6hOR zBT5zZTXbF0hV}7@$q%CRgzYNowBS0T3-1L5JMn^M+2x*%eb1<*t{9IPr{4|MjBpEC z$2kXl%hbKKPQ)*|V$Ik^XU*|rfHlh@F{W9Jansef{XA25ZvL}HVF3vAiG4k^d@&9- zHdahx4j)|fwHc0RZ_#e}b7Ss^Q)tx?Y5cL_8jcw0x34orKN zceee`J3HW}zXzWtnkIhB&}plo4r#+1@D22f{szLfc0NB4Fi~QDtaHRH`K+-!Nwgu@ zLUtHAM^PW~c&#R6_{124Kk->{EEC84X;C+_TAk)JhR&&{pYFNl`m{Li)9cZ!pA|ZX zoD1UUvOfoXhx1ECk3SLO&OHtE1V`}d7v2rnVKh2uuh`KSRYZd^aR&llQ)WXZU?-`flWn$6?mlvHkq1HDtaXeU1@H z>%JR#wDg+%jZ+$q9&_fm#rGv89i<0Pz;{BwFZ}+vR_D8+eOWy>5HL|RhR z?(DJIovf?8u1lUjnb-vDpA(elW%0!8_8+!K*{igu*}XxzjID0iw(U95Y&60QpIXV{ z?C3#PS%S7S^mz%S+3%bF-RE3HH0GC@o@hK=ziJqvSzwobQP?r`#b>2QWS2J4R&?I%HWScS=@Bqe?_mrhsp#CC zF<40vbp@X%r4>eIVokXRo=w<#9rsi{wZj`m_o9ve4wXeFn(8nA2zz-AxXSp6k)QYO zty}XvnLApOW;@$G`fhhOiu5*DLF-`X1j6e7p2L zS3DU9?+~=~9U{PWb&u6897gA>!5Z%i*y+2$yo%nT>Yj^R$alo`&0PG#?=bJTQQTAf z9e4$wR``91@5x%ub_7h6nC&Z=Wc(QC@cF}~iq9^}XU=2oeCrGXHb4{J%byeer1T4R z@~PN+=EP36@Aj&6{2H2VUBqBh7A<~eHV<-Ws6hm~v#^JL&Wko|4SyQZ9O)cTWZ!z* zZ9{(oN&BUW?{KF*cD>j|R8a_e7_VUTQ1raIgo}-NQKN$E?B)@-U+2ZKWcn^>&t;{D z5mZUDpal$>i?dweXqh$M@{|% z^@)iMU@AFB`n14A@&#yz6}9hXzZ$=gihuHra$=soAB-_J1tTIiJ~#TT<`F0(>CcVd z*rH1=DSYq!elR>D{DIQKz_Q9$h3Eau?|eJ_KFf}M*KR}t5!cEd=-*SrEyl@jyvoOg z#}MxoN4^EIw0b0`#7t;nFqKCW(E>jZwwioe;MyO0RE58vu7TEw4H$-E45r0564Q2k zi=v2ljaXy8A#6ElKUH;4#67!}i!y#4xXEY1y1n0{(#M^iHucuan_SKz-x{<@Z1ecV zZM}5x-N<>wF3n*COq7_ziyJ>RF$i#xn_zj_ZHD!15{)H>BDo|`qKB?<;}H>RV*1xM zM8;r8#<#tNC4g<@6U%N&k?phg5mWOggk4C)2kF-tc)-FDc?E?pnh4lA?6k4zFSEyw zp@-%{|3pw@+<+3BHT0h+t~mGcp_q(}=HfrkF)88v2qFCGD`L6*apBS&_EmLG^ctmw z2!uU$**hrr`hPa-(xbh;2e`)~I#-Ra4Lvcy>5o;`rHYul-;|zeSo9qs5>5oU+Vo)U zaeEfSHuug`*KrT;t?j~|)-GazVpP+|Q0G=h%co%hu!oNzCt2;qy}nNqRxLJq5bTtS z2C!Q(^k^gEN}rzO>WFxdbs)bAVjy83>THYn^kTIQvcv7h$Zxc6R3N@l`nyA8>%NDj z*z9l)UmD6nSo8=y!X~Qadj)nUbb-nY$Lw`P7lN{ib6`&TE#K?O;GnUb;qk4zcbEQ2E1NevoRSe4Th+diK-hnH-jp8E zt;A;__%ns_e9Oj{c2U@i3vzIuv~Oi|D7xooGxq1eE|fll+bart|Gpe+gUA|I54Jw+ zXUoQook>p=!tInuOMW}iaz)ainMCL5yVKGSY#2M1h(jR@;b;G*E$qnEn>Iz`iIyN_ zEznH3KeL_C)fE@_FsQHuO06+BpZ#ULXyq{U$?HP?)Ccd%<*UxgZ5uw%?~C1R{SE7S zU|%1$JwFEvrSbpAn-aUL76l<*st}0sx z#hnk`3FpUn**f6cgss2!?bpl;S{OYIby#r$QB1`azt6m5=)l&CrmUjhvfE%*rX7Gb zzi+;uu~E{>=y4C4q*&DmacG2j7u8*!?@Uotx~jg*?&D9%-!Xz~6SeBZ{tJhK`j$O! z_vRJ?CQ8h$^=vpB>_)}roj*^`ew9z2*t9n6FYht>gU!GWSfl2s0S+~Gv8il2#n#yE zk}?nd6IN8WooCT}pzY63&x=^CJj}vt(9-qM`4GW_J~he@^AY>_{``B-ggsTC(@)+y z<*kv-sXyN)>*&u*=T|mv?&#{Zecy(R!=A|fw?d!NQB07tmdGJ1J-+6VL`LPS0 z_ul2v;ac80*O{Nc4*Gi&qniSPvP&QQE^Dn{&hM&hydfTp&51Pw8oYv`j)m)>^D7%~ zIKQ&@IHViCXS$AMc=B#(J8J18>^SEf5nbn#SBK*c=cVhO({=8pa2=rOkDKp?KlZ}* zxxMFKELy+3`RaVM=DS+p}-jmQqJrzCXwQ1X{vaz#kowp;Iik=^MmR;wf z9c~S*iguxi{}BEygcb1LMF|XB0KIA^dM@n+jY=*PW|l#+fB*N40auwXh|Uc*r>~gKdADSI{dqwX|D(KiT*KJI z|DBYjl`HBamL9a!-wdq~;$V~a(x*qzT=$OTidgtaDCVSeD;j^*b)R0IW|nUa2DYl5 zM61vy&rlgXzAL`J6nM6o8*}&R@muHj#5;p#QVg{E?-G6@7zNcaM$5yN?`4>W2R0rJ zi>?=%Y7C(Ao^Nsu@qqj;n$}PJs@%K~y;##ctz8p3_RQ=;z(k4JwO%6_Ew<1@;@`l@#>&(+6wl)8JEwJ*h;F>eH89_Z9?Uqlg5> zXIp7ua|?d@XM#^F1~uv%BKaU1&}2QZ;Z-{1jlkBq=+ljONLzd*bS!nqu6C}lN(+15 zDv<r?7g8V<__ZedirZlpZ&~A`vI=8J}KN8ocm%;>Gh`6}?kod(I{Lj~gR% z^aJ+hwGFRfm?6KP@J7iw{!NM}Dd0+)4&9PSI?nhb6orQxdKYLbK`-6>5N!#<9D@&r ze;Cg2tgx6~pH+u^BXo|`xDZ#-lxRAxtr)mf5eV*Mq#}`c-1ADe%v~Sj77YD(rfIfj zcCT@(R(fpl@SWsufM2k=7g}y?ZT$-HzY6E6^uroMp%g4QI0}33VH?t7`vl|0U|AZU z;<}FE6Cw+LZ;6n|yURI$7gB85v0Xs84+>%|z7iZ`#E1bZ>#O(c`m!~ixIK8<5HL~V zv_XHm3G?Z?d)Kb)&oZAkj7zAAOU-9JmZhtj3oN(qufQ(o!3TaxVz2A&2{rDiZ9jAD zQo=sdA9lXD)M{{B+=w}6M^x$7jsD}$6*dFbOK7?I@HzGG>oI>C)mR7{Y;bQfPc?vm zUX?}#+f|(-wWN^K0Y%oT`CzBH>RsT^2|Il{&sdv#v|pEBd1YtKUwmt3@K7h>l(^2j z`N6iuxcPy-j|(=&byz(DgKA`%GD8=ISbkS-d`ffTn)7P0zx1O!(4x`PjtlCx^oU%f z!8oUbo}U>whMpS0HNg`fDnGgs+b9JvW|u=e*#c=nzTCr-fa<{HFX)OODm2 z1XjIB41o^6E&p!@bAxW(fXqZ{Qe4owBfd`deXsBbM3dytN89q7t4oV%Gj?CIN`Gjh z>CcZ^rgR8Hf*W`n?Tds!aPH*VXHk91ZGkk=Uga$({g9?m(qFncDorr{%>DOwxYuM0 zE0304oB0TsC^7TbXhb9A(?zgPiMfp!(f?G#)^R;0S?tAS$GRW$8d|bD)IaB#nAGh0 zwxx@Erf5FAzVqnGd|e>e(Dk(rec8FonF~8Wu;H>p9L(9zBT$zXyL0|r*i~l-Q+Ln6 z4;OF=#@ZiOlPFc99dgeLE%$DKr~FAS7%(E6vRq|`I|YxANu(_gr>fm%jq1XC1B+JF zZ{Bs-2r=Z-_rnv66ld(zn z8|7~hA_`khw2gVM!hSgS=g0cBs;wjR``A(%GwhprD|6*oU8f< zFc+dT71_RHf4}~3Um{VB{P|0Irm)lC9IXtIZ!kJh5eYUnCUO{ATzz_e^qGK6c1k8^ zT^nk417rVu%PzF{{z_!4tmmSGsR%i&S5d2?>sc5CN0*(`qITJJZ+u0&y;<9w1KtzG zCu)8z>G|syLyvEij!!g!Iddqwq|u-Bc_P#mwbkoB{){z76QR$l5f;ypw;<+O3{rG_ z)bbnD4ohmj*JRdKJlS5v>ECS3aX+li&ZdOFJ?c=k*hFEzE447idx+N zh8}DozcF2R5v{Ap==}!s<2$IdFvyJkR!I6gj~+%XF@`e!x1`QHEn=oeFZx;U88l_| zBk!JKmQ!Ou$otHiDK>fEp`1tErG;)A<(%%JfBCJccc%Y+DGCV?eqV6O&hLBpD*gg| zF9>&_W7TmZdZCTy_u?1TCTP1lc}sKzh(U-)(A0jEe%ZCE?~wMNSA!o0Vf_8}X%*39 zyAAD9USD;tA;OSH!lxO|8Eb3yI9({);>M1p&g)Y8%+pt>$U%|4(aKA z{7;(9Q{A?;AFZ(^CIu6D1Bd~y*R_U4zz7R%O)Op3D-S~=g*c^u)(?Hc&M7Uwj*W!E zyB5A1^h4y9ytUyPYv1UWI8MJv4#X5f?q%h8_kZ z{#bd(t8B2*%-|~NKgV&*{QPYQ-g)v&#Kf?d=skRQyb?o?Z&WzWDm#5dReX&6=V0~l zn<`oZy`Hyzyz_bQraqImezCs?STjTqf2U4J{&^|P zFy_(uNnO=p#eK=V>)wl^V3?%M^6lj-x~sg64K|vPvIs;Pz*Kf+D|^gufYty*7It#W zSYUim)M%mU`@*}Rw#DL_oaSlcJ$5DiRCJ=@>$B=ax+)jx`_ffE`irA3W#4lFPQqd~rBzQ~L?j~J+PLky|%Q}G8YxoGr) z1`FCz&@AgcjB@I30ZFKd3VPaqSU1_(<~PTh@xEec#&Jx6R-0hxY{&48_OZ5OLtri= zV4}oaUR|4;Az%m?0wY6U+_9P1%MY-9eWR7S*W!%-azu&-^oSNFd0<9{jCE`X7y^dC z{6XM(XPoirbDs05bI#gz&it9Q&D0Pu1Pp=si2#N@X#TKUr>J{=W~}5`b9TDxphF0U zr=EPWgB^WXaNGB7wh=LM&npPxyTEwG#hyO;ZnR;I#)mNwWY#ZeFF2<@>xuVQYeM?t zBo_`YxPHO75p5%7IyA-=(AaH5zz{G5PAdW?N}N{sjkqCT2p9sRMgXH6q84c(0@k9% z&y)g-jL`=MhZqBDA$&pAxG)MM^DP<$E%fC_5);uOh@RtL!`2`BcnqVe=xM4r#0M>i zKzgh-*RbnnOhh!oUkLvU^4nC=@b7~Wxi0*o*9b6+Uxb#_DLwsh*s&pC2p9r0837X| zX7cLT*bD(fzz}GS04|VQHf|g$_=ymJSL*)#I>hJ~8+!VXXigX#H7GH;EP8E<`^V3U zHWZE-hkq@+S2a&<-xM3ae~CooU$A3Czz{G5<`4oVO3b0fw5b>ZhJYb3hyc6gxUZvS zz(t=zc3(Z#I=r^%lCQ5cazc|p08PITiDtl0!ZSwlucJZm&mTPaE0y$cpH$%p53~eORMD{ehJYbp2+U>#Oq7_-t7l_31PlQ~AVc8befM=R?Ai8r zKiVbb8OM&*?IK?I1s2*r=NN;WtfSb2zE*+Yu4P3u1wot5H~esy{0sIkmAd43t7KBKJH(LmZI?GPc(_{s;f zXYjX($(g}$PVB$1mY8$*u3a4>VO@UZm9?I$n7Z9I1PlQ~U`8WgqQs0|9vhq?U zBZt8kwD6_qX(?h*ZQq968)n?p9!KlJ5HJL0E&?V>%-prHkr@Jpz`RF*49&+syzu-g zAY9;4jG0=M|2TTIbNHWsy}N1KtEyT196t#$W(s^FqOVm<@aD}wI3&8L;naXOBu@+C zU1NSLF8#(zk%=f+XMXiVE`aWEK@>alsZ$*(!Tse|JhzL_iaz)8_3IlOkX1lpw+#V9 zzz~=b2$(1_BbLDiVF(xkhJcU23&t0|7QcYC6X$l_2%OpnLFv z2Rc_@e|_P2eLTq9K&m;!9mYy=B0(3UqOw9@)+Z(=Dn&7$mK_@chJYbp2*e{`qC`Aa z*3u9#1Pp-@BY`>NU_k9u&mY}c% zWzFl~zqV{*qEei=^iRI7o0myj#hh$KWx@;AQ! zH@~`43xXB}5y5x&e(KBb7mQ8h!RATBG{~mH11s6#n5~7kw+v_)S^o+>8RLn zV*h?w8*)_WHE<=ORJazLYr7ac^PabBhJYbp2+RfqOq7@nt7Ky|1Pp;Wj==E`kDY&V z%a#Qc+=Pinq=n$3O{f2{T^9`aPasyT-|%9|H=*o&$nJmc^KYwU?R7k`(R&XZkaF9yr6S9TB|@zH47hdpNq7y^dC=|I3l ziPM3)(KG}M0Ye}|KzFz+LR?yLyA?|c~WW6)C8Pu@Lz67IVF^&P|lv>y~S9ok=E?ZgeDE9Ld+U&DQwtlbYB zI@BS52HKUvKHWSptYV&s0R(l`G;?SQ{C@cMfHkHFYRF9 zBz_5wZ6`SFfzQ6Fu-_tj+4Ukq+4Y6FedEi3rUUK4z5CucRMuT_g`~kF6`o(RZVL|3 zWN4W@aiVkY-9Ojm{IZQN6*2FLiF1v`FTl<&8v=&F{6fG)iTSnOHd{l$5SUR25bRa` zBN*7;18?q-i=b{kg7+?4wgC6yusF?tq-}fZ$tOD)?I0@PBXRKlw@P6rT-kX<3=E(q z7mii3$@o?1b7eKV`DORHx9F0ukI=d-EYw2m*wGOmj}QIv8C|p@%z5XwZ3Ef|G#ccw zU_3JRinjnw2L2GNO<0JoRq?po-n;+8?!LYM4b8~FSYn>FHim#9U1Wc5eEh}Z? zGz1KRIfTHnYtHLn$V7n!)-2On!}fEpj{7xs_87|K6-0+BmY(|4)@yVX33}K=kB!izZdJDt6BhFa$<{fQb^LK-Ky*1PlQ~pb!D<-|<_} zqOQF71L!rse%JmBE42g=+qh@Xf)6hG+Af7%c&?qAoa{V!@K-W68n&3ArnEV5?^_10 zE6aTA?%kEF@1ZaKjs%j`XUHdU=wE+ZjE(eKY|VHWYx%!?1E*LTUpn!7BCg?Q5#z>A zmT*M8rlpPuSUzvNYX}$uhQQ25z(k3ey*@TNL%L*E#08 zE2NEMS06DZkA{DZG;{JS&%1sI(u%(V+7AM-QqUA|9iyt%fAt-5tifC}!zver2^VL- z@4RFD6>h!l$Aq=txaSSEFe0Gqq%+#Woq6}pZkK-YXxKGFzz{G541xLxm?%*nGkd}i zFa!*N2m}Zox?$~ii%T+Y$&`;SUp^45G@_g6CVB4Eu=^+f?L87S72AIz8AW$k(U1s6 zXeWHl0QCHG#0lu%@tL3P?r=^G1XT_5Ea1W&qbcx*L&PZhI(G12hbT>G9f+_2tIrTw9|cMrQl9*Yo%yLSKN$ix@0)4;*h7(0FS zlNVTq@4Q8`YleU!kQ@OMC6YsG9U20LfFS?^6W6||ORj_C_rJA+TXa}Hv3PN|`#*T^ z>;<@E4_;fAwJk<>n1%LPYJ3UwSt2j#JyN+T8?M)c71qWDHGUO-`|!AkUf5303&;#z zCieyzwb7!WWgxc&$E1tX#%OmoZ`qO+0=9Dg@oTQh+GyK4HNFUFTnN@%=eag*C!I!R z-I_JiMz76S?O8*>5SV`mm?$y-*4$=n2p9q*N8r87&X!bjWrQ$p!u{IqZg-Y1e~#o~ zAQuB=+uy&o`_Mi2bl$i4nNchxhDjJ2?f%q{7ZzpN{aSbHwMMfM<91CHD8HY|V>dRU z8EFx}ioV8xXUlNn9!6J-7C$pHk~-rnACzMZy40YG8gT*xsL)pd<1uIpE$1&%MK8Rs z25I5-qI0+YF2&&g?c%3`aaK{gRM*Kq&ir|oQuS&57y^cXArOOri4rj=SsO#Z5HJL4 zA%K1Uky9U-Mz(HY_pyKfqul6Cc@dq8uEj-a?jY+wU2*(2F* zJ3H!`10<~TW;y-IraF$u`aI3S*Ilj7z|M!_~I8Ev-gY>O$w>*N=Oae zA`q(vPQ|bZH|nY~lm}y|*R&VTeCkw3=Z8Cd+TMqILo0Fll~;C1Q8;|pt{4J_fFUqC z1Wc3|9k$l9Az%p1TLiGx*Mb`?$JC<(#05)ruElwUdb=KyT~NzNh242H6HN;~ z2l!h&wrf`uU3@{D??H!DY)xtM+{xqb?k-w#R`jm@{;jWY21je85FHDj3%`8s^Ka{3 zzUrKTYtRVCILcRqH6$+u#!X6hl=QT3v|!u*?nk>}rf@ z0*1gmM*#O<*n0x7Vtg`cIAEvMu!-!-xOB%9L;~8?9V5nBYu9~GMpKM=aGws0t6-)5 zYxomz4HxKfXHbRsR0o@tix+oxY(HOI%hgp}*>!4s74GZt}#|}L{!n^zC%@-#z(4-tfKP$%b0EP}i#o}a+a%aQ1LSpF2MDv*PQ47>G|qc^~n2S?fFQ_=)a+96j2hY`)=#C6jl257yEU zFa!*N=|{jsiRs7OC>jEWz^q2#qsy0fnzN_*Q%5{eH)(P;Ed8@HyPLMXO2i0UcZtlC z|784CBKzER`|Ta_I1qWs*Al>nvx~m^uf9WA@uSC%RkFyp-gaAuRClMIe!7DjHqoi_ zT%oz9Zr0FmnnI1-5&oDNFJcyM*h~NP8-%S__vgB9wVy}ZyM4}-1Lc8I&H|FMrC$U>Fq!qeZ-6XCI?7M4Wb7gJG=%>v8kPe@KeV!DXBd zhS~TOZ4L%md++&q322M)*t_;$*ifU=co_DeAz%m?0<{n@QKA-H*4Pj*1m-USH?BG- z8QYkLB4WhK@$n9Y-G10&jDeP4@!ZxL3FyJPOHKyY_l_N%F4?cu=*X|3nK!<&YiLw3 z%G!SCos!!CLnF~31c6w6I(j^L;zT8`M^1g9v*s_pwYz2GOS?ypU6ea+>2LD?bPwM5 zKe`jwoYzIOgBFCbAyP2D*bL*igX{RR+V%a&gTE}A8?-kM-E~)|zE4X&imVIkjfk{n z#Wzb3;k^gmT!~A5Z*f`2fB)KUSa3zo*kngXk7MXz zY$S$K2Y;oz`;)KB3WiMDkJfj5`lkm{@xkIF8u+DZcH)xmXlb8*jqk!uH@>{P>xLUT zSzg+1;_tFkEuf+hoJkR)AfJ*l3w6gPXzJ%%Z{BvlZr&p#%?8D=Kl_ z@X_y;Ie`fot??s4D{|;dzas_z3xdF^+jUqYs-b0wb(g0duGYuze{1E~^6{~&hJYbp z2+R)zOq7@(>uj?$1Pp;z2$0?R>g%s}yWU^u?AyCHIeUr5g9t%rB$izMpSvgj?LAq# zI7uVdYV>1c80C=qPE+T>if8HQ5HY;;biK^g^$Eg$?>%s!Lj7y^bs zh5-2w2*^r#;LxFrP0bqAJb<;w2nVAb(wUJ%f`UO3?$EHvY9u5^K{fl=<|{6}R1BzK zr?tPT`g5OuTcs96`zOK>uFQ-Z_S+wK8J$NTd8Y)3#eV}4NBb+PVW%-HyXmGMDW8Wk zTR%xCt#s(fARQz>{c81o15zbDir?efj-D4m|a_YDC05X&jf;H>^knUn)dja zP{8!#*Id&fHv{g{739VW$Jrl0eXse;Z_5hW1SfhlUMGAMFs4%6IM%VJT>r{>TLVMD z5HJL0I07b0%s*S7R)xL48aEYm zk$ahOqnV)K>WuaSQM8R$}v(mvxqY8a9~}a}+X0H(4RXkVQ+*>K^*n-_GXY z)7iWKLSdDcuUy$--b&-6%a>=zdabAy8ZLk?F54J{`TZaG?3=nfc3mK>{>sIRTcxX0 zI*4*U4UDOF-troW;)Li_G|sx$$saO6$BBCwtC6X@ZeKC&$=FS_EBs_|@g_o6%yUiK z;EQ7#ou=s7bB4goMZiRfnY%VNGDE-+IPC}!pq0Wv7F}|QxL)Ia-DY6qQNWd3Gh0)W zMvibbiR6Uwkr*IF5F{NN{t>X~>o>f(s|68(9em%AN&79lRzVNZ2RHG` ze)54U)?m7@)`(|Bt*SFGSnXaugu&f=rSf^ z0TU%=!YbGp3;{!61|smj#m^M?;6+Q8bgui@4|M&KVz}>l{P(k+?d8j#)BVzk-y7KF zAK1xnND#jSSoTdf-`v@E=g-LQz29%qaUF6P#$5U*U)QCG3wB?VLf`5x9nv8U+vJH8 z9W7P#&;$@TR&#D-S_oew!Lbutn5T}1EZ|jr=iMi=R5)hN5%&-)F?`cPHuJnb>Re-9 z2M@g2X;8jP%9&4{>YUiWf5D_n%~z+N*tTQ?ckjeV`i>ogwKD_^fw_Tzi4t>T9c>nd zfFY0^0aB-pFC6QJh3hGZ`TdKr4#qi!r5;5cb8&ygw9rn(P}A}E+86idk3ri~>lkD3 z?6rxb0X^+MW*+%%@@Z+mj6MIkW>zFa**eV4_4iEUhO)zz~@A z2>ijqu})d3n>E=71f!l)Pd`0S*j>k{$uIsj@-Og=-%r_bZ|n=*92R!)k?{NF*za5K zze|s8x&?zXxewK@Wcl zg4G^9c9F~vH*#E{39joG+BvKXclYsqpnV0o>^A+Sh(MKfXa|OXAz%oM7y%O{MhviZ zZwMFyvjqVn{}5zU$BO~Te;hq3>?FZcwGf3!+q9{1(_K0K3~}WqY7*|&YWE-3ZRqeU zw*46A5V0rhoAbjDy}gT(6>RLjJ$s^AUw>RgiNckd;IgMC|IZMen>YVp7nk5?9lwZx zvxV%p-=}{}X0-6f*n7{NxJDnl@Sq(#7dcJawK%VB8tGG@(1`?CN2o z5eq1Fv=WFhjEz2G=Ihw_&X()Hzj7{l^YgF00wzk#jCHV47y^dCv?8!EyWy`_~YY*zI2P7vGwdn{Ir0=3Y#cC9-;J zpRqN&^Yn>3GV|gX(L`8xd_O`Xq`dQ3Vg>DRktYc5!3Tax<^u~a`6b9P5frd0=%)5% z)@bvVEiRbvwaNOdJqUZi5HJJ`fzyb9i4vy~d?RfL7y{FY0Cwop8(?#ojmE&}-~$gh zmiGZ?6aS&4>?gbD_yYLigMUnfsLy@=ZCQcs4$pVqa!ZGFaj_}m*sUiz6k-VFv48)g zj6NcSPrvfK!OOsSKXd>6S;1%rzXq-q(aY{Ppl6R50)~JgU)nBMj8;+9pjNNe_6B=p#}hP;lRCb$qL3V z_$yGzv|X2u-+tYiH61c0-|^|6mRu4c?eLtu59_Xcepc4}<+pXw%=mrA9;3}!yKbG( zXAGL0J$hnd;(RU6-=MSMqu(n5T?y(cg^b}&oZ9d=FPs*G zBLe@fS@F#x9{OV)-(GGYn!8>nxO?U5s1D#uMyUn>AFI%9+f2PebsqS#A#)vA<bLrA&iFr%_1ogq_pQ!Pjh(qqhT(IsEsbANZNJ-#n>o^RR~F@`8vh>}hFu;&Z`L%r-DP%I%V`_G_>(cPs zk6-*XJwFLg6XI(I!oaEcH4vo=OfbMAf{=3+mvY^aBT;NV?TC(qHsbOB_Xk5-0H1!@ zF@{h6Tw;##(ZE=WAi6%*Wpw;|F*J(WuvP@|)q>kK$GDsmJb2>T7j@APAT}YkO=m=< zqA}ZbL%v+l=SY zM;>wM@BXjMv#16K#zuMc3txy58Op|lb_0z^Hb0$bPE9-5Yoc3eUDkKiJ?5+Z==v7T zJG@@ich!BcL#xI7sd<-j*IRL@>M+55u+VuX=&rI07i~;j+ZQc4Yh=Hp%0XBIL%9dnYJP}9z(zoh(rK);iJcnbqI2L z{QkFAvcECi;*yPxJ-f}=)pJalJaIy@UW;bK?KC@A<4X3viXJ=L_wIY6?C8VVlNL?; z&#kLEboZBE@mz@*wHH=<2+^GpJt9@0~h?(zs{P!1Nh=uP&1bhQc5Fe^& z`1i>iJ5m2D)8bV?vU{m45z zL{vg6aq{@PGdfC7>J|jiNZ{gq-N$~QyYJ4Q>7M+z_vF)XBEdly!1RGHez6ft46BQX zh2O-$*ga82uPYryx7ev6df|%z%T2+JTl))Zg0T{*3E!CUi!YYQU9og%N0cixE@8iV zj`)gZhC&XDHX4xLxQ26(KnptSulv5P7!%c*Kt)@5hgt9Y-`ZU}ajv-GBR;35qr>1g zislV%i(a$Z5HJMh1_CBZ%#C%lSr`KI3<0%~ryd3SgD=4ycl?AHw!qTq4z<>);6{wV zcK{lrvbBeXjytA?rED@k^<8-I3-73&Pk+8W_O;q)1DEx@btsw-5u?;6qsjZh+Lx|J zuwG4Q3}W09CCcX0p2if#GT#n-A5zkqqH0HmfFUp!5in6=F0QK0$q<;+2wb#n2)h9W+90^!$#`{A?E%nEnu*!am=(_?bi3!ZrivZVuYF)@Y{Itp0Y1#Dzh_ zK>L;_yu*se6BFkyp!h~=ExWhJ4FN;I5EvN(CQ6J9TI<*lFa$<}faXjf0P42Ce{Gkn z$)lk(Hg;-qvV$=QyTyJ8-MA`~EgB;z z{0dUg!4PSD;aHbB>0DsPm;TwAvfGVoaSA$&m)&(|Se4PicVo?8d~26ru_ES5ONGD@gd1ts* z=Y1&~H(A3GV_45fm@1p)(L>B52MOY4Wd}7I@Ealr20k>DMVDOCxoFGFlGCX< zT>rIiv<6M0MCtPnKK!d09i{i!zyFaFeQs~HCfff{?Y+|&Zs9r(ZbR)7UBK4`trW!> zCXS3wqTyOMhJYbZ0|65yYJg=;4FN;obR$3n7a8d@ zvOffX#dw7R>nl6p-tIT4>aqc~IKXwBbZuq9Su@%#=#Y;ARuQ-EtUqr)u$1h6<6fTK zuSpMsFfZXgtU_64Osin;knkk|I$1r$$`i9igQ5l?XLDM67Bb z(6)G{2k*O2G>N>6AKP`o2a#xg)f|+Gl-u;zQ-N z3WCs4YPf^jNS8CI*&j8UdbsTnj??4M96Y(^nb8$tn!^HOS8u~$i7X) zC8A$x#3+0*xJE(4fbT&CjM?oQSDhmUK!*?i=FmKG??wxgHBNZo-ep{%C7PDbd6aot z{Gp@uBiF>J=H}C~V?)3Y7##v8N{kL$>)8-61d<{E3rN;xa!F8dDc0k>9q6XSVF6(a z!{DQvZ}{QNo*ivM)Bg2242sYM_&fQqlBBC63KF~a)C3@H6Qv55-En6mqdU>q`@^0( zSMFR^7q+&Eu@lBXtdHd5==)`mN-lMd0<;R`{aCl=c?0XQ_rL+kP_Fo)WGMgM*uXm)V%M^r%^+J5Jqx$EOU)8<&;=e_$c6zyJ{zG9w5`-A~nlXoko ze`{k17y_pU0TU%o5AH_Q5HJJ;0rh zBoq-5zX~^=mfc4^zHiSS$qm9>qaH}m=;PO1;|#Fcg#}134C}%WFa%B`0wzkFM(~Za zAz%m)w3MKoewp+lRD3b=ckYfBO<~?XF83d-uDypFdC}4V_Da2W^6~hQ?t!Cj28{uW#MGyXBn6 z##k?jwB$4*>s^pu@^C1fu*P-z?p?cD)AX^z^&0VkpsIO3FNhNu@%YcAI>yBvLp%yT z2%4Q+=ADX2Nk2{tCB7s(Z+XqMh%Sh0Xf&FN+a*)6TZVuk&;kJyC0c-Ij~W7oKpO;L z>oDY?;PWu*v{k!5_2UCO)#6X!A{V7fnHFL};8NIi-4WNCtjx}Rd?f58F6gj$7+_&s zq%>i3@ku~~KtY=jj<@YOVj;OQprv%R{@mx^*2SO(4bWq|b}i6?2-%h{#%=73hwH1f z{rZl7_$lX}J=vkOrHhR}+91`&s1`!$`PvvUE^A9LcpfbAb0{i7b2~Kz3;{!6BnX%& zF%npOxW4o!_~H-T6wqX$NACtPgT6&f}QuJ-uRNMP4K4ieoeEjjDW5HJJ`fkFgK zlqiJ8ZWscFKr;kzziswp{8Q{ilaB%S;Y0uW+anVV*u7?_9b+BXMJ@3oEpLhDV%)hz zlZ)Zx@potaD;*4h2&7rwOZi>gl%YXJX+IBx(!gH=)*fGt$%e*dMB_nJfvv@WN&A-C zvA6`)z4TAM&ed0Sn&e18<3Y3}@?+rEjfgZr!>tWs;+hrT>>_NfY=swSl4>vPpL5*g z*G`<t5HL~V^x$q(4S_j~z;s1uVMlz? zk|iBdxjCEpq~Zkb;1~n*g#7IrbXq76P4`drkhRUq*|Ubg{6@e;iTS<$HhV)LF#?VykzMx0-R5>Tt^rORt2zo>bR{}WXQ8+i z>-`k<6mB|~UJY&TrJz9{^o2`w53fO5sp;g==+RCynsu|A`jkJ7Q)5HJL$69E$?rW0+WWeCh^1lY;f^l_t{ zC_Cq*kHeKW+ci!^@WxfE7M%Ij4|T|RO@yJPA9;57=&_5Y=(ru@C71u_Mj8Tth~i_g zY*`1xCX9733d-iZ?p}8 zIfVd5JFr!hqV#7eCcJjtx(>MzuxD2`QjCvMbl&d+kpi*krW;@0^$lHMxyj#D#(QJ|hZa6T&`#BN*bm~C2CUui(eLHmPP#@RJ<*CWFK6I49sXP3hmQ+e zD#K&3D~7<_MZiRfxx2PDGecmS5pXQA#MmDtJ3?&vyKcW-T&`i~h*Crl&{0ekKZB)z zc4n4Q8Cn#{U66?2MO$9BfZc6G30OL%K}}Fqv?6U;F5H~4!$C|F_AHg4XsMaoN_ zI@M@RV%E*t7y^dC+(y7eiMhSLHakOLnh?MxcInb*bzzCuUHSZxu=Y%aoppBp!U#U8 zr+M}D*GuFkv=E^?F@ZNz&_j$MauV*md6AWvC&513rsN`(*`iCnzI*V2UmBVV8kccr z1VxU5Hgw1jAui7+Pj(O)Ld?(_5y7d6Teo7(ZZFT{UJWZB(m)g=&@U<5+K3U*BWpH> zVM8<=tIzH;@A|WjU({Gs(K$`k3x{Z}zWN1jH~k$6X-rY$cS>2m;l)nlaeIfkAf`{X zup>jj5HJMd5HL|94kv462p9sT2yD9f)e;DF_a|THTq?&#!v09)9LE-un}A(h-60l3 zn$pqLzxkfWe}4fPsFg-o*YC0T3Fd?UfYM-h8nzo_B-rD1Yo6CybeVKfjbQf)Vv1h_ zv=Ecn{XTl^n5-8DL#zMlJ6h8MI}CK7fi^L5-U7Rspruo3A1=@&$ z^?S^J;(d#s*~=#FB1h?5v|hgaIk5Y!cl0r0di2n5NKtK@(m>oJFf8knKf(rAy>)>O zL2eO|3FezB@D351Ke~K*2Qhl!e&Q2+hsflfVr>!+&AKxL<{SbhO3b+> zwFwyl(~ST@O))5e715G<4EPJc7OYryc6aB^ubyHZ9gEU4q)AJW{0C~7lUpYuhu_D( zJ#Xl$4Y|@nBLSvb+LA-O8+Y0V#1y5WHJPH@Xe7e1L5Ca<+CMc$VTddkDYZ4$fen>> z9B$kgGr%5(&3y8=_&GZg!4m$Ri75}XMFmRcXkLG-S%Aw>GN#%LWqril1G7a-&JwrUu>bI!-S?P2^V(n#hFCPPa}RskXC=|@;6K5R zI5oZ@P4a@UKJ1+1CLE5Ra}1KuAhh)@V0|zMD_S2!i2sY(YBJZ|aAGAlT3C3r6Yun};Y5sJrSRc+Gy6djwbo?>mB9GYW zwgg5>$ERt>hJYb3dIU_A7(K>D!4R0^2;g?B0WyiwgPnQmDPur|VFdQ$%HF{O37hH+ zCsNVdzx5Rz(!u#`meTR-HuMNIic2qsS(4$pKM%iuJw|Lrr}@HpR_!D2>HEdaGg=OqFrO3ce;w@DiUsStoIy!^`N zyZHLgcadOT*1=bTLPVfcH2i)LSwa>vJNFy5IEJ;r*8cEAZx?@ptp7gyiY@!c*Z-H+ zEET>VXbcX0>34?MVvIr9$){ih)nYnhh)2C#1wCy}I3L!FEY59c@GP#ozDHuo=wX^Gn zfFaNh0TU(K;b%`90`ndL$Bq#2bEu30m^?aqEhRnev#d@*udM&@UK@I0UsdNbnO4=< z!}~LNF8z{k6{}s_oO~?8(1Pp=cM!-ag=|83Jsq!VAdmmORX4t zjCdxvY~%Kffe&orh{=sjTzhR6@O6~*$h$#w9zW1$nNblDmz<$VBO?={4#8%Jw*C_A z^Dn-&yC$c=a`TMdBYjy6_F)$2Tmv06R(<2J(z=2mW5O6KoL6`bjqqA{j!!}bxiP;V zd_gck$}9Myd{h=F{s{gFRHGec{= z>xLV$Y|-M{?&hS=p&5y#$2&Ob0+#OFwylG!HNG*5RS_;kb;5sQ@BRz3H7g(V4}qQTz8wbA&`T>ky9UN9WjQO|NMCzWxRNH-T&k0(GEdD4}A7b!oJ<{ z4?mUcPcg<@L`@=64_S>5{mXC3uDY^`)Ub{@5Z1cHPBj5V{jAEe6Yl!gT4^%hTAlYB zlhzFP;k6Uz4&<(&UuY;DYP2#0Sta;vteYzBaD~SB2yK$@avjnjqc;XktQGF&bq(Zj z>)!Cu@6FhC#U`xp-S@^Wk%!tw{y_{2G17CiW@OgpJwXg;vNprdOlfZvI}E>YgT{4u zxP7}Th%TgZ(}LEgEoSXTWKSCchQMq@z(k4JxN0_LLm(Ofc95Ozwv_0H8aCXrabw5N zLfx>N(#NnthqYoiR&4dXfSsie*0>1`(sYq!8dfeej^UgXay7afXbM|Mq$4eP#z;yN zws`4Bp4~-!8t5eQgy=%}B4~fqB0hMy={;C{-j&F%vu5}Pz?L5P$6w4?Z)Lfq_6(fZ zVP?md-QTQ($OFU`7bUE4O>`d6#F&hIpCuhC`^@rcM}N&I-+xc&ag zD}iW6iaInask6uKgBJd&cZI^ zNI|WNPH(O$D2u}}&k*u!JwvvPdA zvt#@Dy&dAdy*)eYtN-ddy0|q@CNQ8OPXPAV;%?0Dcb^V5JKp4bNHHh+U_NSi=F`Dv zg7k&>g0!K97KFSX7>Dit)Q@LITF}DP`M|wz5x1NzWvBh;J9Mh z*}UtOeho2mz2nnAJ+LNnQDQpLHd=~!^lN61&$pa zG@?mM5%*+RMbgwkN9RSIGLjYI9&WzSQ5s6`*4uszH+ClqrirkFf5P%Bo}1B8da%kw z@_En1L@O3wS76?_g}?F6}3} z4t_(>bACC=<`a&CfWf?jh~nWrJ2wOjfwTyiD3KOV>&+0DK?q=v4x31%nNbc4J8^=l zV%wdubt57OTZL~yM#r%zay$Zhu-@X%tu$OsX6n><-Y{`)2Ullypmko*Auubs9#Tz> z-F)b<0!&_R4n;$xCCKeiii@ke__G_y7t)^ocLF7mA%MTYQzjGY<+GZFz4C1&KZ*q{u7 z6bKNT{)3CYwo6ITUr`?z0ujwdf_p04UDUY6b=aHLSHGZ3jDKYvaT|p#Y0>S_qZouu zo;)e;%1VctU3EmJp$A%ASaiuHoqc=Wkj)1=u;6GuFtTaU^}wUJ(H=c^OoG1pb7I{P zF$NkpJi$1IK&j-fPy-z`3Sx&C(X3(D*<%>`pxMBtpNLTYTsX!Z|Dk*CiH<;(bsQWZ z?hplvK%F5Cg405u-xV(U!J~~%Lzi`lrsdv?@r%C(m;2VZg@cj^WDYOp(#tI|S|dpJ zj*0e$K*ix$?A#DA1ZFn^CQ8ii^|R3%0woAwe1Y*y%AM_y96NDDe|hA=UzW6Tuw4=m zG;EmQoWQ2g3RycjPtd=1G3j=pVc`xQcymVU@yFjQcJn9pU)Z5&+Y~jLqi{}(mYkJT zJR)c-q9wT?)OZG(?A+dU`|U}Cjp9E*2JLW8N00umF42KfV8%}K`s=>0OWAkl&kW(m zj=M%AYC{j!7t!UxFT7)j2J<7r7eR1SL?i+;($j^w={oJ*zdxB)0^=rMBv+bf77=aP z9%zHx@UuO`GmNN?eEG{AEu%8N#K4X3Uh1esNM!8rRp9z_R&17iFIYG|?Oo z4XNn4y4MkvF{DDP;?v?dr7sI2Ecq!|`;ZRvf)*N|y5lcufBC9&2IwG?qctLZB&i0A zo~wJEjM}Wd&WUXHPBfEj=ym#v)p$y7lm7PsUnl%r>d>j%R@Teq;~>AtC~{$DwQ@tHjZ(;)th92{RR^1HLAr6Qu+Ap;~7roB=fopI0Z0tFj57C0< zz6U)+8t4+Zw~fYt-wmQ8JLpAofsXhaI733jsMtA#?fAX2Mv74xJ;aLe`MT#AoB6bS zdKfFR1%a4^c7k`PZvR8=y?0va${IPB|2DV{(dl16E3o#e?{fY*Q-yC;QG2^?2p9s* z5in7rId=A>A#fTIK-oM>}w|4N48F#rI=$m4OBne)6>%tH)1V)a4i4r4+*a#Q`O%PCx0(D9Ql)|xi zyM|~O*v%_!o8U&C&jZ))-nDBa^JRybop4xBe^-=aY^n)fI*Ndihz6wP(txE(o0^DF zsoapmCAo3css$o0I66K}L&s&2p46HdpQ;E7p zHg`k>=%Iz-_myH@v=1~Z8~)~nT_Te~hp0`sbrT9e+oY`NA5o7Wx~v!OyD9q*`@)|I z(Kftp=Z1hGkRAaOCDLPRJsJWf2w=!?`Knb?N}9F4g^TNsT^9_dplaRga0jG-!YveA zdsq_6Dd+Bpz4dVG=2u`DVb$*V^iOAWpatDgxRtZ>8RmOHbCH_WA_~!>#m~%k@D)Q0 zj#9JUu)_yF`=$Z>I45XuTOWyVWyw_#(mHtG|46P8K@7X~w%a=77g6l+T^JvKMk_h^~Zqky?ud9de5(c76@Ec{nt^w)uu1 z7O~u?!7-vUsp{BfY1()Gb5njZ&_N@I@!hDla+WWDj^wx@A{yWMQTTxP^CbH@qHL;^ zkAB~FYzWjrz(k2UXjxlBpfv)xOybr{Y28^x2c$Z~o*dSmXigQ)Yv0Cq!Wr)LQj)>S zIpY#oIs$a22$mWS2=eOtUjuBey4uhT28^wA# zR=77O=(vJAaVi&bor~6J!~FR$uTjv_-xC?TeR|{0=Rn7js)l9L-;=CUf4!iEwk#DL z`hk}CX!O^Lxo!C9_a>hcw7^j2)t?V@TfgDOBcpZTbHC7g&-?TU+b{~hFaP_;oX8V{ zrfL+lz!7@*IcedV{WApS0|F*W%!gIBInEFSU^m#sr=(^*!YRVy-*n^4d+e3_lRYZq z9C8GZ4vbwtSf7C%$8bHbd6XqGzY0&h!OczOXbf8BQD&o+u-Ftivb#jQ}|*) zhk23Df;CEQxW(@&6ZiV>9OtR+=>6XlqPeaWJNo|IQyfE!cTd*{T3|^eFTyOOpyBty zcS31VW28t>TE9)wW4=lKwSv7z^O3awpffQfbu=*`95pRS^EZTOX81(~L$OmsU|JC{ zQDR!*He&M|fl=AN=b?(Yg8le(Xp-oviu$wz;;n<5aWPm5y=oH9C&y(it?t-E(8WwDqOIV>EM0$^|UN*9y%{r$JBWx7%K)x zN<(WziBY(Y^L@ab89GG4B18b+sZ`i0_J}7$2NTxb(L$^}kvMC-(xS$XO0mNQp70g^ z{nW?2bI>EW^T{n+M$XzJ2I32n-Y6_JGS=UGK)^(a`LN11$J2}ef1a_UrHK9W2*$&% zGj6Vr{rexupKIOCf6Hp3qee?r*7=FcTNv;MtN9E(wpqXj+e zjz>Ym?^AZm`}9^UJA0(`*xe%wud*Jaj4;EzWWD-x!<8420Zj-ch4$*B%a?c1Y>YdN z5v-`rE3YQo_OXKpJ3Qm#Uv&%{=)@{<5k_Rq>$~h4&man5q!hzaB9fQ&hZc^gM0v$$ zE9nE8RrB-36rLfgI{qH452efqh|I^6Cr&t#;7Jib<9?q#b`?DvH3llvk-m)xGHQ?e=||pKi|uOyjs$mg zN_%$VQ?R#1V?ZPlmug3g9nviuH`baP>~~S$dDn@L4{L})0I521CElKRy}wM^MNEz;BUx`b8q9IZYbc zw3El*-Rq;A+!zKq>#zGh@iD^BNDHD<#iQA6(&}ZJr|jzJ4%or~pEWDKIqH3BGZKQ+ z@~#nL0gYXozG9v|dhD3Ep-;zmkMWQ%Wbc6k$+c-QbG0^xz-&XnM2Xq9Vm96pAwU4e z5p_M#Eo?Iy3s|3l8zVTuAOb@cu}@~#ctrn*h(gLN0<}Vm-P=(an!v&k6jSGfZSpAA zj=-~s0l0=j3l=oBYd6dBzQxb%Acn60+m~c>f<+sJkqJ?i$Wy@jD1O`i?nj-?`u?P> zAQ~ME;GiR{zjGhv-5_OKU9qp|m1W0%(IuC3)~x<^$=t2;!R3`q*uEH5)JN=f$y?#v z)0GB>Z_tI+Oc@ObF@^Dq?}B^hbWeT{8^=@7|u+-E)U{%M()(zAN@AYh`z>{%-t z=|~WOWqiT-!q<+Z^M0rN2_xVpS!`kLF*X?$J^nO{SQHq8jDp_sl`A_K#`s1j9K)KV zqz7w5&_fJrloo%oNfkyaxs*{%FxJ>|{r6{dphqO9)ImSNhpfz@D=ONm)Oj+%Z}Zlz znemfixZ1_@zii`6ldcu-AHE7oLu-tWQX2uT93OWXs4pC#1%8M~scFF;9zAwZMvM24 zG<>7Ts7$n>%n*zB&(Te-<%0#qUj#oH#3EfMLM9LyE9LK(z7Y461~nokMrf%65$m|c zxX#gm78(PDn7F{Qs@{CaatO{-{5T%Rsly}71dvB7tObs7bo>BO(M z9>eqMjUgR=MRa<<<+OCB3%?>d%&B)T-Mo4|4X+dLU+UkKj=DF89!==<&h>6_-F7^& zbc#n{cMO5qhk%I^|37>89&}rB9fbXww?qXOCcy?wY+gNgzcECxn!f zGIoeH)mUqSEB%pto;`* z9X7}bLo9(LfsFf{k5(H*14yVehn#RCD7dn0s;zd)G7$dO6)reeLr?&5E;3g!ESPLIqtVc<=DB&t&q5&65yB02R9(nn{c#I1j3qH8k+{5CNAN}ZUNo%e9prkz3e&RdLxk6#i>$N{y6xvB$B`vm zVn9|v6Ckj%@l1{2le~2;Ttb)+xGH?CO4fwmTDVwmJgZ4JC5woJ#B0WzWPfvQ2-i18 zzr1&e(mHtj)n5^Gy9E3e=9=rEg}t3`6j(}uO_W$lv911nL;;^t&Lo#IvL~s0Dj)x@ zUw_#1LH_%^cZpKI`(&-MTpQ2NJpT9#X#n)iFZK+0#Ot?y8$#W?U>`c8{hNTL2;z)clV67hxeSIB> z)p_B=&tFwM%60YepZ@8{R%RXe*$*V|ZRJLRjRNaZU=tvI`Fm3ny>CgqFb?uZZu`4x*&#XoH6P8Rw#(GBr-3o4vgJTQr{#6m#-uKS0 z>1OX}u#e!3zzk)|PCHQRyWgvx__Ys~JvdH= zU*pPSq6>$g{Js3@=bhB|gtwI&1vUz76u1crY@)3{N1 zVhb@DW|p*FQrB3p1=y@$L-*R*B`}uMrI9D1m3ZPcrOvB z2dk}g(LN&|_>n&vW4zQ}AZ=G=DihyjzoPcucD_;IrYNw95;sNZ4NnIJF7*i;Le0OK zhFL<#V7-Sbt#qw8emsEfNxxm?N}#WS@j1h`B`)EHLd&mJT;T%e5jopAIMHao{*C|o z#X`#Y#pf1R*b$oV6^1Yf!rHJ4HX340tq(C2gO$&`_>RB#_ktzP`C2=XZrwd=1MLzQ z(QM5`x{2%iUil@vEf@=~TpM-|&iiYY;$-Z_kz*2P!Y_YK90`jRC1XHxZREX6loH$K zxa@IK9NsQ6E^#Fhmb_mZ-WO}Lrkp&*5QIaMby0qvEo+d&+S>FZjwkIn!m;?RjiZLg z0W-!^#^_yD-@C-|PL1>K2W|q_CHlX?yuT>0i4ynMeBR@uDb|ScS-F|ytoJynw(jpK zaCLzstwY4ME_caOcL^qk>YQ>-|BvA&{I)y=`mI~w`$as9xP^}2u0Q?wmcp<5uxmSy zYxiF0??1S`u!pga<4V})!Edo1<1+`Bei!QhrE#>Q+|D-&Y!tZvD6okV_urh}qa&hS zdGq)Ej*Dp&Te5I$q8XC~h*q<`zt*|%MVdH1;_w}QYayuMFm|&H9B}OM2^FrYt#T+U zm0go-T(p42e|(iN)Vh;sm|D{sAD{KQ-WtbZTZF&Xc#P}2|K`6^*sgW8r?Q8&ThzfT z3(Oj0sbPHT{a<^#-iiz!=O6mWM@o{|^&F4jG_DpG&9FYyeOlvKsCY|5yKJ+JtO37< zVVlC%AxP5OwH(B1T&!TW!K>GyV~$;Qy)}+y@v7&G^OjKM_r3F-x7XfHIcQjoE3BDi z`@}WI(ZYD9Y!z42ki#PDPFruS^LTFe?_6{B*rUJVVz@ZM)eeg?%&omG<1t-|sJkEA z5B%UCdJt`o@2`9I$SZz%I4W{hyu|jP>-Khowuutk8n{a+;MJH0X*=^?vRih;4P0`m z?|+xT#{uZEZ~Nj0WyiTRKB1Zn>^YuG;)5%~Q45Qmb4Jsg$4kO#d+5$jy&X@U^S54e zcV6poXuf0}@_XasU%uMfv&aE_=xYs!l8DjT!Tw2LBh}V zt~9PSap@nYrUQ;@d}}{naJBD4=hpnC)?xj=Fh;ERC7v&1^C#s5Jdc*FTWhbu9fsro zQX1sCIKH!;-HjC3M2Wj`R&LSw@`b#q&hcZtgl6rg`oH1XC~zqX$n*cwZ~yk&GWOWR z!cUUcHSiTS_yNg?{e&yCvON8v|KyJ4_;!1sj5HXz zjGk8|rodRuiS64s{@4jx_Mw{I!j*|plJ}~A>TcVdJz3f$q_y2&CI+@IjX9Gc>ysb( z6T7V;3!ZoVgWvz)Uw_@}ZhN*yhOy`}+8Gxy=BGaRb>&(3Q2dCCc1_(~z4;>GUZ1rHh!SDcR}8RZa11g=Ojc!*Bl z{kMN}^xZt#mDw*71RE0zRU{}LgmCMYvD(7!;cxlES$seckBJLOuPpVKRcX$0jQtnq zvm_=(b~&Ot(UN#X%Rhn5tod0XmO*()cS7gZ|55Yo~snt zM2Y9>%x~k~C~%+vE&pr&#&76icMgrtIH0-z#~=J)x7I#U(V)xYUs%c5?bwdTJxN-z zn7v6>B_t*)U|DItwXJ^6H)s=nwsoxF@WgjM!t3WdC6YskK^{&Bh zE`-Lrv`dY0loGI>e62yyoYH)=@YKKW{qBEtCz4dO8XQYCKH1a2Xux-$5=;Kk(_er4 z8(;F0+r*)_z4L3L4~}zxgzN*&@|e}28{tI7?|#j1jCo6Z`ZZDg>{}eIVlD3Jd1-v{ zd{|HWb@j}rK6TOY!B>xugBd5lvk5l>fH)6F&L7eAlIuk`p7+mxHpJt6-l;1_y0_lt z>iN3~e4NwJ)bA$naq_%Lda}w<9eC$led9M?e*V^53HLmtO_aFjr*Ipm@;;;TM^=5UJ!RU7j`Qyj1k zcZUy%e~AxW!~hJ}ynZ$DNhEsx?)57#{>#5G9Jdl*IBs39HhP7H22PGXL$NuMJ9BJG zD@*iGR{lMeC~?F~5tftNl2``LOkyHv--mvzTiM6D^qg~W(??=seeY7o9Iy^F zxfGHLuK9%Ts;Drk%NIK|Z{EK>X$UUXi4E{ZffFdOi4rFeZELbo;O?XVqx|1VVqG{?U?q}G!}hXntc{a~msXqBobey-2vaU`1V8Y<{X4Ut_$49UKI2$2zT@pU z-uA9{-452Nz0sUY?#idTxjc9(e3@fW>zn?nC!UC~?;bHirXS8bSll^ZdR8Q3h`5o` zBRf#bJ|vX4H)*BVkN3Un-;Q|~2O%7KaB%yJSX*o3Z+_2jiS@$S%KS~WY45=qyTuo= zfOC`>lFvT#%x!&^cmC1hsFA-&uDcf>u*N+!Z1Lr}uxV^NoE*?XGhf6LGz`bIeIGao zdJc%hty5RRT2kiv6Z2q>hGKhO_)WhctP)~YJQGXcpIR3yPXx5)=GNM&INEkQG3U?3 zj`QukOPya`{k%wgx^|M}ss6s^JAd7zQ{+_JQ||db+?2!LDgE2p+*=gbM2UNAzP8>z z=P2;fSH3c~;G(s^^x&~g2d!Vx#`S1%SrXDIX^A#8#!_&k0bDudRK*9J;|g{y+t5I} zHr4KkW(~fhd;1gP`P^qdbJ+eVaVgP8DIB*;M47>dUa|@$j)XsSE+Oque~+&T*dVKY~w*9Klt@*^+;1CZVOH<+)!)IrA;o$n1d-KpTYlk&nVl}Xq?Pc-FkNxRu;}cRXlz+$ph-d-d zO=KG?{X4AFa9EpJE9c%$aRobn6OK6fuHe9SZr|tHv+fWR#c5p9s+;}bP-q>qc;>ZB zmK@Po2QP^uVS85-pO-qkX8TjhB@UXWz~9=8Q(zM%#%Z(N*(k6Q1!!pHocHV2pr44F z6H7RG?6t3jot*tl(T2{&DU5X|eA6pUujH3wbr07In}yw4>gW>fv8@%po=;DC^<{ zk+o}2PhwNAW*IzU3U(S!b1pt-2#;&ld}@@j*qmf~Giuc^g%{`COJe)Fx4!inPBb*G ztOp+_vKr2k;}EY4j0|xG@9-N5r|*q58tMo3oEXb-DaVi{yhJ|Z;n3!~Yv z5JFb-XI~4ViaK@Z^&0pR?^t=i<1_9M%gP$M33|d8ma3K33wU#XKl$|2@hqOB5}U)( z-oIozfEL$weun~^C~=1t+tEgW3sHb02i6CB(5r>buF&Gr%yES2+4+{AKCQj{`dBua z*g1dp#Dz`99)9|#{`=UCxQDfT*E=2zjjcTdXq&M&yH8|79WIGHSZ!8?B^htD;eRio zD#%!9tj{o(J6DCn(g4yj-?=(}1Ya$V!6#N=u4N`66~8i9H}4ZU^)cfXXxYqP&pgO- z^PV664`R!?gf~}83X2srKJ0!8p?(m{KiAHE)(d@IDjK8bs~-RIV1T*o;Ksu81D_ZUFqZt4tU3=w97t)e9J6Y957?0$9&tos zWx5>F209}?=*@5bJv$l|c;=`5#TqI5oQNI47D}%`^z?2T06BX}Y>>9#uOQvUm!PMn20v|JZapD9^#n`*zEfMFt!6;urto5MOAy zg`K|YJO1S``eXakFC7lQ#OL_apZssTKKGn4>g~&-zeVfs8n%Hr#CQ6$pZ|QYkk}i} zK*SY$7zEx@XG$BM<4P-mHnFEc5zVk|5~)73-;05BLy9Lw!5*R*);yh+_=Lkw^{c*@jw8{}vR<&jy&@e^&YX)=sbP*C z6Q9FUR@yu&M0ziVSVu|ov9_+KQ_Y+&y*0%`65;WMmaI^m{6ZAl)3#WaX6GGlXc=XRMESCDw%5+)CS-LFtjlzY~8 zFZ;{65KfLt){Xhh9}CRbdH_G?zsDk&{j|o3$kZvE(5$~V{H@>gAj=2V-gvWamc%z= zr+)egL(hAcC}T(aLPvDauS?)an6_M`CNQV01#RkuthYu<1oTW8m*?+P3v^Fk!2Sz= z@e4x4<DJ&W$D^VC>i4xVR^xG> zsFUN1auCCvi*I=CYj^(n`|%K2gmC9L>+w0xab@Uvva@;p4&>pZc3g6-XRJayz8Gtc zuYgO3tR>^iap<3%@zz;AKiSLndX6W{nhx1!u+09JQf41(S_yX~?c6tN=}@mr<11ou z7ke0%pn@y=xwJIjy|{E8osQ^Vjd$)kE5;IBNni7R$~Zj>31ey5&Rfo3TV9(tdlX#w zuYL~E<(&6sPgjqg!~MGMX?ISYcD~Pn)zh|K*Ric(Pou5QMu8a$Y@);rfwtQl1D zu(0QWxCI6cUb3|L~j+wnntgI_=n!##3!u+zD$phW766e5g*2c{dls?S`)AI+?<^8&`>KDeEbo zhhTSi=S#*0f6`<(hM^wquI<*+4##di?$*zEi{U%^ei&m}+f!>a>6-YIuC?dvj4C8iP31&Vs`iAy(efO5)gk764x&>ZhfAyzsj(JC;>gIyOfa6`d|IIbc`Pi+6 zx!UL7kNMcYyU&N#2LUZknZrKlTdn5`1vXLQxiZt+IG+UyaBO<`TfXo?zo*{##@if- zdb`1iNhl=aza08KTU`m)q&>`^(AFND>OA&sUmUR&`q|S*`TF@w%jCmRC7d$~?sW2) zg27RV!F`G?Y}=<3M1iriWx}ArezX|&^Sr0z=*0mc@yUwBfh1@<3*ey# zIUVsspRHO|_>uqPKR)1l;fsFJVIHe0gNL~@@Y5go55t<%ix)nYlH#|1o4>uWMJV0GD0?oP zOUohp4qdaxIExP5yTmp4ZX#P$_MIc=&=!w*e=9c%oI-(3lsJWITbqpnT@;Xyyx0G{ z1t^goA7;O)uxYHjEJ5mF=dl*qE_Uo~0gs;F$y6k}5)o#oDo33$c1qZ!aO$CU?X`4J zNmy-62R6wzVChVf_1aRHc#nU0i!Ty(?mLBs5hh!)xhv&);AUv=*TKk`S*wq~~?w3W!3u_uYUCpl${Pg}bK zx46W-v=#x|)Fm#Z{tOK1=#ur&iXt_PJRXXtXz@keBk02?o}+|2RC?B^3)39<* z?xxw;`f{I80Bf4sSuE-$wNAx<_yt0uvC8=qrpd}sLV2&TIXxs6rYxyu{_5P|tQLt! zDboxs9+uIkX-S4EzRyGxVXFxf3ts%R53B+ko7*+*Kp0JcJ zd&w_3`rKf1Xr8l9#1-t}vV!FGo+PdIu*mwf_Rss=XRpl#y3)+6=Mz{vwPeO3_hgNl z8Q!hTq^16cfB1*5;mY<0dY1XhYsw6TCW1_{TiGT;;|nhMrgjA$!dKD{?2@`h`_GT| zHE}6r^}&9|_$IbZJK!tpBGz%Cdn8jWxQM17|LmXZ!Uv~0@GP;|2!FQr>b0rP0pFe$ z`sZg}tr>Ne{1KVVB?}lh(in(0vAZ=fDbp$>9@*B2AgAD}`$e?0W?2|noUB>TbkffH zv_HofyI;dP=QuLZp<%N=&w)ABmN*sr$J`J;eTl@#WsGAPx~BSj%01$V^|I6|cM2}= zG!|Lm@~5<4+UA*(Hskegdeh?PY$=|t{xk(PQDT}t+ue-q}nymA8o+OEXo`?E0KzV||9^ zvie|WWO9-S6N@9OP2G>VW0{$>>-D?FlB(~=sfu&g9FDxNpP7`m=aa`G5ZlrfT%5p` z;DVDgh<*l)?8HDxwBo!KTm0?E*6VDP;ACt0i4XtrV0ow3KFz1Kgq`7&uk3N8N<3K- z&vPE0`Shm;=inFr)PHwdpR!(-r{J?KUbe6O$*BrBg;__$4C5h+5#2L;6uK^TItuoA zKNr|~j!Zo%y%Qvf^hjwhd#x#k5u*!Y*f?#bU>H25#?`sCIyeRi=bKA$~%OGrSOi3XQy zhu})~P~P{tAMG(sX882)*~4HFi?O$@H+GJl<@lo=wyf3o*6oNXnXEC{9`xuPAC}<2 zHWkfl%^KEt9GfFJzz;{W_x$18k?mxE-qs&FkG&yEeK2Ih8LHzNr|l*v!;ww^kBafO_XoGEn1s{JVbrgF|d9!#}5{OmGTU?Vs&gAQIE~ zi%H3GjFlV+;A_PytRAg$_J_z84tNcqU~@}*&NR)f!PCTN*KBONN=3xIoz7e6WUku& zB(SCUz)q0_?DqHM-~GKTn+Ho>;>i*|ZFU$?K1`S@}&;B>&@21{Mv#}aeY2}gud&nG^# zw{@ij$5?Z)z@cHScVeXpr8Q4nS#QWcy%%Nn9llm8jvcLWCBAdUm-HN>Q#jC+vDY|< z>h+FIJ@~-DDrCR0(sHgx2A*=y{lkw~R>JI;;Hc}`ow=nGZFOEFBGIts7~#cQ)G(GD z*Z4Tv5ev-1gsCKs4#i@mHO|;VJgysE;5Zx*_w>B9cHaBV{N{RD;*fUA z(7jU=moidUoC2t`O^a`-<*F0Sp<1Ye_V1UmeNqmUS2(aIPtvof*-n0s^>!ukQGYEF zPkZ6l^*r|>dY5&w)Dm99={=^VwEv1RifQX@_iWVLM2TmE>Ko2`lmaxXw%qZX(h{yU zJYjiQP`|ZydL8SVP{#DPr@3xJWwn!0hO^4hkCkQ&_R#h4m*0=AjuEKqPHAd~w9j?h z_L%6Gv>e)JBiQNk$;~>l8|8Lqt`Vzx9F!6-hnj>H4mWvYU$U^|eP!-wEmS_QFJWtH zdeTm};glRpj3;61lijX4l;yQ0_^{V3N!fO2ecE3l?`N3~PR^I$L$<4jZXNJ(`~ja> zW|wfs*fdt8#MPD@y3)$9pN7f8a~|;m`*j+9E@Ni#YhjO_3o)KV(%cNoQE}WGgO}IA zWvR?xu-~PhM4l9>8qlMs!egx&mZF5Obj{CNZnqlS?M%IUH!XQC>Q0E2VhzjR)HZD_ zplLy24Xfc?>X=9$Y;mE*rEs)xS?bWfy?&{4_`K^b1;2&+-gmz{p4s;Lc7AVDU=t&kA?LlGFjCPM~>~rEw%+K+xDHv9V~RA!C3#r;#jA4Xdgjw?Q&Wp+PIzA z@w~voMvsLB(oy=|RkXGZUk59p;y=R!U!LOam8h(3xD!vv@ z&$%_74sAX4&#hzbJ;Ot4hjU>Mwf8)j@80Kq2*#3Y&lbL$2sK9s<0F!_BKHy+Evd`7 za>+M-3G7qqZlc5~RNLCz2Nb{(TzVMd4&L#WuL`yR`+P}!90O!up+%J;mW@W0^q|!Vq0s?{)_+eFTB|N8CN9bguMJ=DWjcw`; zok}7ajuNGx+RTXwd{fad;Gckx7N_nbN0p&|xn^96tHoDX$SyX&#W&^%QpObtIqSaB z;5o-W_G}mUlTSY#T6gXIA{)@a;12UuVkEL|l>75u=L43P>@UY7Tk5sw%x{RBWgKv1 zePy0YCD67TJg?e|m#FJl7ufp5mGH64w61xsE@JL@-awfFCwVs)#vQ0Z5g9yt-eI3GSjnjBahC8&5PQ|Am*1P`Izq#kB#b^G<#r?T27x}Tko(f}Yw^88PqQE9fJX@6C@IK2Fu*KGrJrK_6?qe_eW#IrsJIe`! z=IEpU(1(a${o$r zMQqGIXJ|Cht}E-;9^Y8fQ)`3PJ@J{Z%oYVFXDYOe9i_7q6J=zKD)l+ySPxlsY;DxI zh>W!U@co6q_yt{Tq1YJp{dTP04x{9^d0m-nBm1LFF`g(xw5acm9fKom9KDPcel*K9 zP_K;|#t#2(tx4uu-iBh~)(qU)~t&KCC z=PoT4>9=|FZti}UoH^KhUk}CGoe6rdd08tDaDGz`R@g3HoC2FDad8rF2<|=#h%3N0 zV->KCV@VySdd>krJcCfhtKxJp=JU(1Ka*7^8xkzZ6!7LAT-c0kuarQtsTLZ)*ejvj zuj|HfTY2Ps2p8N!uG=P$J^CxIIGl#~;!Q*?s>$|S?t?jFG zY-HyBPx@f5WB2K%L1;>weg)kZjEz=4@hEA4Yp1CiDkVx4LCJw5p}6Q5W_SGb8r@LCu7i{09@ zV^!&0pf7NGo_Z}j6^O-$z7fQ$sjVd!1Kk>I6u3bOY@);s5_>~+w@|<*H$!SK-6z^a zBFoZG65nvU2>1GV=Q#2+IOGVCb=uNaR1qRvKA%fI)vPPY0xik;P9vusF>EN=W!t|z zmuxgM04>=m*7kQ;11w>}nkzZL@$1)5PHDu?xi)z}Y0}$AmKjb-!M+rH!Qv&bjxx8U zvs3U?by{o=MY-q5srVuUxX{!5W%eExm1HZ;sl>i4aB$==tX3zC&zOX~qig796^!@3=RKoh%ox7N$kR^J z$a$T7)Ear|w}0s*KCECZZuIWPoam$detcnp$s0{+J|j74MGFp9Q&yS=M4Z<6#&+_? z0tzF~`O30)&T72HVs(JS$>!+~eJFOf_KqnyZgbAEp6Y(VM=ZC4dkw4SShC|_lPfx9 z|LankiZ_4X?})xdR=9^BUW_aIWBn6py@L~nchshw35^x%8#DeVSkHbpVpDR5N!D<#b?HaR3hpq7Hxnhap%X}(6{mEDY z>}>k8OMrZBZ4~DsqLnr^zEAw*PcC9hTT!rH2e~#P@h?RCzZd^6|3Zi`?)N4u&bIvZ z&3UeeTnEn0%~NlD|;d}+B`4fp_{#3yO^kjiGD z$yt{Y-&sKJlg67Uai2`&HjEw$$T7_sU}~r89ymR9I9Fh&f?X@S@%FSiwGIs}XA+^1 zeBhTlQ!T~I=O5d{2U&)pbZl7)r>DLx#e}WXs1eJ=xb(C;wa(k$`hsE^`E=Q3_yds{ zXf1<0r@HoY>T^fyYprmq7>jLKIu;sn$wDPvt=k$4JhZ(n3tZP*din(JS^>-99>`rDrj+?@I1j z!K%sfKyZ*n{%$SvVdP}F%$1OaEICwAtHw0 zhVT&`4rAGC&-1)xm52Jf)3neBc{ty%Fzd(D79da#y>1bEua_p18l!n5k^VuC+ z2XTF$3^wbWvo^dhu+~HdKh9iAdt{U`#Lr$M_4vlpJSDaOw-_q?e zr^YdMug2GF`5S}Pa~(cwg(~YNECNE@&7Wtxr+??xVZnpXZr10*>S@o(G-9=i(_c^h zbL;pnM4FLv0o|wVH&Nm~o!D(yEd`2U{`SM)@`c4p@FzpVcWSP18a#&0F|8fEw9QYx z?Sb$!rge^AdChvgxBZ$s$J%I5TV~6>md_iLaCly>0!ebK~;4OIS3?Eem14 zguGAVOp`8mzH8cYVwu8G-wOkA)n71VX-YqMtYC)fx+e<{Etl8|=N#0QSVYWA?Q;)? zA1%F4ZX}o8U*ohRjz)jfF*c%T-T$6rY(ubQd$@_SEv-lJ^EyYm9{59b+HKkrW|p+| zwD+I<$e)O@xqgbjeT|!zF5%CW{I<`HQSuymp0N{S_{`bMibYV}y07OPb4p_gAigAC zS;1H!r{YD3us6<17x9)Ip~~>u)6%Cvl2FX}3J= zhtyZIrY#ka`Rs`?>w`XAidkrn&AB zaV7iy!hsM={?SC1tfL&-Hmim?cFb8SHQ2^5l$e$Och>Q#XE;CAVMVBE_IoUUeC$fT zUni}@%-A@_uGjclCq5#cE&e2Z%{~cn9fQ?#-P+5!NqoZb-(ChQ<4)qq63v&0nnQgE zcD>MxIElrr#{&=XQv5`Q_+n|!aeB}ej2r{86V8wP(?1oCgz3Nk{Ga)T*xpgSb2YPQ zWLa2Cm^DPmw?*p8zquyG0 z5>6e1Lw!NbmG{;xsae40bZ1=NYnK#5BUY#2$$6u;TXQ@uEUbK3ruR5M^s$d!gpM^{ zPQ>9zS)5?Yb2^#;yKeJbINn5w=fW&M=f{ZU;d4uyI25|LHi3jMrr3hzv4vXkqh3uj zTKAWqbZY6bYN<)&^DNJPU3ZP++A86bC;?_>dcjWh;_w-~P?v_l|vqua|`)O6L6cgu>REb&e2;uh-Ed zHDf|W?W{gFw!EH}E`8)MM62#td`Q-rDpPwL+xAUW+~1>-%r%;HVRac2RfoW7V@taw zjv=Ha(Z-oFvC3N`JlY0skik=VJR&(taBB{6a|^ZD~0$*}yPwnPio zNDg|`4?}gu#_T6|V4CEn>toC;Hj5N?_l8(y`7&ha=pyTMfS0I_{`a-wD>F&R7F( zXwd5BE8=VY&EaEtILg+tB0d^=8tj%{8B!a=9Kd%@*u3mXvN^E|BwpIVLlWTzA2DJL zb_pw#_>>2+q`|jj8@zfMw8y>pmh4;7;%Lw7ttRc?va>YZ*~-rQWlelCf7LW~3`c!! z%`jS4I*xrc{1zWke2NWkX*PEr+1VY(r5`utL}tCfCzZew=g=WK_FS`8tQk1x_PyzW*Hg!P-h21b)}^{H z#8r-?^|j~H@g_<mN(SB?wYGtoSVE4{LV`oI{QzHO{riIE9aP zeQkUZHdxUy$EK7EzXu;fxLo)>>;W;V#*xQ6N10n)2JP5MxAe~^bk5nV-kvIP*bH5uG*|`bsLr`RuWoe;$baPCq8}1>_m0jq2qcF1=gqtC*40&I`)7XAb+&RH4Y)#9P>D-T@?*`eUCWRL(|lp*KONlnPSB4)Q%})Kg37Xb$iB$ z)e39Zz#30H2Qjdj5Ugpvd%K;Yn<|5+90XYxSiM-5%G!?K7@h^^88d>@cFx%fSow%a#vr=^ZtyrrEiVr&hdG8##+OFH9*_rIUZ|$-t~IiH-XPM z(Q{Xx*Y~;ByYZa<49vlA?_cf5b`x}Lao>&Sn<#NN&dS!OGbkV|D#B*VD~!?c3{YK7 z!(n5uIlJbZ(*>cw60IN~=hO#=Y9;?*4t z>fXo;fXuW5;+pEa}`FoC?JfN)&%O1bb7-2%BBRd|tR^mb(}Tqs z8%%ZGCqa_SY^xL!TvfStC}gr0r+ViapmaP+e8h;U{QR6hj3?`J=;`$nuQU2&`@({b z&U+++t(-@+S+ZNQdBWz^&Kkv;X^@u0nRJXXz8vEcd_rJlgNg0wN?-Y253vyMd*|2O z7FUoo5K^A&WeL7q2$`HW9I5i+zAO%GWxTr(X7Ngq1*qK11i)B=rpfH73_JRsA^#c_ zaU!zf7#BLoLS~PU1eW7liqBSM>43(!{>w_ZvG)cRKIvzV2jWrUQYOB+_d?yqv3Fm> zHNJ4hD`SoE7L~_Xll#E#AoZsb_{dpDYn96O<=3%#ID45%KG$9 zd+}d+-h+?4;+H=-l#y4&I69(-wYDrB!712o-QtKx+~*W{8icLY z-A;i`l(^exXzO1m1>^+xS;tCD`6&CGdDRPZlUKYG{82Tb$eC=?D|rl`&m(QVI{D)V ze*|pi1S?i+ji3JD*A23>*c;*9i6_fo6K8PLw?oX?_m|%|-{ty#9!IEhHLg?eWf-)! za82zVN*k=6PZD-M!okb=lJUiSci=L{#HWw^m{Zd)L^EkHS0=t-*(NGmKiDbbqan3E zlb-+V6Q3BQ{}8xj1v5eEP#xDIG`Qe`gP57(hqc-hPm*wnE?~@U@m1r?acMinJ|>R3 zwClO{60uH&H%C^m{rJ?!@|(vRR?l(HUyM<#MM(^-9r_ZR#2bh~seSt4TKbGVfP5sVs-)~OzPS|55wweVc;mwt@OpU?nz0Ruh!f*NoWlsL1__6j7G4i|? zH{4r0Jh-TxiVEqK2a8d=K0o=JA83*I^#Ki{rJMtGg1)?g@un#`@TN* zwbzoAU~7D1qBLB=dRBBdHr5>)_=MF{*7#~RP+NVcK^Eqo^V^mop7kKw*CDbdjC5?1 z>55ZDihX(CF7`ytaG=_c)fg9`yH#5`6Yfxn-Oeq>i21qBh-SU-KmGFFLX%{@YFrYo z8mHaSv4r(FWO&QEr|dSbkMJ4?=R209^3EAu=b>KM(=;qLb!RScOiQ27A=OOp^L|cO zya&E|!{gfGR%*@puW7T6CfeaR$uoMIA}M1GPajyWFGW1*p8 zP@Z#6s@urhFHd~pqdg=pOyT2%5soG0b*kM_qSG4qz%gxT;u^R;cnNE&jZd#KGVTo4 zynYcrY(no2vwZupjfqc&pt*f8YOL_h^^dPF8o%zz-@V%tq>KqZqJ*8jYj{(~o&jxG z7$TOSsr>Lue`#FL`Az!4B@SSzmJ+^tt3u9&#I=YQDsw|bPnI*v{ds7>o4)m)*3X@O z*FQMSk7p$II@qs|Q!NDg#6n{ZtZDOD|6EH}C1W@Ar#J|9&sp!d+j=#=x%JhyJ>@)} z@BREyHPp^rr@-rKv!6dS4fRG>qth$-Zg`jHn)2+Q4y~^x%S__qoO)ib->Vg#hq-?2aNOMd-Sqfw8sCoIJ9^I1evSL} z_|||o2ji;xUd`2An#*QOeXz?oAhjYMG2djHRR?(3|N_F|#AzT^Wn{?0e9dG%n$+k7H9(?vA2+PNQ zOq?n>L?yl@KEFNq#DFAh&kSc74pFXg3Rse*B=OP44aL-OHsH($wwMgrs?dj}z_sv+ zuL(ADT<@}M_vgBL=|f~{im?@(85@DV{Ow)ub-YWscCik4r7f*B>!V-La6c~hBZgz& zh*My;nG5etJXzNG5=L8Em-R08ECyMp-XS_+PL)D@r5?!kb!iPs1<*Q^*;HzHi6rbC?-0-u~2A4`z{B zbMV0)?VT$ail>s%d2Px9B#CSKH!tanPcDDfG=jh8Z~TVXm30UPhYJZ#)eUEll5kM{ zmK7Lko7eO;aVdu~4)G=SdgcqBb>Yinj#UY7uEZv1yD@wS#0+ns~Y z8j?9IVZjGi%jRPBvrZ4b_9#eW=y#p{Iu45%uox+8R^6HVz1ygZkO+#Rr77Cfpt# zDhr8TC4OzdUpo~)l5}5!4-tWU1sG?~;e>a8i-2SfJeiJ3ursxLmhAALiS2m1dRK2(lrlAS1 zk|~F_rDv#E{7B~5Q+c2cZAXvho%Z#r7)g( zHBE3P-c)~yWuP8&;)~GH`bKq(^|qdJY|+1>U9YNiA11yrda`~HgKJzgTj_(}jc8v{ zm@KP?WRI=(PiV4zLf{gwpsr(dJ;%yreS-6pEGtF7+d_`wSttcCGkB@7GI>kH%~)d9RFLZNlO)(Wgb1$zb6W{>vgj%)`=W zKZv$hMZ}e3->=N^3uLB>j8Xf(N`~2EwX5eZ;nyWxV<`e68`d75Hbkj*Jni`uK1o_> z*4s9DE?F#xEV!P0+XIu<%WNaNPMrlR$F_3}d3@CQPkSj6a?SS6pLRqdtX}=YCb>8D z$00HMw*=o5%&B|flvrpfwZKiNjS}Bb3WKSBo^p?sQj%|DlOgaF7`<(LBXeJgn8V(N zC)UeEyR0rZ;lP#n?CWu2mlrjI&vR}Y%S8?Ev&x$&ai2}-y+0fp*|`L(YmG8AoQ3_c z`ictB!RXV&&}ut-pTQxVl-ez18Q> zc5HCjrMA~1mGEBm#1psEUq3bJcJ$5rSP65K&SAtR?6@^q*US1-C2`aIgU5#-K66y} z*;uf*uE|uxSw|w(DN$|=XPNid8t;3*>$@V;5%w}1gQ^BJ`F#8z{j0w!`lC;O;V*tc zv7Ah^#}$Y;#EOWmAr@hsSw4o?p}+Y(zvX}@>8%WFQ`uUwm!)A{K}(soDk? zD8{<;Xi;l$=JKo&2{_cjYwmk?_2x9hF^c~@OQx;8Y8r6DEAPh#v7JNPpNXMhEtJon z154d@=va&kYYT04Bum~`;&?=SB`R{7oO_n7QSY@O9Q8F8EBj-hr8d}F+a+47*1NUT zBgWJ)=Z?`)+=uua)>C+hyAu1(wLRs&xe2vMQH>baITpB6@NaE43OpMW*hGnEgX$a3 z0SXir_yO7`?C8MlRWFIJ5ITv4@+n&tcms^12{tMuDyyBMVXM_#f)5Kw~2d#A|G+d*on{|eaBOy??xhodOhof$P)A{Nq||!SToY1!C}p| zm~$d*SY!%5mch2)?fJ`J^rGXKlhTeQim2QhFOm54yM^DLvwRVeiBx_>!q(xhZPT77 zzIb=HH!pBr)CUiQGhWB|)906z`ebQnFC5LUNDi%!Fq5HWvK3rw@o4MIf@M8r|CDW=`SYD~ z(^T9^XwZvR8!nl^QiSPY&#vL$_jhck>#_yi?VR5jSJ86Ak9BcwZC`^qbzuM>c$~Ln zl1ln<-l%u6&-GF}d`+P_+-vX=SFqqYNAM9(TlPLIL1lc4@$EypIbLNJwV0QJ$FqOv zS96fhAJMS6HF=6{&9NxqvTYm|dg^`X*^zRA8enYhhY%BW>|c^UPEkGK-C#ZW_kUmX z@8duH(~F+Lp?>sSi@7WG@NMX+e{LP$udrT~Ik~9}Kso=mV!R2QaKL4)-Neq<=EVEK zK0xn%&wFlU-JH7)?&bEIC~+^(-hI{g6eX5^5|) z`;mY8r;eO0LOVVOaN_W7|3o;~g}nMXMvF0cQ`fPdIWBOB zdTq=_;&VTLAAaBa7UL7POY=*+%)tk(YG*po!O_hnyb89ZJ>a^m)@|S+-i*bb&ssa?wldt)W zi=VxKop6iFBGeZ9b1`AYmo!Xt&N*&<_X+qoG+CGF_$9VQSiX1inAJd{SmXX@pZLUq zzS)=Fk~)R;qbELwW0%ltPHnyLPpwCsww5`OoeHD3-Q%x*b*Splvd5Ct3wNwut}XA` zfAsX%U!0ba4X}(>*U2ZPzbHflZXS9Bntq-4tjwd)*BeR^)iXN0zpUUwX^>F9An8 zM+@INZX^t!uOqE&(Z(=h=l$N&CzjJfCW+*KJ7l%tdtPf99b->>@u=sOUu#~$5MCY* zI~5@8afnFeevK3BQB|8fB>M1KuG`Zf4QWKLWTt9xImcQK)gMCqxzBv&V3pt_&dM;< zmJz;AbZhtK8h0yl4B=7-JE$F4&wT1rgY$sZq(x8oXev1;|L(ljW3drBoAvq95B144 zTHUNC27m((mI*!b`k#N{&mC=_a6>fM&j)9-yxx`}uI=m9j)W1ju)p4eFGSR$C8jwK z$CCz;_eWSJ_L_Yf*ZR5SgBhVia{@cR$+ z1gE|?c)Z7FpO0E3ONX?a8|91t%JZ(l%S4n}ZxBa)agR@Z??PgMUs`Yy&JZqOmg4jN z3NfkROW1Yk-jqdiDL#D^pT@dc<8kaAHJ84EMXhcJUeBL*8qufzxi;0-vuoZ@!8ZkW z?4CFrDh)9siF2&(n%8B;G9SJtYr>s^<^6h7yv0;Mo(uOjQR2BU%Xj@4Y4ou8@d_`R zg{9ic-T^Xra(+lZ=43CT>;xi-FFL(8~m=ZBo^mf|Zk+|jCt3$1^*_0R7fEp3xnk1LPE!L8g^Cup8G zM#_5rYI+hS9+fq2Ker>zZ$;LvtfQRy6PlhdYkW1GrZn@BMQH)PTz7ER_%fT<^ZxnI zPR2{b9PIH8?Hke#x5h_grpoanHnWDz!Km@|Wc#wEVA8WLJ&QfbUYFvdhLPwPE+Qz^ zk6%&y96e_(UGjV>KJCOE2q&+%NKANhC2MlhrH7t#@aFDQ+ps3H@J;Q-GzVwNeJZe< z!nLGd_ha2nl(-*f^`09O%_z3=(ne*UWzIh|GG3#~OW|bkXnmgHw6k!}1PLczVl|0R zR)J3w%-AcDrB7NJ-z+|_Tc5!QkL>AOsS`T~7kq`)5BOkkT(H~t90)ALj-~s2*JqYE zp?FJr46BFrVHINHuRd|34*;hI;*QncH< zoo{d5!Zw>nqFj#^#Q3m)z3WBaaxW0#Qur_z{6krRhQ=E4K7~GoJ(#71_MBycMJ>ld z%RL86_6EuIIVL8weUy4Yrl;L{DIwHlUD^%1tv7Wp?B1NPlugxh@2V0}t;w-%@}}Sv zYs&+~E-?t;nTvKl!F4Zo2`@B6M=Wz&ZwgO+4_(8GRp971M;k1ra9q!~YjEU`Gg96D zlw*!z_8{?&GDitp2_N=5@RakOXM0H>B<^IX6(i&q&aQi_0hXN8<`Q`4)Ri$yY|1$> zmbA4kT!h;%X>>+i?=9>33=mt>dx!#?C~*%>*8SU6eyqK0%qdi$$tS8{DSWE2*y0T$ zgHSOp_hb<2#S2fck>yj!Kc1!?3+oqJ#d3WK8#RYFJ`V+^PjS10aj>ELdvkCK`&@jp z|F6hSGv;W7rAI@!u(_=-c+jyEU*u~qb8ukq13=XDl} zWKTIS<@Kqu2VcZ%?B_2t5V24st2mTgbqr@}?WdBeib2Vo`PQgsj`N6PBFqw&J5wc0 ztrvYw=Q23*N90_Z1e8;Zbu5mAMP#b4nL#V@^<-@tqH83;-uGK*vrI=~*1V71Bcl3o zgh^aV&%r4Pm|1lA?5BfU!ptmH&c!Ln#>HV%>H^Pz>*9^h`9^R;vo`7hsC1HH<#LJr;=}SRBpLf12~iP#r$*2VdKQi{pU(6Nc(e zU90BfBw1gh1IAUzwyn0zjm8OIWJD(qXEurXYlw18=Q?h%Ua8y zjzbC0x&sCcB5c@rs__lYZztMAwRBv``AIQq=)9fZ1CRv=o`D>nvK~)+&M{_jHms&E z-W^2Ve8v(VYn68hrA>bgFD5AAi3KyR#F_mOgHo@BQNSnM zAX20V(a!G);JTV{x(l_^e!WT$@Iy~MwfG&{g{-Q?>gp#Jr@FiyZWK6~0-GptGU>L4 z>rp@`C2jtC5XT{16$b&DE?S^AGs}3Z<#+6l5#gD|Ng`PeA-u~W^|c6gqGCtkyd=J~ zD&*LdA|Uc>L905l&?_ zo;)TR4#Awhc4sH=*z;pw+1fB)H4W2pmiVy5t@e2u*R5NuW!YX#i~2&M>vgD(-Ope9 z?mu`t6NqLTbKkpX0@!GhW4J2sD6D$M2qnzgdS~AUEO_2i#+GvvRw;Puzj*(Zb{x4_ zY7-WgmQ}2^jIes9l_2Y4_hU`ffJct8uAW&=Oxk;2y!YMjK8O%Q%~+vYQ7&)~RUWx! zjyNy%B+~}MGZ`Xk=_9AFr52`WTBsZjS z8r$w^O^OI__35N}ijdz<;1XzRyMzrQ9`P52%a7Kt6J7XUYZEaE9gTc~I0q!IrTF;H z_u`Y(QK)8(>r{LkrKX0*$5$fS7T2lxk}Vv=i3P54h0pv6^Oy7-%Z1O;Cqt9r!=hrf zh-is_iRC5njjb11q!KRn537m>kv(QVJ`OHpbZM_AK9;sRG~c*9%b9GMrnlbfqQsuX#Ll_Hn-zjf={Nj1HH^o$pTfM8&`|#9DI?!09(JGi=(p4S53ou zE>5K<80PK>k8zfDm}{fPlgEkAyA1vFdQZvWk~jp0j9b?GGY{RGWStXV9U9()tFB|M zaNw-3jUBInZwz*Oo%$z}h5HtHueNp8JojwM+?I2`_&m-7du<;D+tr(yps2@RmPR)r{L3fbCNLXd9go6URMem zea)+W&EeV-d>kweyL9i@LCu0kvdz*DeKD@IVAa-xnw_m_)*ctEX^d}@q|&2ujeytPI4=##D@i~!#g9H>Ym26wjItI-&kVK?3)rRl6bO=U6A*c zh!d$*cE3*0s>)J)wAL9b0A8I*MmTql9gQFTYrpzvJ2z}4jc@jIihbR8KZ<(J(<&uq zCs>DqC+1UpMDwSw8S<@8&w2qjoqLEfnG{)ig6ucej0LJecWo|uo`-t=^4V_J(3pn2 zZmVxS_^|akj(_~Y55^ea&1-!4$9BDrj93Lv*6qP(j4%z`}NejUUDoPXN7U=CtO2uUOBETBMEhhlkv_e@59^A z%!pXguA*Jia7>kuc;gT zi4qqe>IQNN1+bB}mBAuJ7~qn|3+rOV<5laGxYsl;1hJ@khDUl8dL>4F`$Z6rD`R08 z!jG@+&R9$K8L3Ll$txcarg{!OFgPIWG&~O@ojh%)aKF77_ne}^1 zaINcrRT`lcQ6@FluID};-kHvc)~yPo7oU38x_*0C)DC&ps%JYz38G=L*RjL)5+qe! zEPRArm%e-cj0c-Iv{sY;nfNE0k<6IA5_@Kdq`eXt z?hhUAIIs@VaS46=>TSj%Nn^RD4%l^8pt{~1Dr}gP0&?fWfj&xNUWReYM{iuNvphUn z_AJPHl5#GI&p;P&@|SSxq_#)Ch+Y-5J z7CiFx=gkFWB+lI98w;Zrj$HT47~|PkfG4ZQeC_cWLta;!8(E=@yRMfsJtaK`u_5O_ zOR9m~r8uv#PUjlp)FmcI+a+-)#z17gD(}Qh{H^cpScqZ7_FA*fQlj2|`^&}bFTB(} z>5vDnjDDQAPMJ=LqY?+=-VNsDAb(7CAWb2iKUE|7>Ih07s@=o2TL;V%99! zSijJcm;#?{Pp8t5^OX3DDY|griY!EhmhgorJ=U>_>%_mDi=hOh?uQdxHD_7Bb|{-b zada#8t|rD zs<7ed^rv~TP{gk2+E5#`A=wRzqf@j=e9HQqMW@D7H;io^_x0y23U#~MiUSuMcGI>N z>v}`SJB{`-3#)K+;sSAVOk5bk<(l`|>)-UI+mX0?tkS^srE@+2V!O0aU^NPCqQq*X z+yI?M0S*B&l`y=HY2!|VeB26akF}^XvtS2o({x<({4<}1)HM5a!b!8{ICeEXAH@*_ zKCAhumJpLh|Ixqts}9$WDxY~G*1)G4KEBB16aN4$SniVByN%eAZshnfY|j>VIbP!7 z5?DQTYkYjdGroWibdQgH501J{;={(IcG@~wsxg$rx5trSr}cg4AiBhS)*b3NPO+TG zHMhq=j?bJC373ZJT4O)@Lw_XJ9JV_46B+0uC$uZpxgv7p`e^rB8EAIx61F+p*ICf) zTVej~4xH?&dNk0vH-f&Yvt;f#KqkIAIdwYoI)zqIRF zs9AaMUfOWK6i)(PiIZS4t65eKg`R=z4-v8Fon5!dw&u~k5t2$156484XNt8_OpJOQ zU_(W7ZN`o-P%n5#utph|~`Y|j5N_8W|(+@ri3qR?^m8Gq@=CVv>-KmnLBec4u66YZ7?$B=E`n3i=`#o@q z!R8vLEKBKUU-xq@e5@Np1}r^i6pl?bo;;@UUt@gWaqQzjGR3ls9m(~ZeYjM~G0FP8 z6#N#hcS~3w+Zz`+7vgSgxrQ%*;s_**g0&upE*KV&j(j*DvWKZCh&JvXo5;YrX`Iy4WBNJ$|y_)LP4gRqs?>YXBo8 zHY;_MjPpnF97WqX6rPvb%bj+#gu9kxzQ##I-77Yk_lzY8cBr%mgCj!SRdE_=NrKP4 z@JBCoLlazAo;Z%oH>FKFZ{)mO715||mKtVGVrU|(iu$p=TU&YLJaqB&NVFMo~8GY%(>yO^)>u?*eWjZfPeC)QS`t+e{( zm{=#MEHccTHEW5ppXVXtjXcku3%^|JiO2iER)hK8VIfG1tM};8NXD*FJA_rXS?h37 z8F;RTLdf%Z_a2CM>llK8>)tc0DMa-8H|0zlmaKgrStKtV67Bm`oUabg|2eU310)!{ zWIi({%$gglwDLu&1?q;{(zP;*4cxOtflZWnmI%M0MFCoqOTVU*(lkL>Zl4A_yAEkg zv+f$DPgU)JfSsArW)9)Ro^e|7n-W8^9(E%Va_&EaKAl5whpu4<>+sR3>`%ec7aYQF z(J?YNm2+90=2$(r-~4xe*AdGiBhQqzAv9cjeA){SPIkXe8tdNBU$TYC9z|A>p^E!! znR%=Xe+EpoH~;)%(ZU zsdkt$uh@1!4u05FA}2av!?E2m9btP}?>_v)KRig2Df}GJQe02^D%*8j-}7DzHNq6& ziF@|7kS;+2*n~TUZ>{$>Yak1g#I)wi8c7v{et0*ymZHqW5Kg{Ds~vl|6nu3G4x-u; z@oY&4ywi=3_*T|yIOci(T0cwR^wwn!&G)0KpvmMVRvmql*ktY7#*t z)3pw*eLq`qQZcY*MTWaF#<6V&d&kyWFC84${`O4a_YU<=5mz)-Z>@VoVXB3mR1j<7 z+~C}yz$QxEp~ZG|;}i&IgyP42X~aH>d~UH=K2b}ZIhOR#r-YV1KUGSsjL*vwIK6cv z#C!j#^f{yfhx5^rRJ0eyl6o>U#n=jKjWzfArF~r@vhXGEbpTm18c3v=$AMR`c&FxPIozfqv2z$Qwx6xq&ikOE#~YiTWHSD9in z`34&h5@Bki&6HSD2de)>7K=l%1aovd%%DOz#BVuh+ji=NmT?E+s} zYnkkLs9$;Q(f!^W*~e@v?q}*9k?qSme1P>zyQ_g%nyjb90;cnn`l;m>hF;g3JAU|E zzA*5i4a*J7Mv7`OUSX$Q=lpauu7Z>ENYA+HK6poQc&TBr2838zthM!Z$Hd1>jO^U; zQZ}@xSZ=dk>Te4_&xtl1@MLu=eSCoBE=5=GzEd2XtU;DPR=fHqJKGT5a_lYOXs zl7%U9-nleB4kPJka_RBW+My+Uw4vtklx^u++RF+x6l)RNxD_0nr}$J`FX#H*rgI9% zgSH>xG*#FtT0&wB=Zj}1oKD*I?K$>`rWu`56sqgQc5MY813h-Ghn=t6x5wcOR^Y%_ z?^BTV(iv@st0Yc6^DjbS3qI>^D4R-+Z>dG1?HaAN&|PEOIW)C3Zr#F;xzAd5ZZ;+C zFFN@RsR20r#5Qr|{u&v15?`!^W@uRA471jsrOt#6xHwIGt-~Lu7~8|4BXOUoZ_CCZ z7)!31KcaPryv4#{eyAqW@sOjdHFPDZZ&9Co>iu7PHfv2~@sa=MzmD&W@%Tw$L2Z(K zM+es_C7}@YlL=q>2^~stoj@qDpu#J(Q@-2hcDtI>IP>X%lHLuy=yp-SSZ(7 z==g1)i6)vy2yS6*vAHzK36mIug%f5^{IgAC-aEA!&iD=@ANI&LbcriPCv?X8vS{?e9;+9Y&O%S384;9KhR=WE(vCQf25YTR4xsQ+ z32;hl`vxEJ<&>C{7FN$WhtLo&dMd9_N2spxuK=nG1PjZM6}GGK!b;vfoXJjcf?J3x zs&mxrv8J8^d#H^>zQnbmd)u{}p};0e+zg#JEJqZer4`C5*K3d4`tAAE)z3R_Uo1r&r6L1VTe+NP>_Uqt&j*?u zU9z6Du9C=nRG~TH$iYdhZc`sO)vuk}xf)9?dlqf4P~&TrnhUIDHI6*4!?Bwthf^^w zL^_UT#B=QRT)65k-1Bu3BMg25Pu`M!jo4SW zwLZt#72&jXUB^U#AyGAPwB_5r|NCzX)ve)BM_7+_Y5_4VwS!sKC;ARqpjtfjx%X{^ zs233-;V6j=G8(}@wKl^VRRioiM#prb415#e&~?8ezq6ohMsd?YVt%#MJn)nAOjV?eh{}qy=dkuYB&gNr%DS=kJ^@urhmQjq#>LL*G#E z98QDh^nGjFPk~L8=%>|I^DGMRiIx~~$oKv%z)j=A@`uqlSb@T#(V$<-pv#dY{EZ79 zpK`BgRtTE(F;S!GZuhB}lK2E*JIgN(#u8Eri!A47YeTa$w4YdsU?U1HmZS7hHiu&g zE@`8#iq@UA!Y|>?mEk0{9~U;U{?v}O6|x^rL;H66I###7PU9NB+WU6mQ0n_b$Fzqn zF8h=W)orhZ)nSk8s?er&bg1jotjBhPSp8~T97~3_m#f=4#-@bEzO)60HnOeboL@Gl zk^`$);gVIyr+BDcdyNVp`$=SL@8@}4FY7DOki{_T&6m5q7sf0&v7K3AgIPeZ!}V{; zdOY+4i(UGeyR7GRYskDNt>K~3j`)^6EqCHmw&*PP_@4Z`zc(zIteIj<>h-EWSTb=6 z?k7D@!53oszP|~lE}gyC7llWNdq?2H^Y%_(OGmfdY=39)>RGsbK zdKB11iS=l?LApi(ntUO1;VXUZ?)n$_ruwYYJgpD?I>gkNpYn;j4sH61uyX7QtH5;4 zb?zoUq2=p9oQHDQyJ5cSDm79gC`5V{fHOd_%~(EK<*}+_ddBXZa-H!XYh7EJSnBxo zr#5s=?IiXu)(p0IAA0OP;JK6nAog4+Rv~dQr;KN)4c0#W`Z=fxSvEG~(jUi5GH?yy zXs?+kV`1F_-}qR&uuDYmb7M4GeD%5YnwiGdN?TFGA3FY2iCHjC&&<&MOI))?cg}H_ zfPNh&3)?9o*>y<$SHwyTIh6H7SXR@wn1kN3R;Sm7Z_Q_Yt|uJTtkt=C+x>MZu!$1u z(sP4$L;+!5Lfd*Ysz(h^IN_rm4n*aXj+GhH$R=o7Dai~^(UQ;{#dcc-7Z$>AEF%zn zr9Pu})T3bx?P+#ori6;moV>eHWCWK$C6Nn@8|87h15MF{D>zd!c$*ALPo!e&RtRO9WzmpdM7 z*Y2uqTrvKlNlv(|b=Y~<$=J)K>PHX0+R~7rxk=9mm960E+fIbKbxYFE+|@Vx(t|I> zgKXouz()Jk^95J!_{2FX+bFFg>nZy>R)+IOHHI2SeH?p1R6tsMJx*$xbF7PXGJ#Wg zd$9l=_5D+hQ^qQUFK8F$B-}oIr!l8M|AeC$&6ULtHb{@dsEW9m!8AW z=C4L9Q#A*d#^;?E@pOBfm&S(z&y?7uOew+naGomX*C6V9&X-v|&&T^*g|LYd&()c~ z=(sue_~ib?|NUQdhXpbu3%};SyQFhO0tqP@Y_Tgn4pibD!soxVS?U<1Z5oH$tS3YL z#yW8n$Tmyx5p}T1+P?j*FL*FQABT|auc2XVdsgaeBi@2D%S4l-TSBpY7QXKd zE_gWx)nUwIF!Fj2KCuq|{D0E~RH= z?J6vg7>&e{C6oVW5?PnpjHhnIT$K5Pnc9^G18;Kp7J<=)cq zfs4HcS5_$EGj&YtHBzp*UXKed>mw2|mwQY0BGJ}ggyHXD=Q>c*mJf+so{g+ma35&A zgEs%4v!IapgyB7G9gV4e9(&m@i@MZ9r#c%^g;25hal)MHB`b)&4JF;4YIn*#@ApUt zQt(|h=@?FdH`j*4kFk0F)<_S~i|;1JrZ2|FS!^f^;9Os~`{z+$6D7{0-`0AN0$%yF zdVW5&gOygin2t>}Ni@K%Z}W<%ZZV)n_}^ZuLmM0p9GyrQUnPdcR`oOqU*$b6<7-)o zDHy~a+mC6-=X$EHd&Hg$!_-C?=$!bHFpK8zx|TCW8>iP=5^72;z_B&m-_ zzY#onxyC0HP@>+vH&$YYBU4a=O9%muR?#J+9H*<&dFr+<#Wdw zMK&cdQn_1nJ+25bEtZP<+R#q!Q~0#S%6r$qQ^U$*aZ}=?uurma<#m0Eq@P0wnrk_} zY&%wB^@;M?yvDa=OSQ4Jop@O&IHlEM1$x(3AmUc`W3KdG=7bhWa|=y9v(EE3^loDz z$^IyxIUxx&RT^LYAuCJUXWI}aJ}R-bW9(XdIX>&uyRE&y_POyz{9CcUt_^z#9xGZc z_7BzBuB}gjO_W%lrW?dt{F8@X)0ZF(&0%iabP4!1ObJkl5e)QwtWVu~`El+XFv7VVTv zyUe%0#Ybz~lWB}L8;dvQBsSDt<7+Jg@C8dd)S$ZdvTy$8+h6dre?Afd_gXGF5(XPu z!NJz|&{Go*G5RKhR0U%6&|2n1mJVOuP2LxC+xR6w|>bmAh+KLyx=obZ@Ss?vn{4r+lP3u|=9I>%(d&y((oI1hr zC2@gmZ7s2SUb;UU{Ck%In<#Pb&fQ(ybB0@=1KNlW{O|vM*Jt?D;SgJB(tUDhcjVI_ zg5^_-{aK>bYT+2y8(#NY15bS7N*F0kep}CZYAiWI4Bel*#xcof9Uj`|sZX}N{_5rL z>I7qXh!TZan`XokJ*VPttvvZMnPr+v`=X$Z;=GgMYrR2N_XC1Cy-*1mS@9O6-wjFFM zO`Ln?h~mU0?@dWy$KEZ#M0`eBm#NV{h2<58U@pMWZ_hFdksqr_9xI9ciAzaDDkA)1 z)}F;d(%Lpo1O6Jmb7A`GN^qR_IeHS&CP+DXivz6n*#6r0D`%(_amHegCJ~)*lUQgD zf(|FNKcxwJ_BiUE0()!o+@ZiGN<4Sw`UXad)y4+hghc{d5Wd0XvnHf#8JkzOcq*E0 z2_3QeJ&rDQSSw8@?OZQOgAlYx#!bV8`_uPYr`TBc;^OK zCG5Njd_>#q+fCp*b^eCv$Juyng(2lQHW6x<3o1Jsm!{T#< z3yVVa+{|G4-fK%vo#{N1eN-;N7YU!M5PT{U7DlXd{S(75=E$`K zpD~D6V%4boyySQ(J~-=l%@SP1wNth;HE$pP>7Tyc^NYO*_H#ZLQ)MjY{?*UJP2yuo zZbRzN9m`Pr9AA8shAOOl&3g2FP%*9ydIL8>flZW{AkKDcDFtMsz#7;#i*vwI_I?RI z;mg<_+A3_Kocc>bjF;e2mlhl@8eEA|m$qM8-@XnUczW2ngzfc-p zuwISrtZlono}FA-#~v*X9CQ6LXIP{$3j}SczR$ry3!367O*R-Dg}`fb_$T~cd}VD# zXgclr)bn85FZjluO_rFR@#!B6mb%#M>~C8pzO=Zs_r)I2#J1Om&iSo#;=^i>(N(r8 zeHseW4JWx0=DKt%H_w(eK{RPuB7H(L&qMAW&N1q9pZQFvE3CVv>^9X;Zmr33SJ=PU zI;)Y_iHvGgALvPLc0HgzeEwmwl}$ce43x1Dbkn4`cZO3YE{ zIe(v};Sxk(kf$}QVf#d4)$40(9rGox^JxoL#Cr9FBcIX-_`PCC+vBu|2_BWU6Xz0PYvfY zC&ZI6_BQ(xNlS~hCdWv;>t&y0UgCG|hb04JDvUk+-3x1IT@u43=iK4_{3x5A{Z#Td zC&p!8g+UL+O>ii~E@I6dd-PWvo^8%C+R9kequKTzvd*;O<38jwFGe8aro^e#Y#?i0 zsvd|_^;m|Ev9oYt*W1_(=i&>DNgdgihB9Ay4zc{=9)@t#*X)wc^3lE@&@{G-EOik1jkl)qrlxrflZXS8)xON>Ju$<{x03IFnn`- zDL+|`E{TseHFDMO@$JqpgK@`V@5Xfp=Vfq#x})74d^@@>!-s{^ZcaQH8rxD@)-dMU z?lio2#)h7AeQZab>kOTyVkdT5-yN>w_0mEdKd;9&*U!k-Q^o^V_Hnmf#^JYjl0I|K zPhAfUPQ_REx$R4y*EC2?`YH6xKJRcQ^yAW4vQIS)cJrL;=D5w+S<+-z4%f;$aJFm7 zSm)-vhI)V%@22|B?l{MP%9_6p&6OQJ+CJ7aR43C%5#+hn<(*Io%z{u zGeCz^NzokbUW>C$&Z@v@o_@2D!1OF=(~Vz#b#>b=wTZ9C?|K^I_+9%muzn;ir7SJu zEynNAbTRyoCF(z!3;Y@%&1$Q~9cX%V zN&j;GX`Umw?@^7C0JbEyt&>6~&Qo@3<^+@NcOlNl$i%_aNpZ8TvS?Hn&0Re<;py#m zjWpLZ>4_s)ojpxCx%65kas=T>v~S;zjRu-lIB`8%9I%A6=J}2_Nl5c0i&y%V8(SS} zY+N-insw_l(jx5pH)o}3dth6&>}$Pa_@_#O`BLV83F2(>BC%byho39*~Hj(st-==8c7pY#v zi6vb)F2yd-dswb>>}vOMkWnt%EOl&(t#pySG57wlM$|YQ zTQlDM&kQ7ZWtq$Ry&1dezSQ`vaa-NRoYs}9<8$$OM|t-dPtW_^vzl9Uo-)3w>)nU? z#Tt84__{l`Ra~6{n<%k5N$(ecv?vVRewT8RK`X3J?EYL{^|@`)Ri{t;DYOnh-{QaQ zvyu3er@)$PgY68bjFs@Al}|^6yf(2}det7>V0Eenk|Yk{?fkd+u+>BOYIgYmbmRa#M6?PP zg$B5c(eLeVzkO{!Y)6;(mF6#Yg&u)DbUES<_VX1m_Qyl>flaN)C(|1CbqMa*HNN}_ zBk^Hr2Qwwj%Z^{Bk$)q%~(K>p0JqL)+I);pE&_x6k#G z*AC}}1?TYAzF*qsa|)Mx;ze?6!kEMA`EyQWZ}%SxtI&SV=d7bc_`QcXQE@Wl)Rqp1 zVtj^TZR~fE<8S*i1pnM?`Vd=h78;)mtGB&aqNQSBFYK+}-9v#*l(>85;x6im*RanC z-`pieV0#kKxVIXnxuMlrRw5bRT!V5yLT&f2{T^T41c|+qPd`2{`}RFnf=GCppP5dU z3(L>4Q{$omlxQ&fGhXgkPoLl(EkBLxd;ihDdxQ&K7M`9k%w&anV^xGg=eWcXh&@Ss z<72At?8euNFTxSac;uKbKFPO>hCT3&Sxjn7yW@rU-teylE$ue0JvPqxh%VP*U%#@1 zo;19v=@t)SuLrR&@b%Is`dR9WVVToX3LOWV_8SXnhhOK`X570zoNHHvt!m>pC(0R9 z)~!o^`PTrCTsRjW?f#NoaK&rJ9D0g-W+E z7725zjQ8Gt4u`A^#J3d75~t*`p;4a#JaUf}GuOGjOVCw(i=X{I+AR}}>r`CXg(B`< zqgRflF7@XW8YZ4B+bsiY9C^%A)+V3L_NSDrdqf29`udmax87dx6!^Vuh)%5LYtXS5 z=T`6Tpui?d+#U09m-WIYm(zfd%yV*h_Y8}{o`xv2Ger}-BvEG%{I*VL#|j>-UjEuT zYn{KW?C^;_BkK56XBh8V@M}0Uq(liU6HTq3&%Qkp>UurLoSWwA5hc*?(c*OROmP?} z*+60rg<;OMt?&EvgmX->w$#_#V_Hf0Xh*~Gq-YuY#fTlWU$vSmOWJp{@c-epI`%}lZFvOy1@A>Ho`8>{$vrJ3*yt30c zT*Q8Be5!B68fzuPZiR&Rh_*G%9NU}&7MgR0NjfU6tsyqKhTk6N7HqN8e0y_$PSadx zFyqLPTQ-Ix&`7(X%i-u%)+NW7R&8LrG@@9BoM$=*q0;DRzJ-4;b<*VQgFaYo*}tfB zSj%#r;6uxBDyur}xslPz{UM7`RrTnQ!;iVB=O&JexW*zQBim7f>Vz6W-KEx58gc9V z(ejSUU(entRAE#Gobsw}T=a11wt^S!%m#d;z(NXaqQpXiJvXZJC8ueVLB=b1O$|y- zLD~yG=`_vtCq{fhU2lzJEN*@#Yg~@k#)o}sS(?PjfBcyNU)@v3i1nadO!#d%rBOWv zr|dB;jy%U&F7Zd7!w(lHg@oUhu~)<#kOlX1YdUzaN}L>OoJ(jan+tZ+Z_If?QqqJ^ z49qNFu5p@Kvdej4Ef0Ow_m(w#;<3jN0U*-tsNLdQSW1GBViJoYO(EH zeCWe6nDFb;{I%+tbMc`OkAWHwYde*WzPMli$!1osv9NB{6*`Ws59`dk$+wjCbgCIg zwN%}mf^Q1;cJJ<@z$QxEUGs4l^oI5{Kb2m+r|hD-WEeDmX^2&ZGsF@A3+iI;BH?Bk zSA>$6GCykzz;so`2HI^mK3{uB3FA8jpMIN{HVm@vNN7;Yc(C(!FZBwC`xJcUK!{$1 zLf1(4$8y-`xN@gdIoVGcAM01ZDxZ91i_W<9#V6DKWUo&hA0PHyZ?wuSzFd!3(+JPr zS2%Ti;p1KUt^aL|3k`3Mt6le}j1O&sr7r6X&Fb}H4aHoQz8H6#PyW>LW&c}8tXz*n z<3`ea4wtM?u^_qbYSuE=1R9rd6I*EV+j-4)OYm9e`cLFac-BvfEy}!}W&8eY=QZmM zmC?5{j@lBy!jbE>UXR({9LGA|kH7bQ&GMY%fKR`rY{>bHXFQQKrG93;UVQo7!D)Wt z8?%2e4n2o8N={DJvwHQs*LB+Ch*c>x(O2uHUT^K%Z`(Tey!6z|wmClWJektNye)x| zuzJcIpYIr&8fPzUdf>NptTWGnc@Rf;Nn9=N?fmYiz$QxE{d07~J@krTji7a+Ik_Zy zaSK2C-hVGxL`HgA+Dqc(paKuJLg}}}e|iZmXm4nv&c(<1fo6Noap+2@dKp*zsu05^ zN!^=jG)s5frs$*?v$KheSYEj|Jl3ynCr6ZAnY$C zP@GgiLjm(?^O1fC3KilbP|y1&L@H2KZ9u6SXw+6zMFP^IQvN_{R88ZD)Ui>BCqwWI zCYf;(6N_ZV7&9@c9Z#IZBxW3fG1Si(^J*M0Wg&pzkwbDw*k z_v<{;dGu|(+rH_y?Bgoq8#$G^?#?@&dG&1pLEBGxUB^9k zBadU^c8l#jhMvR+?WTgwCSI(BdJNOTJ+=6QU<(e}&1d`Qb79Mkjooz`QPk!2ed4oL zL+j+4&0|5`^7+Xu*3&XUWewOMvtv$Y(OhHa;zPeqh6bQCG??&Id`9iQ4zsw zuHv?gjeQ<3M~n#9+JSai<>{~c6+xF#+w~`&_}m>G(UTT+ScE7@gO;V;T<};%g?4$s z&ib`M4g(kdL^+1JR2TvMWW(RqqpfE6wh8!oxjq=%(e675J=Y96we5S)Nl(8a>@p)G zmpL$`x#L9ZoxUUtPey)&-tAcGgxRM0;deKU=T=Y8*s-@kTzM0#Ut z^ZdrCjcQ!pAC8*ja^2Q%pTifc;M2Y>n|k(M6qGoxCGNoHA^SQ7|CITO&#|JXggXU` zC~W7`Zq!trHGieRCX`IB~;3JBR+W7bADR6L&qrUIqi!&|iN;CIt_LJ4hv^_JUZ7vKz*7a_Q zHQQF2c(zm8(sOQnTy;|uo$c~$hzS&X+1T=Im+2HfB7a2oYUb~~-}#-B^xs2P9nOuJVYrpuYXGsID8IKRaVcT;H(j>wTPQ`u432EeEG(qcM*>=8~`6J?-cK&C4Pdh zf8)O$QP8sg#J;@Wr3K$6%G#@k=vED1{yEKd z`6Nap4|oK=tb?YCc(g=Y#f8=Yn>txTh94 zb8f2XQYd(0vLA4!?RAI20+_69i6{$pDk_bQ~3!1=X#bBl}1^^4Dc$b>mef*JCr?9=M7)|ilKFByhJR0#QOEd-{v`! zpdOq1Lq>@;j8S~vBMhc;u4|k`HEU@sve|}Z_wEI=?u&az{}G2noZZeH!z-@k#DE&_ zOF#6*2Wgb!)6Tn>>c{UsuXa4%X&K!~=m_t5b844h?%R^%uwz>Haq8YKllLZ8Jlh_V z7V#=XFL$pBu!<5(sP#zH#WK;FU?qp`ijSlWwl`0yrA4u+zC@F^ z6fG7X9I0%Ek0V{r7)J%zt{NqT8#A9n#OwNHPS|% zc6_5zYclBUj-41p7$Olis%S2k7&;Jpay`d-8^l+SZ;1idb@7RGWOr|{o42lu&(2d)jWI>8-uFzv<&DZ< zBj$`4hvitNQjwB~pZZVbWL{l-iy~5C$wxm%q)X>)3a9V1cT>(M_z^8_J730iDqM9R zL~WxMFh5drb?!f(Mm*z$@);|xeP6L-3D`I1 z7*8?2pZv&|#!1@7;J&4Sai0=j7<2i=7wu_1E_i9}h=D#~p>b|IbHp=z`VS@>>xfvg zg=2l@*do%=5%Ak4i~y_)_`s$;REI%D*xZ*q`R284JNHc7VJwxqMQvSl0~Zc!(=J)| z0c&cVwr!@K?J_Xx0Izo4t?P%c$#yL2x!|+IGis~?-reF_ignSJamCy@7$O6ymlt1z zLz@1&&+f4;+27bfX)9FE^>0m^(VdS9#I2f6VXRmeR`(2Z^8J$i^IltORFe4kJxtAc z8bKwVOwpCGXw|KQw}~%y>YCc#n~sx9qe*xK67@acV*wvlwP`l{2%mP|R~f9-d&9MN z(t8o3Yu}yCPRoAYETU~;j!)n1e*10st?kY*D*4Lb;+~x_i5z}|OW)CU^CO6EfhkSn zs+=QfFCeNNpJUIjU|dwgM_4=jO}j-iEeux(>NdkHGHL@%aTG%XUFzfv>n;FF6^#2X`uu z%QRYopJ#)d(>6CEteR^OyOoj%>ZSib=|L3 zSc%P?6h?-thG|-M(AH8t>zDvCc7G7av~)u(((=I?X=O zVt?@a|8R`W_|t`bPGrosb7|tMLj?G3*vZikYj$(|<>nlUDUo;%M_qfl);mo z2*16AQN+{N@Nc^AZyh}1gwyA#%ULV9@~Yj#1@{U6VqBHWrsKk#(407@d=QT9LI$Kv z_)k3fm=7qrgK{UJpkJW#x409;%_l$?2?-MTsK6e9?ZGCFw)qrF>L1) zZLnv1hESyqRgAd%F45lR@MIZ7v+QqAT*-Pw&a9Vdt{2Y?$EXvs{#2T6>>&0u%Jtev zJXrGBNiMz)nI|P~OyS9VyAE*1StFTQQtBkM_S?#}AtF@kN5GE)9-_AQTLq$fo9o=J z3eCNsug>JiHI)57W^MEk5|48SCt)Xz^O;L|JFXtA(LCapV<*iy9PquT9Oo&IDAtOh zqi_$^yyx$p@ZEa|?^=F;Q(zS(?(g}z;odPS!4ifG;SJ#4;3;;h1?L9$`H)^j z5YP=S_%_ey!IyM|C$DqTH0dba$9~#0^*CzuICh=P+Grd`Z`1#+Gn4>2#m6UX5#x9#fDK&L*9zb?a8rJn5^dVH5Pj=Q_s|D^9+dK@mT>fh7; zI5xkpi=g@bnmO)wciBfcH*hy&0x$7g)^c|_=403BKD_y-nidz^anH9nMd@aYH-&G5 zXa4--qen-z-3ZNQX}{UsXiK-xTYQXL%%i}Pv;CU@V|}G2&jx+3 zTKHvb@zLCxM}}3sx^sE7))^4ed_+LnP6PN_E#@)Cg$%&-)rCe=Huuqm?fBFkjT+Q; zJq#VT_&)gk-#_^~ir;jFPe9Wa*2weT`(mcS?u7B}*lNR_nPyjt;Pabe5O$@oWW=t- z_IH=_H@Bu+_;ZN7W|v{A?kVya%H@s?gTr%&aydnxR~XAFu!<7P zY4(t|z~XLPSRcRh-gkX|1o%6Jo$(a8_QaO$EB+Yw$SL@3e!2_tTd+avlfs5|)Ci%4 zH}}jYHdx)V!4CeDuY282L_0?WrwH6z#uGs|OUKTQFW9AmFC(5r5XJ2ro@0E$R&Vj$ zEt~4;-j3}#hiI1cG_EJU{#OP2VXefPotyY0%Mu?Jhfj#Ld5k-`0Fgw=n2(vhi1xy_ zC0Q$)S}v6{O}a&Cm?N;JQ3eBbJ9jSCrs{r51kgnHZip ziG=D{Kca~7c}YC{+5FDKFtgyij^T{p&(;cG5UP(jqou%vi6b z+^}fvoYZ;_wyY%w4PP92S3VOWpNKQx);ZPldtZ1VSa|l@E}ty+wmsL^Q{ZH)3I}bI z&u@N`dyzX%i7#DQ?SR04aSbirT))E2wZMPot#6GvOMDT@XRhIqWx?VUd=dSnz{tSC zq2)Rf=3KMHmpc$SzH{O8+rw62DL(y~&qTn@WUJ=-IZ`&|UKz(v7MjA7*Tpr~i{Q;P zom@X#9E4&kWFcfolJEG%%k z=+ekc4te1GcifqOm;&dP9! z(B(Qt)HZJZAik?aK39rLNs~2QDey2*U=<}EhFKgMgU<}sdd_8W2+)B_@CXveFvROW z+!7D8xlVD!-!FLJa!nbLTn9(pHu2Gn`t)8mvQG`Wy{EZiSYTXnI%`|u#Lp>u!FLlI z?lnGI(i0Eo3b>{Ch}R;{U}v!Wc{u_t+`q42*w0vk5Brve0J)ayGXCr?-pk%ZbS#@?x) zSv9_Mf5YIsNqp8-9-I2iTmM~#iMvQzIsQ|ocFpa&Sue3ee@fsJuo!u@yKNIy*X#$7 z0;?$T08ZL*UFPO>6YgYsSG$y*8)uD+f5*A;)$Vy0*U;vU%wjvXkv4T5?e)<$LKpSw z@zt;n-M4++z&g~bu%cUc$0;L{9)uPb7)$Yu)!$#s+1F$6ZQAxmv!A!=*CkzQoclGe zKDBKR(X>a$#5b~DHt_fRy6MB!zRlqprOgzK-Z)!YxV)Y>ujsw(?nC`5A8k_u%JvrnpPH;&f2u~>CBFOwjI_zR zaMgH6+9tlsc9}DsZr~;^^)~FtCg1b?U-tK6fQ}$#-!4Cc8+@0~^Y}J+v>E-|z*qYs z`gr*~0wMc5Qg>75683^M4oH`Cc-1qCvl`dsoQ?If#kj%Kr>U~~cjkG68naK@2S3?2^rvB745!EmTG2$^c?c)=W(l-qZn3O&{4GQWH}w}u#O ztQp+RxsH~x#nD{ZM#@zu6Da3%v8p zknu?7#HEmzIEXDvOe`gUwr#x{4uiKv1t$ZM4pfg@Bf}Jy5ltP5xk7bPVv5W z{tf>eS43@V#>yRAnMi6PKJE2wVl1QC{8kub)mLp4%YQ=ID}MefZr8{5jly5+trWN) zD6onW_roj<^}**x^dZ-|BFSV&1c>8GnXlBat96AX+Fn^do+R*cSHea zwL9dO5iY$Qe6RSqPnXkMHz(N6^;cQlXQ%hh)$ifMx?)M@+SO(JDUv2j@nMD8GuQAA z-SgWa#<;%jYkukE_=E(=dXW~l+7K-cA!y3AH9qdoG1jL1%isPVJ;psh>m@A&9X%#8 zT*8USN)0ZL+}5K%+kPcJ=_h~mPcEis;_Dr!$Il{J+^kpQb1&jlr<;>m3CAhrS&4o5 zQ{_F(4Vy!jeW-d}v>zj0^u^P6lF8qKkHT#s8iCO#>4bY#6GK5c&ZLm%pn(e8(6I|-ji z?d+2{dt^I@%KZKh&6g926HX7GprUz_w|dxIXI_A7Ov#2T8#!YF?WgcjMeKIOwz%5g zbge#!_sxL;PT{kQ7SYNI9{m9~$5n$&u_pUmm-X&ze1ejG(-TkJ_U@?PRa3aYi-=g; zK8-{+s__v8Gtyl4SA@ORjlS-8oO`jwHxlf##+Nvx0(4Uc9C9m>TEOd%tnlP2gJEx3&I* zNr6?Acp&DDcI2tQ{_F0z;wZ8+_WF%)esc+e*BD%!37>fC8MTJymi90{HD|!b@F9YH zmd}G@oqTS->%}iP3qG&ujEv&=HJ>AaEO&9?PdvV0oyxed%Rb9kxt&2y$zO^N`#Tre zBUyWFl!!q2Z0=tE=tsM(>{K5F^b{1>=bHw$tp`Wa?a7PJx}EXn;i8FO`Z=uG;NJ0G zBH_duEbyZo{mLbR)>1aX5mgCGN|b5)E7DHQTC0a1WgZu?Ff@%B!6$J>4F84|BAV2! zAiDd_w{$=BYM?e|zjCy5`Z7*{@6;6Ij;D z@EG%43hm$UhR?aOCaw88kg6za=DI%OjP`x>m_hG#xQJh^h)mq8#WFiijPTHhp&^_p zN}BK3ovex1xzG1t9mUEH03)^9V6JyWDHdInp?XK8gJLf$`&ZWVZS!D` zT&a1Q4D2I3UjJAe_vgarv+Q$=Rim+Y@AI@|BYSkzY`n1;*b;1^&x)~~OMCCL4?b2I ztAYK%7A#?*M#tu}<-}-Wpy0oGx^`$EM}aJ;=k!=(V;EsPf{1#CdjXi?s)^5fw7cVX zcTqm^BRd3BNCaSQ4LGtcHqLw*lgo*}S++FAQd3T@z}KKKV8`N$AnhL=QhZT*IcPc1sXwKP&m<0N!v@M8Vg zb;={z+P3YpcW#cY4ZfOnlh%if^s@d`>U3p>o{6s&DTuLD1~K-sF6aDxrf}5x=m8c{ z#OdJ9PRMei9-}wqjH|xp+kX45EbA;!9}j$Cc+@ne9?NmtJ!p!LdQ5oMn4J$Q2|nRhxkF<^N8*Ja+e=zsc(947(ZdeQI~L|#yRZx1YNit3E}d(5WLfTj>~Pe$ zkAykZjAkFp+r>kL}^UL44uj`&`EZ=6J9=;l8+CFSTa#nnT)aPTP%^Shw%yC-tc zwr9Q3l;)bY91K_ed$Nm^ArZV&7)&y#;m4sa@rK`-%lwb*kk|<_ro6PZ6C;F3?t>V z5)L-QCO7RG4U~0~<2wgF8g}r*m!Nba*t_zKwd;MJv!BPNIbZ2c>$d=ZJ|7Ers$I?( z;{nl$f|V)rH|L5v1#2W9KI4fwEJiL^5n{(NwA5dbcIM_i@#Px9E*?9!)NY4g$i!gT zz(43{nW1+rm1&f7*iD(Q}Y22W3m*b_@U6^ zUlbLI^-07Y5h08>4DHO@_A)L@-w4t7s+K73z@uw3pl z8djDuK&^2F&eE5;^_=~xQ!uC=TKMqzeNn&ojs3Qt`}CFfzx-o<>+ni{iA&d;E$~A1!~Bm38E|xd7d*9H0JrPco3RTb#6`?rGo4z7@i9sIx?5 z(y!PNEA6!PuJV3NTv+^eS1ZS27sam7)bA+kwJB9j!q0W!`K1s2v9tI9`d!%Jv0jK< zc2bBmMLfA8vb23&pH~W;iUO-BaVkol1r7s_VB4yV;d7lSfn93tki80zWi<{FBtMI6 zslDR!({v7eL?lKdaEO4y2*K<9T;oeEoJd$ci*}a&<){9qz;_*Ek|~-p$VvLK%TFUU zV_q69X9`E1ALk@&*-;zpX_ymn=j)0my07u&IHl)uFHFCN8W&A4V~!IIX%CE`ur)6C zg@swf5+*7 zy>(D)mDQs~T>`(Zd$;VqaCuIA@msDdlzZb3AD1mXX_%WT!;g0S5|^|WY?TpcT}PCl zV^sz%zK?(UFYfg{-EHB+7Q^M9sC&##q@96Ul~WTYPkyG!f6x2g7yW?4`-PF&F-Iwa zpF0zAeo=Y06DaCd<|nS~3!Fj_7-y&zvS1*scZlsmgc0a=7bM0t;b+1E!1#|jzA?w4 zzkXj*hQsIZWw2Z2`w0it6>*}7VzbBM4UlcEvQl6X1y)gF5xG=Oms79Oa|QOKW#ZCm z!&1!c{Oreh4~u&|-vF+j_6g2&;EO0R1$K_9PJ1q*^SwV@I)|_40(%(Ze2po5KG$vJ zo_R=PrBnFgsI@T=i)uZp_~y>p=n;^y5jNh%xS?kwt=(t;%wO2m%lgtKnpn2cc8BWU zQpCjXbZjbW+K4M_f{SD6n%}Tfn6o@nn%ZDHo4WeBWPOg}>(xt|EWszpaD7$^7@`U} zBf~myftNdtiK9-5pi9KTsR(5?OzWb4Zts`8&!&FIHOxc!s1$Op52Zpf5HoA|SMJL< zRi3kBF19`ybsfG~OQJ{FB_(3jFzmehuJL8_edVu5B|^ni?^a}+diUhe7?s-SNO z+nt)tL+#l_6Xn`X|7?eq5UD1g>r@+Y;R9#U{hZV-f8d9zb`CQ5Eq&;nwL!OaBLB zH&s4%l_Er9evoM>_HO2)~cE@Wjs%c(Q0D9alDGkKUrhLfRSBW=S7+xQZ_U!-^IzSiSu0 zmom8dBD8JW=S`c(@1SK{=6Tca$GiCI`U$7b>)q$rdS#n26|FDL?}5Kjd|+*G?gKPl zG%~)NpB_!K)m*=`&L-W=cjm#9ZF4?yoKtHlY2NF1!p$`6f|ct{yLLX|R43_ye)?1A zCCo#qws2_Q;%VCko-JOuN7rQwXX&$k&CTahcx(NY0xJa`YznNR#DhJ5YwuSI3{Zd( z+lcWPtt1<7@!SW%PF8WuQ`iYl*4Pwa)zZe7kwINu;7L0UBGuIS?L7^&h%zJiXo=Np z{Va@|-M2EAvk6)}`OI!xoiJ|6%&Bw&AA`cgC8fol!f7tlaaP0>nq7VeAC@nJj3!QW zV63xix4q$G!1!YnpP;;8F$~F_fZAA+k&p5aUrHLTuocp;{pQ~quCcWItv^RqZFkF{ zG{?YThJRPuiDTQjj@u2&c?MTB8vtnRW0#@Ovh4UAre$dZ-oN@+|8vkoR5Uwp@H3Lh zlad*t8GrUG0_&E(aQhBAmYt0`BbVLawl^7oHsMJHF|F&NHJA6gPTtQ2lC5Fnec9c4 zOrTWl1yJQ5)Qf6V*HhQ}7ZQZ~eZm?|8RXf?JapV@)>HQ!#-XGAUF)qBSSc`1fmM{4 zr_WltQs5z=0M?+695Oc~ELvfYZueX`#hP~{*Axo?2m4=9eJV@f!BF%^{ z*uy%KRvIs5zZt9af~nT{(0&5VE<5qHQDH6`yYn7C23~wR^aGUhv^K`=GECJ2m z_=qbuHZ$?@?`ZoZkY}*dW$eBF3oD_$$X0FJ-gDw}tXRAL?v`jv^{K-xJ+YJ-fO^FL zWP^X>noEqx{$Qy^#}Um)-?9DNSRGR*Qe0kq50shJJJHPElsJfjDytSSyT`0xJdX9R*fV;@;7I#k^8rkOEjGZ2b}q=OE}S z)o63EPkGIqDqJ_F`4u<4Wy|Y2?L8JO42udrY>db!8PK+^+dk6*J2wMcHyXJn-F%G= zi&w+M;tOm$w|gTX>H**zB8EtkOxyg7Em$cn&szKT<+wAW2eJ<*{?um-liDSozd&HE) zWoN)WcDr}(0bF4eRO+b5mAHl9KHHh0sZSi4+WoLrzU$qui8AxcuuGmx^e*Gd`sQ$M zUDbGri`JRnlJOFmtQU1egt9}CaN87Ztm}HM)v%Afw?juPi_^8Y^rJ=~QXdO*ZMRZj zrNF~OfmM`vc;<2)$wNy4+G8%3#~KtYB@B%;HpMv(OWPh(KiO(Ay9gY2HJ+@ zSw5VujOx>=wwLu(iC_dDZGX*D#%b>*Ul~IcHt};BVSzN&-2>ivcxsEU4z_A+#0-XA zc3i&i2mVOF5)72a8*7>dUv>wK@4fH({G;$;vx!G+_XXC~MdQ*hhFVE`ec*fZ!5{q3 zBai5@ajWhOs>*uT^S?9dnUxFCzct{bm7=Ni}9z3_AYlUL(wvI*xCm8Y$ zxj8ckw3h#u$8RnA&GL`&yGuIhD<6fuPM!FHVWUW6#MULMhZA>W1-DXQrND_Ou!<5V zBIF8YrNDV8K>JN2%@|}U20IySsl&)*uyGin6vK+N&+awj%8D&)MU61PNu!Kj!zK^g z!l;QT&^sZRfer%_!7dqk6hp2(8sw?oPB3pkUwO3afV~B$XA8~kmzYo+l+^g_proM{ zt@9NGO-A+&om#^r+U_*QnZoBp>MR?<7w)!YY%MKGZY zXH0fKu;}u!(AHg?Jy4!$_mf2YQqo9BKgQVKjbRgeb}VxQkS%Z%7jXpq7vA$&EW@wD`&z*+g>M;&NQr42r!XVq^XutPMK=g7UVk zc8-teDbTSrb07mC2{%sFvgx36pa zl>#dTHWXMziFGBc6u7S`P&BONv;Fv;h?a8$J-rBbX#qnYr?nnp88&^}wAr*D>u8QG zBK&mlUBbPDSNCtK-DU0lyGwYRzSgDteW^=s-MT0=aV5TS+Fa7)DlM}Q(nEh)_i(+7 z=;waDdfc1){qn0%$KzXyYmV-{eu1w|jVI^jqBHgyMvd!GUF~`};L>)}e)>0(i#93Otf9>X_RM_C#sC44C(Erx%ayw4n@Cpe?{ z{1&W1G<0-dy(`G)ViUz^vl9Yt?=O!vbbMT|d^FF_q~DhSvvxXaLpd<~Hp@|m@mo^o zy1|^mgd>977COgo`y*c(F6QW$QKaCCC{`Mr;LrNweve-DJiBL1Sj4Kc8eW}X2rGz{ z0*@pGR#D=Soc49l4+aI;F^jx3!n)>&7zk{Q7H)@;OcoqSi31TgzkIeCMzz(k>|odS z_v&Eh^0WW^JAd~Oo2?zU*=)y+R$l&LmZ+{ z&Ys||EYN5IGPXR%FzaU7RBA0d9?|^g(RA=(Iqif5t6lJAAFQhsIrzsU9_bVfrf^#? z=Fsngrd~a}gE6nA4>fG}*$4OfT6QRQW1qq?nos2CKUL#Qe8v@LyNm)`SdVMAcOX2q zIKB|fT79L!N`W&|U==0KOyCv#N`Yw##Ib18#aBw~GB{=TZlif+WPnY~Ys$5h5yTas zoeV54(Sk-4+aUT+YF}xrGd{mESJdmk89Jf4w8NSuEU6Qh(-riGKlGu!GUeFC4!T<- z_|TBHv&JV_CwOsWUJ;4vvN?Qd$KLBp=F#@I@Dd+QIjwG6PP?A%>vUv%Tz3;*7^Zyq zOYS`Ry4MA3X$}MScHXjH?+gLKQ;ASD{@y(eZo&yS;Hr{!8XEEjUu&RaPQvzHy#s$W zz9*je+$;6ttc(VqogpFt`Z>mpOLO-50J>6qDIPI4tm|X*Yj^b>%m)8nehr{=Uym|AqVc<`+V^hjb1X!q@*8k5=&IrBO9r%MoY-Dj*27WfF;cQF z_h?d$Nxq$@C2ENpe(&DdI8+_+-l%ZdHG1!N{(;?YORw#9@`MMl>qsPY%%0uW>%d#v zt`xXmDX@wX_v_58{d&kLKnp&ijpt9mz@|qIHq;jb20jcTh#;-8P!A^7Ni?8j!QO{a^WIcZf4$_8-C*8t4M!e|_yQyuw;0 z%uM5ahEg|nVYZo}H7}T5G~ckp3T{t(}vTiVU!;B)2x(ZgKD z3DHxPoBTYfS+t&}X!4OC$FtA8dZT$Sbg7T3V<^(DaoTKbu0AIFYL{XXK~rG6*QJ7^7;IVFNw|90Nk{^!m@1G@WIixL;jZ`;m9hgM$ZW!w+M%$cWKE5%$99l*<7rBOv_+9v?;vrsRBt4`FZvyDsr z8XuM@4Ud$4^v8d^J3esYyoFLHcNS{cz55m)TW^LiIX0U7xw$3Ys5^oWJBzhdA8RO> zsQF4<(z)aO%pdktJ;pt~@zi-llML#*V>c@;zP?P^fD?nU=i1HHtMPG}ugBv4Z-48* zAFR9kdQR(h#syEd9W@%%zidM+NMoMxcPaR|{_o@Y+Q+cJ$>M8E%ot^d!?(gX%I-jE zvwKI_{c;uvG2+>`yd~Vg^~LTWu^%2j9mX|^OTW!upakvMv7b_(XWDO{Cr-c}#P{Mm-VtX#TomoABfLg-FPkiFf?8cGf$&@Q_izlCn zo9gA+7MXu2wMLz2A3IQG7s^=ebmUnt#FSc8FfQ)U#$|Vp>+&HSBjw&Vc9@KT_#zAg z(E$x}T!(hkM)1^i(3^;z^vx6vPc^O@pL;uL#8XC2F1tFX9#@NVeO@W>Fj8O@B_76E zUB`5vQb2?jT6mhpb9a%(5i7Jam^cAKECxd8Fc@!2&Am+b=cgWvy$_t;NIrX|c)9Lm$CmB-O| zIq;b@0^Sz3QX8z8V4hh$cX{M1ucLH*`-@)?N6Teju=&|;D#hOMX-Rm}dQH?mBG}x! zTjMOOe(WxkKENj$*N6y-mCgQ1zvDZ8U(r@?T#^OWjoq6qO#MpnB!cd4$E;l&40vjM zwLg$`Q8ea369!IsincN5`28lVD+XejhYhaR&=jss{-sX_yMB4bC&+H%>7~XbUmr(L zd9>S|!ZpDx zXuf4n7PL6FYS^q}*$OLrEDRBcoX0R?0EU)3mgES8!BTGf%U1tT;M}n1FZD;xT)=`E zM|R6|2HM3w>Gzyp$EO749H^J@qi&%)pOWw^*^Vpocj>Od{tZ?!=B>0F2?daJZ&SGW zDrCMn+Q+nc%JNYk3AD=oNzsxkEod=s+wn5;X#@S%>vGiS4qWZ{@66;nw%umC5mBI`Vs&f;*S*d>JC) zZoO{jRiNs*cGoTQzCq90@8-HN*Y+y~9wiE_qQs*#+v`~GAq8ke7ynxOc<9 z1fO-nkge_yqpM`md*iv9cd1Fo{CXq)tX3FTOM2^;Tz@< z6Zz20k%+}CnEga9-3x-JWVSxp5Z0iMkQ_7=W zTi84^cmb=Y)mAlOsS}#mroG2!zWKKX3_cv4;wEdu)`#yvQ{wT{ckk{Z@$`bIn$HaW zIf5^CUC^o%m>0X5EVl7+CuZQHAJ#s%{WK=ypOJ=2%BTL~Y>fg7n_b(pgO?h6xbm`d z!6iF(4Yv6VhpdAs9Cf}ir0u>?2@D2_bZ>v|cLWY%7k?tgVGQwVe-vXUZr1&*VNcy# z6AX=v!5RyHjB;-*40P&t#3DvPe1-J2E?aWXfXI9k-CcZFGTPV@IBVUN0xJa`T?(wC z#G^a+>&S1E0ygjMNYMP98!@)fPDC0ch>$}&Jc3srvB4u}7}W6K6!j;yr%tJ@%X{|> zH?V8jhM3Z;HNoo%I4^)kP$-`n-by1le*->XySWsq5{VUOjeg;vM84qRyKw(iqd zDcR$;ynU{BY`$Ea&?0D` zxApmsv?5^`Q&sT5^_xo&DN-c9DUmDdZ_vzfxeOz*P#YqQq62tT`(MPD}xYO|{GLiNWuI zdfes8Y2C8hfx*4A~pN_92cB>O3C>6B%k`1r?f2m4;?WS-P+Q;GyrcrxD_uiMVuch^V3 zH!^RDk83d&eGbo5nK^s!3ok4>zXD!osMyi5>rumY>UsTK-+QNA_xDZI;YKf%0iPwm zGor74)EAL)i7R>1a!pqXtQ5Fe3ap~U&C+|t_DE8ImLHoy;u(v+Mf9F+du_-1yJ1JOpQCiidd8LG9I_X$#uwvEK!Yi| zE}>7|zhK82Ja#$e=2z6E&`NK$+fDN;k9Gfg;|n|da{QdWOwDhyv%U5$!aSus=P!)9 zs!F*lljT*>?pB+*@?FJp(~d9tx~<@MWv3zMr`Lb<%yUnAfpcqId0+Q0$7kMRH*GsF z*?;h}oiyjS&9CdU_Za|K|INKI=DeA+n78dZRvz`YK$1RnUJJkXoc(XptXr=`G|n}v z{?t0kw!PGEwH}A{*v^MBsOP;>r(^Gx9@aH)rNByob5URwCC)|J74Axbpg;+5o49NJpF5TnpS~e5B0AtPqsKjg2MW=IUk1*D&=qn%GEQhn5F9A+3zVa&% z{tj}CH1)N+c)}<8FebuUGcLk{xA@H$Uyg(|ntCr7r(+c1v$G_iZQb6x;G)pcyUSep zt=02g*In;d3ak`(G%2u(5|8G*ucN*(3NToqJX=;)`K)B)I4V zh+EoXfkyC!!B45n7>dzP4bxbieE?P%AJv7`ebV&hkN%0tK4ta9C-P0^Wl9W!%Qa{C zmiR^tWw^^?r=v|GDk&Be3#}~J?W(N3MH_W~_(LB$xL$tjWB+yZzaAgjwZ<~?xa+sL z3Ol;n;aM_&#%I3D+}{qPszO7?d|~8P?)Bf8i6`nwx#vd^-%?I27%||=`OA5#Q||j* zs*C-7ncICsuezT5xzojKns6Uy#Fb;x);T1^loajti6LCB`xY02A>u&|_t1UpIQ-xL zZ5T*Ro!{V{^)E`Dy!=+YQ$~%&4)tLzUn#Is;K8QADoQ-q^SAc?zM+7KKXKCY4fKds zkgKOpdmFs;9OyHS{-JXKM3mv~{q$e^)mPZ(Ucay+L@n*=^4Mi4tW+G&RTnh(FTCfm zU}uA+YRcM2QG&P?W6g@e#E!KsZ0BQ++ydiJKv^s?KZ6l`5jCs8Y+bwaJPR-mv0{R# zW)z=xJngf{Ld3}Wm@gXm)L>&lYk!Tly=M`8Vr->vz4AH_%P#nA=Dp(Qzaoxq+qFM1 zK7A3ORek3pB%G-fKH`#c?!alNRCZbJJom!y4LGm;s;}BxM>Q8SwYTJ4a@vqb&8gSL_)v5DDQF7w4wdmXkyJ&Z}k(sPfW%~b* z|JfgpfW0Z65gkMcBQ7dmayEjsG9^BM52qa_wD7JG12@ck-pUiE-Ye{4zw!@*Wt8Dz+5QT7RX$!%Bfwlz3QYb{*FZQQ(3R#SQ$?`UmS( zw4j@G45@bdsEu^2)va5bx<%tk>l)Ws%VXN<<0YOAz73oW&K&!uUfYlCU#4yO)#r<# zL)Oo9Q?KqzOXsA2_H_g2D*QI5ZI^IsTnRJV&Xr}IskFs+v77fC>n6?o937+W-nf$XQ?UE_moR&Hrt*%#)$8*V{5t;_T*i9Q z{Vg0G@1~!9YR%=gbDv{Kc+#x1>DwH%k+Q2}ISt%C?hWh>PUFaN^=q%WD+L}73ap~U z!!eKRDDDXbiUzBzkvvCdXsEyLYk&E$C~%!V(1t$$&fi^*Mtj%M<13%ga48mEfBXI4^F?>es}$(AoIRuYPQdjV^A#@b`Xy34*xN5dj6g740s}X zami)yBeEE6GM@#}#hhco_kc!QQ) z7Ru;z?s|!o#CXgp*6B+kIve96?%x-MZr(rE$SCYyJ!fS&ja}JIzR!AnS@Tv3tQ2@K zDX@wX59YkBy}suZz-lp+8D+Vy+u>ku3mdcK*emSnF-8Gj`88h?r!He_KKp0>!Yo^z z<9hZjzc~y-7-(Q;M{Tz67O^Gc^gC`V;W3!Og1elGo%xwE*r{2#(epaO51`NydtSq> z*}|To6MJ=rCJ||-wC7fveVIzF4@P0eR~xwy3!SX3{|v6?Slnqm`Xc%)t0+e17MmL^+L|!ISk4AA`Q0k<-sS^UP~L{h7~1 z--w-a##cEWYZ83o1H&!w{RW9k*51=!_bWCuEq*GF8T9)mLJ=);F0>gj;DXaR3+W=S z9nlu9>`!atq>c++q{e%%prJl0}iW?1g7=*{rwj^rI09C(@B{#I%uQow(jdFS0jEu#5sM(p1<+Uzy7ueU<_8|)fYGZ7FQgnx2@;o z>sr)m>*h0<`fm>FW8sJw=_{Z-wfNc*rJcEvZI!Fr6BiRtrgaT{|`mG5qj1>587I-j$CUwmZ=mS^sS{w z9Ot%$U(?~Ie)4~eUAxiu$rRpRzW0^)4YA0M5+AC&TYr&W{WWi;z)FGpg9584aevIi z+KUH@0<@b;So-S>j>v+g_y-^P$Q9P_I&gb!u_4$)qQ^1fPQs+Y9oemU;_H7^7>m%F zXIm+)J(i+pgjCnddqGu$4bF^-l%dMA-|!o+u(eJI#x61HnrOP0%^f`0tG(Q-545&+ z18R|zX7v-F_%p$BGt3zY;z^8(U4^SC_HrVCX&DE%RmX^NCFM%3uW57?r$Xg_eEpTm`PY+N6<94^Nv%JRn+Y=WGi* zA?i5Mm;n?v)NizH_m-!hj=;>yM_KKJ0S7nv#FhEP2mRn)52xP{F^>2ozp&|70<31+ zV^Z^@|6sd_g?aBBlM#_@DPD$TL{d8iL_q6IG^x4Ww52%K`YQ!i3Ou+JSVf5kckb4n z-xCVZk`*>P+((xf8)?IMfbILYzxV~iVgdVT+G`hW=%GPlTZ3IL8pUkqG~Usml;QHY z)ka37nnMVya;z&ma$3?6gB4B1wAW8s`dZV;HeH#kLUHAm5lN2oN@0_M?oRSmKcW>? ztxa_A zmcFYmXmR=}WL}N0yI!_!t+U)&n8cF>+cx@XmjMp=7^OKi-R_M%5vz#$`sBTm##VMJ zM&1#Lt~ZwVfA<&eXd~KK_TfAJ?(d1+ryRGoMDQJ~fg3xd+PRFc73;mbHi4Ee*Oj{Z zBx=_HioGh{w@!L@Id5mLaQk34%{w9MIlE(MZS;Vxc`F4TQ3|Z0#3MTK>#&cd0Je{o zm1fQ*ZvK*^sFvVm@Q^X&X&!x6oxxCcDY~D<9&DQ0p4)F*m)HOY!wU5oZZLAFcMNDJ z5BXoD;Ytg?eNJPXiy7N?Y?3k0v9)1@RO+_TPE;nc32V8X82cLAHLA1?j`JxJC|^tD&GFwf4-+n)VR zF^@#V-cHW$jFg42r>e&eUZJTmeA5$8?2S?2T619zrrIyfx9gibNY+mJw4iT_#c9O0 z6!u(w0kgf2az4{A@Yv%9=Wu12cLUW7bq}LQ{$bwfS-z$#1&*b_DoPwnxwYLRN&$A) z?|J|G_dapwB85*UP2|QMauWq-2`=riTZJ7N`dobDbI%3qzJ%4P`;*#Hk$FZwQBQs2 ztHM<~+SP!{{YGs0w`PrFMCHj7hiT*+5LX|uDCMkx9xF|3N?2DLZgSY7XPqLF9A;+< zKCD4Y3&-dtg4iioKRGgG3r z9RdbqnTOsjEG#NkH0zA47pywCMA^J@m%$neU!>A+;*=BZ(>Trv)#@iMn*G`pzU|xS zGyfbglg4&SKKbK+YIj7QGUGa>J2c-Y@4VgG*{%(STytuG(J}Sv*)e3GW*5fpmFrx% zdidA8l>!es1y)hwL7&03|2Is5I5w?(-a;F9X58$aos#X7JsOqeI$eq#g@TK%{glzo zb>O#c8Mqi*>;Bx~c39f7WzQSdcg_bR?E1Iti-P9=^=O-kJ~BRZ;X!j}Z^EKS^J-WI);0Png$9!OZz49riuTyHeocqQELj zJY4g+j^-!|{QP%+!E2^s`yT@k8&EW$q3PXs zS>hI_6NTffi-KzeU#ycYYmTxva1%x zLDdo@2^Y4 zDZJDTjO`*a9OJAK(APCpf6ZSh@Ssv)6(t_jnOpmPA_}nMX1J0DAtwSm2`04A#SWOU zz+|PSfRRer%s2TXk67kQd)&h@0hslV|Xy7WqX3@_@oy?dYWU>^#+@YQH42~3-Pf5TV5q6;ta(Ns6huRJnN zNq9udx`GHtH0)VUbZDakIchD8dP;vcEO`y#>b(d%0k4RZ+)3*ic61U~rutOlbd+6| zwP#b*urBX6fnUQf;xhA=u@OrWR-M{?z|zs=zx?4Zi8?g+?Ji0MZg-n&r8S6i76u$x zQ)MAKl;;}n{(U*`Vc^e>fOd8vvbx> znt$yXzuEOx($OUhJD6Av`bbuF8Ag>4B86(?6=hKU$m>H`bi$W z%LGRL(SP};?}#SG532W`ISH~y6bMy|_m_)-D>4voGcN-Do{%=&)YHH*LLn5`zN?ux zWpH@HF07#LF$Gpp;vP@HivC_xz^f12!#?&V`VuTw(Zv1ZfA+_B?8y?m?6-Tn6sZYq zS^Tswui2pyd^GlqMX(mwr@9Rr?eF`6KN4E#I>M4jHEGxq$7$7#4&2jHV)Gr{R@jX_ zmgzgbs-TV@2!K>+}B1?AOG}U3|#sTU%|E47aDzK zM7Q>gffh&o9DV`?9&sSoo^=o!?2-?cU5R!LIigG%W0@M>+&#GbaP+f|5Qa6CmlvAW zv<#R9m-5=^X%3gNPyNsr2Th0$2@e~F)wZ+cj0Z6FsfNXn%-ELV%iYCv`wtiTEgYf+ zO+A?H_ymiFk3RvCvF^*iO-+nq$viQ?x(~%`O5Q}s#n8a z?^g=k&lFfiiTinW*1p{U1+Z*bx+&w6Q+HuwfB$%#6uSgAJN>!%{8O+JUwodYDmD3b zCe(K>LQ{0`anq*wB$W0CD{m`=|4FI8Nk?1Vy0w|J#5IPmZqu7{dHT^CyE)j5YrL;X z%T3#>W6QAzy({00x}VBM=RWam=6xUE2F4}5>N@qjIiFy^<@8gouO2OHnDzLwk4ekX zv1R}3{$<<5C9UDt^sW0aJ)+B!99vB%xHtGWbll*&bxT_3!?koh=NJ;Ep2#ys>!;3F z7HhTOm7jEwKmM0%VI$UN|9f*e4s)(%;@hOgw!t~q=J+x+Nt&7uG@SY!9*@uhl>!e61y)hwL7AzwpJ$+e*FMemO}+N*NYG}s8sWL2>DyHQ^xf6AH?A}= zp#h`~{NlJ|8>55eTe)lFKGl;Z7Vj<@mVi*edcN9vpDm2 zTFflirurH7-CHducA@QW(`55l_3xRtzV&i^Reo=L8poV%>y6J}VVO#s2pU>o^yWo* zJHKtcY`+=A%>Sl;y?N=azqZ~GEu$No@#Xr;`D)X8eD3S{9a)#V5pTzbX6<^bpX>Rl z=WW++2XOwT?x(gnF6*scf7oF(Z0irN%k0L)xQwAYj;4M+?gV)#^}Dvdw3XI#A8dX# z?t1*k;5yWob{*z?)qOeyV`+Ik{#s;Q3TLgqQs7ahz$!{S%CrB-kKL!s=5`*hHA)aC z^QJM!XdCsyzP@~#MSmG>=V&E1*h`MJOW5J2S`1GZG8~GvuML}SM{2RvZqEuxn1|9} zd&?L{j<@5Zf?aU@RSTCPlfaN|e)}AetqP10UjjDeu6FJUtMVA5kzP>G8Xq5k40bx= zg39*-JJuR7gaKFSvy-5mj9lZ$dv@>)UeJeu&4}%~h*(6j7GJRPTYRk#1$@?jG31K5 zfN$iaYwfU>{2pq53nw7|i+}OoN52Ivbp}c?x(b6e21?ddo+$zsmw9vr@9^LL?hOBm z8WwmiehTL2!~f*JjOSf`2~?YXIgIPpEp$jRh_O!?s1^9uih1L|kg(g75!{qp{}gV< zb=C_!{E-r7o=|R{xV7_R$}%anLHw4c08ixm9q_Ml#z4Yjc-Fc*GXlLRJ|FyNo_O-f z+jq;+i4!0XW(&m3=OwSC@4<#^g_Qy;1@2o4tfItyJ2h*^&QAd+7YlybJ7-`DJCOn%Slc=K?-WNyM2W1cf5lFDM^CJ9YJEl9 znPWGxy=>FDi{`ke(!|HMycUb%bmY~*rhy|$SbF1Mv`BlG9CYJ7gTZ1>&Q7dv<2Tf%Nr zZlKYpL40=0z|Y{a?j0-0#yx}55(kMEg3A)u^Rh-VAgnbt;#!j+crOx`iQsDlgz$!}IU-Pl{=Ef)>U?~>u<&S>!sOTq4I{M_rXGJ6!tb(hJ=CcIW zp}O3Jxmdg6+=eaAU^5RjKC=9|cYS`uz9(W}$&GP@jZAddFIwcHh2LW{?2-u7h^@%F zZCXUSz{l0NEt`6d0H39gEgNq===&6{XoW?dQD@X$*y+6)h01PL_6fU3y9}1yvCPj@ zIgvoQv4vQYg)=E5jc8MLqf)zU7bK3lE3j%qvIIi}a5A@c5n#0HXV&SZWv*}2Xlo}j zS=M>V(Ss$=RkQX)Q6ddrnNQ1~ zCmHu(_wpimXc>d!;6%3)=vBXo4>@<>M_x(w1AQHLc1UFF;mbK9f+T$7xhOjVb~x-D z`CaAJ21V|{oXZnPH|Zm_;}$OCbB(T1c2UORx98$EPi(ygxV7m@ft3RH2?bVB;y#&( zwHr500h%tF>5H%Y$}4Q#srw>`V`?^iuFw4mWi&#Im|9V+{wc>K>I&9VR3LB}t_TQ7 zTTE+NYv8Yo7aK?03@)RH8eVGsM>N{mP9!15Q=%dlxdhrrtI^~Y|}+rH)V zb~K3lrXu4uV~fa0Od1E7llOA>&2&692tXjBp&&CDrPkr*wMti?C z;xZf=px3$}vdcG4c%<-VojDx6GHct8!=a-B(NR5OXb;Ysw^CrGz`duyDoWh@Iaqse zPbffp&Mh)n#~bLuMPW18koyGJ8r*ZD&Lh%jXGaW<^o8v?Hp11~)!V_5&(Nf&5ycut zoch9!)J8T_V{|MadSG8%wFrWJp?ys;0z21=V0K_=inIL`o?U)nfwC?!tJd_hea|wi zl5w1LGUxJO+t=y-vl6nFhx8>dEQa5P^ALAOkZ<&{A?N{>W zU-wJG7%Fxs%J>*=iFx0J-)1)W9Ql_x>a=e5!SDaWyZ#7_j1{iSXYVU~48>wMYda^2 zFWuCQ3oZsq%7_Aa`t~f2sxZz-Y2bt9)NJo6_rwLebj0fH1M&8dUr~i-cT3Fr@c;E+ z?cjjtcgA3>KK0$2NLZfjF#oFVfu7i84 z6};WX#K#h`$?h53U=?Xk8AUiTc@#eumH`<37kUE(C2fZF>ukMjd<2&R)Fz(ecMg z)di-Q`7O*iReJ(o)@2B!PiuN^~9nt>gxd2$GY@ex^ z{P59y_vo4Rq;^hT_}?iu^^+`R1?!Nn$?(N1X8+s4&1H~4l(rYhdu5{JOt zWt1CQd-VC(%U^cK`6I?jWWU?&NYrf@w;k$}`n2@zE)wG~bC&F8oU~q^%b6T|{YrfL z$B<}b{_H5!epHT3#M;_@%k|@ThE|T;OB{7t=pOwWITp+~QpDre!Fa4U?|f7o?`J2l z#-**@DZf9ougm)Pj|V>Nv)`{Z((`=~`a$37_m4I8R54_;e(f6c;qT}E&2Rq?*X0&*fno8h_%+WS6^P(m2d0Q@WoM6Q-3%(F3@t;_%d3Re$bB6%DZpdGt8*lwdK8M(X;9{j@Yxi@ps@;D+@;TDtU(g2HWXzLw>`aBR(;n=r4=pWoJZ%bYJ%2>* z@Q>Nf!{~8m-2HnrO?(XHu($9TbNcj%vXphG6_)r-wCgkB5Pj|zaV(FFlg~hp|4sdF z7#&+ux3|+m9LaZ@-@BcO>_?q$b_!oT{2qhTT<~K_IAHK6%Kk`+-Zc%?Rqt4J5Vgid z`Q6#knSw!GN>-|cBl>!?ItfIua5*{K7V9$O2rvhxAa;R{G3vT($(OxcL z7nb%T+;y>Pg(a&EWtPGjsn2#g$Ax`OmSUvs(mJ&pb8lSO-H}L1qj<4d*b?JwcTHl= z6dEf>gDIHm(TtDSZb#n>%od(C8Z<7NUTkRkoJ`>~@8MH1T_H-||J`30{co%7^0(vT zN72^Vd*0UD%j-PQREIdQEh7%-r+niJaizfexu5;nd3=N83)tKF0W+Va=F<+G`J?sj zboVl=^eLjLUFLM}a{h9hQtLF|FH(kb^6gmkfNW^}_?;1d5bMGYjZ1weT(`eJEH#?< zelK>`V(g{7c6aafYc$W=O@AHqu+yfWu8a(3_n;LAM&bAB!DAkcvzOXs?zOv4p7n%w z^+i-W=X?D+{axFx6u6%#u!<7*(`>ALxh4f@lo&j4dprb+7TR^F-dwrJIzDwp8#xPy zVF0`7h{`hIHadp~J3*6&rDRYr#kPPEffLIoI6lK$Rj#8W`uW`B0-x~v}wyeg7#btog)=6#nRC!xJ zUA@~isoxp>XzKUK@Q{dMt&JHQ73|yjsqxtjOByL}&!kaqmy<|?~PJ z&7r=;&fO93=g9$26fhVoV&CnhzAY2r*6fu6_nZQ&C~?oHVC}%^D8LYhw(N8ery+gf z;~x)~;WnV?bc^FlYIgG6_-yoZz0Obi65HfsM#!EyaS0GP>=JtnveJf4K78;0o-ev` zegw9IOXNw7Tn$B1xM`hi_?jjSOws14gD`?DP% z?K|7|JOd-=R62r>%d5JRc(P!NuZEA^rL8CS%I}F9niFps=UHb&L6NQ!>{Ke$02gR* z85apOo36WHWk14Ts{(@cRyWI~BXj_Q7{qqIh!lI`kp4S|1uSguCx84;?JfaolQlQ4 zfB52yC-D#CXN8`29&-Iii!Vyh&wH@v3sjNfs9Wli;tR67?#}qy9ED(Qv{GQDz{x1E ziV`Oy>0_&v12r$+wu{yRrLGDqEMM(UR;U zjL?&>5Z2W$4oxd|JK4TCf1&tl^!nN6(HuWj+<4pk_F2THbkXMKUu(wU>m9Y$!VuMm z7Jo!kLIbpLW(v)D?c;>K9>Ir3b|pvZ5(VMo%G|nK6Vnc*7sIAEM}fgWZ$);xE(l)!J2RZqVKs``?8k znibQR?P!jg)!!`+a(k zQeF6t@b3|N;?%gZKMWmz_(LDME%bujx%xbvth1&M7X?;P;^CUlM|3o__OyVlUGX`{ zfkg}Z=%N|CIH@+d-&;l`sSTt}ZEInL^Rv{JF{}!9uxJtWb?TJrx?MzTDPyDg>>0UW z?-@Zb4yfC?&(n`n6I%y&ZR_SUb|UMyMlgBewemP2IvZ_MqDQ@h((7;2TR{&#Lu>^=eoUq_3G4l_52mB@{VO;j28wwh0S5WQn#t^gWW29vIg4z*7fo}jgIuK z#+Mp$a6}o)`fXYVg{|AS_42u{IxH4d1qJBTsd4MdoIghh{!-z4V{$evfGB_tgeQCHi7vvwX@3s*7{mKlBLK{_Qhy zm)k#>et(Rb@=Bw&r1#wXUctGAuWh!clHtEYOiLrEx;*dw-b79t5%m+VcUZQU`VG`U znXMt>HBm#k@d~}*-5|14!Wm7yuhEBb>ARl)os++_QFx>Eu4_Cyif662Qs91|z$!}I z53{iL;Tjb1d8ehC(gIxr^p&Qxyv{M8?~WdAnob%{Yr~wwMXPQXhL)8v&Y^V>F0`fJo3?c?uHMsj+JUR- z2bZ}|V zR~j$OAUcGrb@`0?l(xnP3&9<%UTdSVBfSPRZBwivm*3Qq)~tFDj=a>K)4ul3y%0w} zw6Defz1?lnk2(M*d+jManVYw}oepu;=xBfB_$4q;_oC z|D2ELQ_(A(i{EZ^H|)yMVu|Z@uRmcl z*3jF|in&gjXMY&!oq`o^cPkQpi?2P(vDg36Jnz)pvB=+>xrW>PqwgrJCH25H&pFr9 zbKzRjueI(-l%Sa~B0E^QO|IXRs%q`OMz< zjEy!v@s&B;D^Hl=V^R7Pw9U+kbce67p3WestA$p43+EF*@#TDA_R6#FPyN4tZa0sa zH>$96G{|8PLua#UaE z_jk9r%+Zy8PHaaE7^iucjBp0EMEvGnqCe0CkYGx(Z-!+*e8Hrm+v zi{J7Ve&Vrn;8;mMKRwvJJnx{r-OU1%p_;QR>iX&tPx*Z0#r?gmGkPE44z_(at`^rQ zoY8tzK;`{@+k7PrN87ITRth|*6j()x2X*GweisU0BWUEx#`IC2nWaR;{$WFj3QOA_ zQ$IfM1s{9mW7=!ubN?0p`!BdNVrQJ^E*y)`TwpQKC|!N$`pu9^-6?# zD zVJrm3RGqq6&L6w_Hh5;ZpB6Y^5fyuG{|sl8yV{6EHJ-e`;44&UN))I?FCriVr(`>c z3oR>%>o{T)~ji$96T&`mP@gxwNJswk$63P44i81{6GF@e?0Kj zN8-Wv+`B%1SC7av;^JE4I#pS*&^Gv;CH^*-u9=kus;_AVl%cGJpuXAbEZNz3~wj)zt=j7$QM=79GZzja9idF|`h{ z%jzt3nb!tB<4XFXQJ!%!<)WVWMvR<{IgN+lqP=e;wlOp!nknzm2tH*?jGUZ7Vtl>b z95{&0%G+b_7)YhK)SfHS>s#h&=Qls)|HX)kFN<-0{HK3<=VqPrPvtRU)R_22(l~RW z(9JlB8@Yq&`IJUZbzi)<)^jFmimdl51;!|_iV|aVT5H{x6!@<4&E;@*dj&_H1AWaE4B?wy(A#x9L@{uZW$xZa<5(nK|Pf} zOa^D=%H9)s1g6!WQ{giv1~*{#{9R(_X#n4d);{USu+B~awy4&2-YvU$p$$HO&s>b` zf+RjEQNS3(Xl7e*v7KASK*m{i3id=yJ7v~5TubTgv~l=ouhYmT0x_4o3|x$j_uEj8 zH5KSr8zRmz?!vK)rhPHCDQ6x;Bsa!Cb~r+8DrM;Fv}$-<{6+k(wj^1U5D5cLVV}8| zyH7US+Re4faED=`otlx|kEu=&_lQc_ck8v@&9r`m38>f96s*yFb5r0s%ViJOX#2I^ zN`aLE_nrc)C~@!SVC})t6nNXWd|t#Tx4Hj~&po&Ixn{TAV-=4^RvP-mhW4>&%V-XZ zCNEec;*5=PMgdsW>)@!{*7)G1rDlIC(#WnKg>9+Z)c1^Bmh`8_N1U+n9xPYEo%rT_ zAZVs(iMddY(7wjUHk#JHb~R6&^_c4W)P$?Y(4sEu-PicE;a-YG+u_M^_8OPG8eg=T zsF&F-J~s7NaKSn8w-g`S`7Zn%*EUU`nilO+eCaD;oO2g{fmUm6eWb61w%G5sKkYNt zIwCza<7vkXUTts%PKYs8yAcCC?W{|pP~xyN;VgyN0owW&z~}q{N6TsB?~QN%^*i4T z;`tAL@AnSB7h;!f+gH8<8;HeNvCvUJ;FF4;<=uk~=W~#KB)(*ukrX@c-Qr6BC+)mD z_%diFAc9oVIHc-7|uBy8GJW!Nozy8LvWUs zJI^9~DvDo)kO96Y&hS|ZXRW_d;9gT;6(#QV46OJY3e=zDrs&EeEm~?MuM{50@XG$W zulc2S1om`I(Qe>b69gOS`6kM3c5oiw1V=7oL#ylTa!mCld@TxmqKl9e=xBbf`yQCW zm%F>+7gBJ+S08OS)$Z7Qzq6nEZR25vmPqlMA)hF+4J|^dOE1$x@=0DmLSVf6P zd;agk(Th?t5^MidU3a>wPt2*{&A}n|I8`>ZUUL;sRCc!*B&3b}i9pVRA==Rz*6h`6 zgKHLs>uk{+_9FJQpt)4`4AY2|<^>xs$`W>PN~{bYl7f#mzJ=dDzyG_x@D7*UjOvr+ zL;}WD5oM{svKFw^Szf1M?_SEe@YeagdvX3-d{{Ls4IIQ8hBnw++U%ac&k}sWwim;j z+6N--*}v}x{>Ys__slb)q0fj{Z0y^9JEMixe@qj*@f%8f;2Rs`zxsV)p4rp$->7NN zMcKIJxBcinM-bmaH~6fx=U(`|LC0QXGiPd`af)!(PZ(R3eoW16i!)u&FN#rEoTXCW zp(7Egt()^|JT&y$#2kyjHu%S-xZaT?9{1Knf-44HL05)zL@PTry&%q6zn2n=wJqy# zX!kb6^+E@0jX_%v=Ak^Sb*`C1b%{-OczUN_A8NanuM~KcDX@wXkMiu_mt!YN&_3EJ zU_^B8PQu~yme;xQ`TTzDXEFP^Ukj(u`HsaENPeEeP%>KNuP(z zpGrgfx&8dr?xj=pd-=~k^Xgz{Mp&neiSE-K=O^?49>`;;BI?wLb~N#YCi?)~xng2O zoKZ1Xsg#MfgO%vvW4}ymJ;JWT7wpLad<+}Z1GlXjd8Lf2?%aAH(q;Vp7DoG=_YjPUfCEAd2?U3TK5&E92hZ-Ol?aKLiEeZELRzAtaM(H0w!W+(?+!je#AX3sv5&5-PgF98Q}Czq8BhrpES!Rrkrmcj zeMU!o!0NuZhb#O~rhpFQPo4E+r-flBJpAG8W-Kv$9YB4hir>85h^ZaPD-G6+l>#dT zb`)4ei5)4{n|n_I8_|w;qCq@|jcz-_PyEDB?AW=R&>q$JL|XU=HFv9!U~wiPCxea6v$WEZ0b5vTfYSuVyNb=ll~8sgM0t#kFWELi3(zR~%^ z@`k}zX*VJcBrXxMQe>eaZdvE7*QWIvBlgtg472!TI6|&v&C;b8`!;%R3ft^BXQRw_ zxczRCSqFAC;$(Q@TRB&v)+%p|RUVHuf<h~1iPAF%bSJi|D&p zT-mhW%sCu++4&fq|GEz&_eneS69gHqk#@({u_F=2vhaQUx%IZ9<3vDhVo;pA81yGIPnuP_OJ3T#4tuFn}c2lW{KG^I$@w~1l zFEzcfqf!?(_oMjoTn}318Uz>k6lJhfr^3e%A>C(_EzDFtQ3$|UqGhy_cD z=2YJZa#-9VMV1Ne_EI1V>W7A^dMjKgI%HF+Kua|cUe6-Cq zY yj$P@i*B=^ic|?ol?#Y+yAlE~#1LNq4-EbcJ$CW(Ck}162Rb?miB;r=xH@jn8 z#p~11iOT1|RpWi~;LWFN1X8abTn8SW54a2XRHCXwykS71sioJP$L#2j$11@gT?G2sq!wiY;XIS zCFbdO*nd~`d$)OV@d!RF3+;TmvL-zEuoojyyab88WPuXGL|S0>q6OvnXgw!d?y@7u zZJ`Bv>@ufr4&cLP9twhb3VhZ|^tbfqFaM=qG}~`%vAXT4&AlPgO8fn^Zn4BLb3%Kr z94p5^$hytQJ=7Crecu*PRo$tRuxq^FMBmEsg2V@9c15s>H1SV7@ws~}Be>??kHnn(G^WIFi27yx_M3xkMGiXQXcLo`{yaJo<)))=lpk z7FlwTC1oI%XDJAHdraWz3fy|TQedUPeL#U#l(-KjVeJA6a3jPP(5juABD6SI51(J! z)Hws1slG6d67Ug=lB~v5UHPE_FZkfZ*3H#z%V^Vstz7bHi$Bem+HK{&y*qL$a}BFX^n@d zRNnR8_qjHnc=E~EDK!D3I$eC z;tEODjC(@?E|P44eXeh!2R>)CaN$~c0|+A?{r2%ymGozSG5-_%OBJ#rLJ z+pTd5yJtr@raNyBA0v|#qs;H<*$e!Oz|RZ);1Zzp4PX6=pl2&WSvw*`X|vR5hv;`v zb_UHgG2Bi~?w+8JXimmy_e_`qG&HX4->AW4OV_FA*l{UylfSW?FXK8juIvN)nM3LY zJF?VSxxTW_nywUBDR7tqt0-}pMr)0;Q$QfsVAal!X@kIVP*@o@&({rTc&N+u4v5dB zPydL<_YmBvax60qEIaD>^cNN@T^Og@TqmChk=jWtxiOCJNL&Z6wtxGZzd7)QtL7lE zLZ8QaN7dae0Xx|>W2xQuz$NQiM=R>ptMeGPS!?Z_J1!2buuQ#OChRp)Nu7}$2{;8j z1z+!RuFQ^0%S!Wc5Vc7;x8}P4bN3-~l>W7@`oyu++}pWpVQ@(|uJ8N%e`s$v zqpepzix!psI8HClG9p*(_!M|xK2`iF`1sgGz{Y!yahKhn)_5;r^wNlaRp2o?%Alph zgq#yc490F+p&7PYcmVia`m;1NL+s3ywy|qgI%LlE8$9CA|Ign2$KJAC2VuWZMDs%; z4K2t72@(}T3z1q1RHfv7A5sxYf2566P$?ipRf<{(i7MicRz=m5{Z(qcASZTAlxHHa z-C$zsI*tiWNNvXo#xlV+R&dhPd5O}HT5e*jHUgB~g4O+5=d5$rci)+F_M9{4`|iDO zzS4YW&Yr#3+I#IiGwb&zI{kk?|MQ13<<{Xy(%z}@u|9xb4`tSCzdl%2`m$-5^QD>i zIcem)k0Y3T*5)CN-SJIf0jbAW%dOT%fsF!7DX@tWODVS1zXAoYQa=4x0R0$9Y!1J8 z$rU+hI3)4w9WWkicxD0LX$~WtKo@+Ew z-n4U(aY{SdQC6cA8ESPb@85j=>+i4wnG?R&J9VA=0(reI+Cl5L&3 zKUKy7Cw%W)HXp30Iw=MbOO9nz$=#cSb({h|QO2(x9qoUTw}}!m`!t$9m#k{u%QH8F?P92W&KUH1>#SonpO`)KnR^dv3p% z*q;L{+r~${n;TCpkG&2WEjZC(5%rNcf;A;wNL6w8tIKK{|KW3`#=(?rt;A)@MI+jcIKiY7ke?c1P#jHk8Xpfj}- zci*P|Hb)7+p2aEk)-uloF+)9;Cr)D(*2s0m_r<^bOE*H_)A4K<_Wj*Aob+r@m-9sw z?cXmWxYTL%xv}M*xg&}x^G=lPwvJc7?7%H%C+n%Z)*m&Qc$duB1W%1eSqskwXQRL? zMS)F}c%>-6;Vl%vBG5WI=GcK!{o>1TYHFWEpA!jJ`%un{5p)2IDcLkQ`8#)2R{v>l zjBq<3F;qC0nrB4xq8Z^Tz@EzLEyJ>pjO_psq9>Ip|? zIr)*7|9E5{%CQku?*O0pku0Y+@ht7@zb`?&$(u4 zFfYc5wt=fyD*9NPb8z%+JmVSLVLWhq?!-c!QW^BHMt_?#c=KrMbXc?-#j(}fDDY~h zz$Qw(+E>6UK7PKy5%$&+Rc09894og*XNfH|(GpVHvdSlI#5X)~W6{Mr^bREM`_zBw zKl(GXtV1TqoN{V@qw+m zgXQjzWVNQkU+>o3{fZ=@4PROZvQCN#v7KBNPMm`+Z0$#^1hzlKKG8~6@zI(HcDBJe z)$dU{=xXHuZ!od7wd=vAwQ}@4<)9MISo^va#?&<@8FT#-QDy%}jJ4i!t(;;@g}<+7NmBQGhg&G>C?`~!e+f!Xw>05 zHP3J-Y-}hxa%xh>a>lB{(qr8aqx!!39s5b_`!*6EmH3Dx$#N@8hde0|^xay|#Gh~z zY0O1hAKf2Q$vp1lBsL_+ld$KUq0ocloOj~X`1`V4FS$M{u3FbPeG6D-Jg=@}|ByjE z35!KcDhKP+dCo$X^;yR=q&>^F^G1P<0;egki4v!2wAHvO1#AcAvniX+wc{l?w$NPq zJjc<(6@?0ibIlb-`I~QF_j&3Q}_F_mw)`nZ)^`H+YxrW z;_mDAzd6xx0K*>i_eOq)k9u?Df-~5@6OCx&qkm2FdyZC8$aan|LQ_xT`|1NddVGyt zYzZ%qJEIWi;3I-YV%QV-vTx3`^;sRu!UBLr%DoZtUY?0PL>_HNV?ioDi9Qn+wmI)5 zTblRsEW9@BjE1>peP`AO^KU$?0eLUB*h?h)v$;x3#T&olHABu+nkG!UJF{+Z4AVZ# zooU!BJ4g(`lDaoa$LwyxO|e75>Wq)bd(Qe`?R?;ee>irR&wH$032Wq>?chVpcfReb zqTj5G?H#b&3#Z|;{)p^Z-&t7roTJN8Ww zHPN#e$UuUXcb1IO=l@zbh1zDq%glVjfnk(YJEvbVp>Wbke9rItz;ky#pU2?pGq%D3 z*0H+vUhmso@6BCf&1zN{+H-75@25T0K3Ly2wjAeFoxUu>R%@HK4rrBy=lU#TC-UgS zOqb%*KC=KF`q=PgLR&11@?49reZw2>(0VDpUlKlix2ZdG>7uirdbpYeXqEp-=$vP z2fv9JryK(VXMsaxQKp}D9qhuQf96KQ)WlW$fQ22x)nHH0`EG5XA-wf{L$wsvympUO zM4h^ZVhl(AdB@faZ?)g9Kk$){9B7^B9R2%C^odsMbN!N}s)7SiMKyX%*J! zwU{eTx(*`f5*m%-*y?Q*I7NX?lsHA9?f%LXc-XXALfl^d)E^2BP8!cg9dMR*$7ex^ zq)&%TM!9{I%q&$?ycF(eeVTsagU?Ez7<3fI(t43J@}BU~o@SWoeT*+0H`>@}OIfs* z(q?J>#21-+mNuTLFKbvhzf9cE@bgoST@y_wi>!a)yw>|v=ytOFBUYVB1YKdUgKd7$ zt4|Lg9z&dlXP*|oYkJnt#5d(2l?j-agj%2KL}slb0*ilPdDQ0iwXeOQ^~V0LmBIll z8zNF$>nwOQ&(_6v|Fz#1>z{LAjdwZK_x^sLs5Y#^ZSEq*XJ45UnOFufDVJ)!iLFTJBA zvlWxYIc^lj(t1)Gu(Y5fC%S9I)6_mS@iHumQlogj>b>py$)dm}N<3Nf@eK8bhKB>n zrJtWuB=NcR%1JFQKij3+q*FNRecIgAIyuWWQZ=Nt?i$y(oD3QsS#dZGab8*j?i37c z3AT%9K-)F?sh|lHwmFyOX$qH2KQi(3aRgI!Ynjic3^O?g=)yUv)|t9aixlns)O}?{ zp+0+4YV#AHv(NV&j4T)8t?u9d(1#}L5^b@b5fhS)+F8aL?PUK+oHhArse0$qI^@GY z^}HDq_7FUw*%{}fJVI~bYx5lrHSKx?-!2dTH%7bK5peEg$Y~nobo2-|r?HM{5kYT# zB4$?@lh>_|x=By(7jtS2DJQO{1(36k^#vdHs>b1Jt^$s@DbWgX|-fg62UNYLHkS{Zz6>|2vKJr_9!XR$50S4ozS?L(3fkDsYR z#Dyj7{%kKDb&f+>b{FkgU0(W2zwvnvu+9!^pm)4n2KjXDQ}H6FA#61^3Or7MO_X?? zCR@ox6rgQn^q$*#>=+e6jE`C=Zu!2p&nrvE2uoS<`y~FufB0YSSeXW+-kZ9{ zrbY70rX{q+-Bg`pWz7lX5Z*hY-Nq* z^)P+ohPL0=!k3O9i7)mOS%6PwC9F3mjfA1!bASE#u=U1< z{dLA}&b3t@doQ$lH1EgiJ-WAZbU}Ms;0(!)kmd#lyMN5mxD;O|nnlNO5^OkF2fX9r z`E9>nY9a2N;~w>KcZL1TWU`}sb$A|fZ8#JE(vn2WooiJFy58~;@dmApcB9row8}II zM9j<_m19A#%z{M(BXVH7(Ixw0u1D5N!j$e{Ps7QzAxxjMQ>8;*Q*n$ucZqBC%o@Wg zCxt^E91%y}DWf>HdK(3vWeRMf#IroRugKW=C@D-RWVuJNycs-W6;<_)w&h-)OE0yOP>Nhz!A zv-XoOe_b0acbUy7Lx_P1^~9bYy|`S^94^gqW>SKWwS%SMVOj_dH6J`k)*9=b4m2^= z1^9rohk~`@IHnp&eEKCxF%~<^SPRzn%}>Wr_-yU@wzq!eVXXw~Uudz$C8w&KW8zAR zr^;fTu7RY$AdY4#fW+&}0{V@w|DrqZfzs@v%liI<3w~nOML>@<_MT&9&Es0>Mvio0 zyY-AxU=t;t(TRQKhK1#conr5)3&El^L->7&5{3ky*Zv3J|Fwgf)XNCL3UZ(L;!}2y z9BdPn`_PydO>tPI(nb+(u%~`o1852FXyV!_I9u69fsF#w6xc+GX}WBsiz&d!ZR@oW z?df9R6AjXN14}n?d-bAlY-;RBY>Blx^eKQZM4tHjvhbvHOs|Qb>e*Bowr+}To2siU z_i;EE-&^1D)z8y7+deE=<~dx-bG=MC(^$LUIOd!hZ0RP{8w)kU$|SplMeZ|ph4|3m zfF-y7T))Mhs57Fm$8rhBRiAZ(<~WaJP2GFn-}=3?_#z8atE1jrpK7^hLFSmKpY6Ky zF?`Y?Buy*aW7jmRJx(zalHXcW9N6Ym8Js7v+N0ykek2=AEu9DXyNAe;4ff{@ zD_)AP19SMvij#AR1^=`E?|*x60OXL9w3KZ1(CESoR+xNN#qqRp22P>#IqyxRo)r_% zK`-_##Uxg=<}e#!=J2sVT_c_#99FxvE3|mZdF`Ar^=+sVLNxoUzcuK{8E}cCSPyUX zw|AF!T<;(^+J39IQDBM!n+U}B>)oUprf_D}3o-R7+V61EH9B{3cq9`;|xoN#)m zH`+B#G$%LsM#65%1gsp%ZuTshY(W{eyNl2G(1Dffx^?&&yG^&gO@)`g z`~B~~!3M`VXl#t+vDuIoKSO`rr(w@~$Ii#_-C1`HAKV;cgznlK0UihEl@al5*Nl=Cq8U?j-wYzCt5ErD)=~}B^@Oey$Wl) z$Hys^h+=G>xobHQS!+qZplcI>h$_noHP(Xok@#4l#IRU1W389h^6bKQ+hPXOv2Hn( z@fJiY@4*pu$~e@m=F?7rO_X@rXYlI%XMC>OuWKTtDdG6RG2$BGbyIz}B^!qTp^Q`a z*DjMKNyuh6F0BoG0v@YIIDv7@I`K#2=+k$}XPxF3z7er3oKafc+yX4S=QD*xOATn2 zPn~mz=LlJys!+-zKmG(hW7g+W4n(w}Ldw;(OM3x5dCBoc-G0YsBGCvwVesm4RvFR6 zs_Uppa?#S3Zm00EC}kYQ&^`z;;@y^G72nT)_OsDv_{{GlVOrr&{pgP#>QPn9)vM)L zcW^V#0bW_nlsV=^1(poH_UhPie$;x#(xDA!DZ7&A+$TO(pFUfh_^l9kT07$AG3T8+ zHrGO|f9&I5e;5xuEFQ9|MYg2&J5vLw%Mu~ognHg7y%SUqf9ki`9OCDv{`dbZ#JbeN zv$%lEL5%}bZkMQx$a@a1Dg0SxoyuZFyoAF+3=n%Fvr*Pt`^+-X-pOIXVc=O^CtNh9&MXjz zM&*L2a2y0Tzd#l5$Q18uO@9>4iqg1N+BI{DQmzNQK z-Ehg+HmVxQj7l`M$alHl2-@Xuu;zp}_>VKREP7($)Pv z))!-PBrchvYCm%9b7xjOPD>RRQHI#WB4NxSUQhG~9h;~;IB&Zi=$nJMBK?4UKYFo_ zHB1OJCp}`KgSy~ve8+1R;hG!sR=!bSqrmzU*hGo-Y5EL7eB0aKZd;^#d4>P-KmDg+ z$j6GxCUb2v<~D9VqjSF1YxaSLGe6nK{NvBT$)hP#lQQz~fuO|)VXrA&* zh(tq`TORd3>X*QsYirxA^l5dTtG8x3TEZ1M>emE311s@O#Td*qd4S~$_IIwrk@BVZ zh(O_>(rD7Nwuy!#jNNdjLOYk@%N=O3FjM&I{oX28vl@-k6DwxEv9CFvdd~Y%$98As zFV`n$zhuwL?Z+uY3QJaG)Q9{2x-{#Oc zkNSHluEZSb~o~ zezIP1y+_K?hh-&j9l;lwjrQzggemu(?tb4k))P$~*jJ7xVk=}2%5q`IU9-baG#IIu z4I%|Y(yX2L&MuF})yiJ{mM@#c$GIg$t9|>C>{P66-~G;i{SN#C4NE_!-c`AMUDnS8 zt~r*uvsj&)r6}9iGiRXc`e3DG*Y&`W&S z@VW8Km9x-s!pcK`BZg@ovyia+v&?y{FmrI1mdki%d_5k&;i6Jl3iDKb8OZuMW(7T! zEcq1NWA|B`SX%PvV{MML-R^A^cv2~_i4srh%)Fxg;=k&+uh*OmLlVh;@jw4(p~=Cj zZ~(a$5rp?K>eGk|Da1;pk$n^=jT5Jbx#Xceoa#{J^LUK4>)Y}Z*5{x5$v<|;J|a~3 z7k=*N+I|YRjScvJ?l_NVPeU3w_N>;NKYowP9txqgZtaM58TwpVX~{UJwE7aJ-eCoL z;N5RDtgKV@;jj3x7_{bH^9M`uRTh}#t7;8L@R{p7iAehnb-y|7B|U=0EcYuu@fTB? zS8+<|2On@s`>D&^U0L0J`>BeL^>3@kAX? zC?oG0b(5aGm8y=fio955&Naf9v+XF$J&I3=GAAmQ1lEF#NfGMJN$%L58igzm^10K` zIzEo3F=zW{xmsv)OwM+SU-BLcmU#~|@hJ<-O)DoN3T5{@o$fPiW<^7vu$na`o;8+Vc5ogJmdi-Ir+K*8dG%6GLI8#=K(7xTdzo~$1cS&Vn zeG`K|@R5()q|@fPaF4a4HW1z~#^u^pm)eiy6KdIZ{pzH^CQ7_I=l#T}Y2+LupZ61> zPTuyK%}+KhTh;QNY}i%+W2$e9&3}?e~dNa zG>%o?-=n3|&tsW)=D=7l$r6O0`CP?)ISqYoy%zcTPt@;kSA2oDuh9K&tZT6KtY5O$ z)Z4Vd*vra-*88rP;){%6ZNBU7&IvPOF!pO9%lh+De6*?NS_Y*Gi})n|$h}zi^=7$- zFYENN2Bq5JGGMhagg^bE)RuKWb65wlgLt`DFLSFmF${A_g=_u9*Kg^Udv90+A?DRa zpnyDj$zr0N-Mqy`gsP)Cfe~xNGT2&EVMxRrj&b|-6S$g_RqWJ! z6vN${M3uUGyK#B`V;$VB8auCx6EaR#xpVi{@%df8nV*eVI?Kuu&F}+=i>KGxxjV8;NzH5lro8vP%^v&HO^@B~@*8@NHH`w2wbIze<4qWP;_=|XxJv{rCRA(((<^!BoXUg#k z_$Byq3qElBF!bDQIdM*D$!UI<&@;Eg>+#Vh*M1W#r!04My+prG(6egdZ-s2PKU(bG z+Bdf)Ue!*AW_kfW;tFdWwmWHQyV0ndZy|2?Br2# zaUP@XKQ+8cj|$s08rJt!e?NcIx=&drr*Mnasm@;sL&ms=FFbgTvs7l;%6s(;*HX&J*s-lJIH<@-ZMY&y}vV3KDg%SIrYw&8rQjHED6`Nyr0{zt=&d}b11Nh66a9u znQuc=MEjK6BwcgB@p)*UFZdi&TB>9CwfC7nR;S*>#<4`u)?x$rAuq`iqRp{Hpezu< zM(&aNT=-P_N$YQnk=>?tYUcWbO$P8eo%0hG%4j>aIW4kqUHJNa8>Z0FO3Q*HTs9nm zCMscxz?;Lzy5oMZnG@}6QEZ#n>K@xa1gkiLkAqBwJ#{TSmWXp~e+Jo)j^QJY5JN}C z2TnMJOm(J}p_Tp|!>1`0X9OQs`xrh6e&KS2CV!@1qxepZuj1pFH-*l#@xk|h?a=sS zBeRdg9Q$Q$dY1K{C^MDK2ph?1XM~=zG8tn&yS4w`7pxP+Bw~YlSkhX@^*PT2r^Oyl zu1glD#5ZMW#4_v4i0I;ZCsLtReSfmNc<*yO^lqhA0+Uw1?^kRI+1^i8Oz!K{-_k5_ z)Pj?G!@=@EbV?ZBoqlIlwQNr;K=a5-mxTl8H@xd>Ziv#>M$Bi!A3M>PwnUiTqLt!) zWOXZbM=x2|rE!>AKjw}VDaTCAVx7BILULAE@Wt79KJvSF&6p4tEoocO9PJ~e#VGN% zdQTk%Hc{fKo3d+nSSFaAU-t`lG`H8nTQzD}!l_s9nz*qDsqON~m4I+f_&qE;8mERY z^7i)?7XCIr$JlS+I1l+W(lYy$E<|yt4wujR2y4Pm8(kyE{{h1I1r{;1^9@Gglekq3 z!IV$4&!DU<$DC-6VGnI;!==x=3_lMZL_9|Oz01%4!X3+)%vNZ6F22ax)nH?zu$cPB z;*x8z-+LkU_GtAl#fJ?|I$-5a;hVF_B#fyuxT!_2e(i~mKYWc#!P;ah0#?Z>n8(V3 z^=-7tovpKOCi5jyWk|5Klh3@J%BNBbWc#^u;uG#%>tx727R|fMI{Nv~eeOWxU`K0% z!Rrh$uPiTXs`YOEux8G=D?2Nccfy>QbqX!@4c0c-py!Td5*}K5=^fC}vBo=$8iC_d zJ_oU8+IXmVPWAPi`^JD?$vPXG-@o*QFDzbrBlvT@NLEZ#Hcwdja9SJzu&_?zlMTzb zY+IPIFAG5pRi}<&RU`^>M6*ZCP{Xa#GfRO@lz3*R_S}KdoMY+o5UO@AtdVv!-1&)& zgndnuG1BhbI{boDe1XqvKEij;1vt2hXF^03$iFT|l@Bfh>X=Ui)hS7Gl|1uU+fcLxW>w59sq54x*4KVG z6P;>!!F`(Uif@E9ya3$;4jw{(6X&(@WnVKR(#MTnGlppIiCl7Vt9Nm4liUWyHe9{AW3wHR}_`>0=;Zlc~)x)0po#Fsi&^WVB6(y!bN~~eBmFSS|PMzV?KS%8I z(HcZ3bkoLWxpUoel9Nt~xbB7Z>=dH?ecPI8V@*e_O)KYYsEi7N0|&ZdO<9rP!UnsK zCdRmg-S)oKHnV-jwtjndILmsqg!&GSKckM5hvDyG=UT2Rh=hf0l#U?rICGFWhh_Cm zK^L(c=(5`a@`cc%r-dT>W)7{BI+uBG_}p+&F^JWP^d7D6tY@R|ieWtaibN#z5r@K3 z1f`C!{~s(A)%OG-qNT`Ql7b6{<4o-GP&qQtW`pBIisxHBVtJ+Aq> zpaJ%IsmHZm^GIVXoKz~lH4OI0$lkO@&xJO1KTe-g{;J2?xW}(s!bKL7>Kt;ao%@_d z>KJ>!<0%-ztZC4+8)YRE4wf}D!f0RF%B<6$t+DK5WIHMq_+a}iJErD`Z?SAJ+1|M{ zpS%bAnJBgk`CFC6;!SKj^ZmH_oX3MaP;C$G`2vbbo070vr27M{AM?W^?3XiR-R5{(oTfS5qps<|)%NZnj%MFj z1`;;0R$uaWo|nWDa4gDp#0aSq5|^_qYI(mudvDr;FVzjT-P|=XNP=nalU`KOCPV*w zTz*f9z;k$(l*hJ%`!xd($0zUSkx=<1c(Sfo7tg!H+BnCdFkx-yjRJEN*hGmr%DgJe zX@El$cIDAyJ$g8qNbf&G9E6Aje6`bm8%`|yDSU7G z_FsRP3vDv~1B(m4G7dXEyOD5xA|sqU$D221&XM3Y>$bDxsbCXJl1|QZ;j`jKu)v49 zGkCJN?&)D1965-UBly6u=}0lu2S>2=@qO)OZk-+{ehvLDXj2j6x4@BF?0@J^kjF<;JtX*xa}4F2Bcz))I`3P{tQB3C9{ZVVKHHsh7T3h;_89Pq0*+d_zA7%_1;@ci zxl!h^)^Y859~D~?JqN!C!QaCro|s4XBUY+8ZbiIAhZh@s?z>ui0_QYwGGRr_&g{=b z6Yn=kcv+WjU;Emd`tIwh5C2R{&v1jS;zog|m;#$9@f1(qx?O+R{Kfe@Gjcm5WyRuS z57x!BgNF5`$@Ix`95h4iGyIc3{nJ;$NmQck_nN1b!y>@TdElDXm!x0a>tTeQ$#&ES{mlsmg>*17HV;geiHa|GFHU^U99BF7tU7)ZL6^iS2?yrHq3zq>@)ZYqrTC&8%a~Rs z>jK-WoTXr?vkVq9Em@VG*$3|}tS7M#j(Ut_6|3}eU9(=r_J*wt>>NX`N$)VpjEShk z0jUnoVSg(=Ynm9%naeoLQLo-101Kj{T^%U4d ziKl)F*BSt=vriT_Dp`!RU{Ar3rG)m*vENns4l6R%=8`f#p0vAI3ghI2Q=KkGU=u;W z={48H66I3~C=aYa8)s_ZMsTSUe*3MjSlDc{o12{mdJbM>Kx*yAwAnkz#>UtlbAo-@ z{~1@XGDrH)0Ykeb_`;c?wdu3sBtBN7xpjCBzFcqpF3{p~jL%kFIp+R+4nCFvpZ0!D zq&RXcp>U)+STKLqF~9wOJ+o?A@8-3MVzZxop}Xd9blWjv9mlXp23m-f;obHd2K`Y3yBt7jkA0kPEa}9i+aaKBXV|}}Ads)!fOW-N4CbI!e^))jFEg2s; z#1D8J=yPzyYHML?w|8y)g(_wtJ+&i#b1q~ZfGeLj+jtUDX2tW?pQZB$U;ZVY{GIDB z9FDSZe~x!;!$-A6gz9de^%H*TGIXoY1xHfWN+M_`YCQ|VL?g6BQz6pUvRD-JjgB1f zCYoGaB!gIf@8EGr06b!)J{Nh=ZNEMO*LQyB;ahSp%&py%Pk~L8c=G3Hr5-Z$hUTdm z;%7e*E8(gM7_n(fSj=;}5U~Ue4_q7(nls5&Utf`tV~?-;xzkX3O*L-NOU3eb!^%u%wA6&lMl- za)xRsPDvW6Vm&f?kK z2$8yje`vceKQ=zw5MrkeZBF1b$JIFs`yiM2Y*Q_bY@AJ9uBH`O|Zrc;h{3 zBW!(xJ?I@Qt}$Mk$OtdJ0G|-oQGaHdNMqwf;)eGV>ohtKUyC5}Qv>#gBpndq%p3D#O*ku%4^;^vKV_Uw9| zth-V?ecA8@J2`##0YCJ_u0z7-mZdH0Ih$YSrOz++l~uAAq5Hmx!@9!?xA{fi-2bAlzs7HBsn@;uCGi|d^4p70 zYv9;mY!rCbD6okV&)SUM84qWGrscSy`M7iAN;r3r@rgyK4ij)GcVLld zhmZAr^xnf?{KZ2e%}hj!hT+dYfRB}EN#afZ%W16=3lu*0Z9E6@$aQ1MLZdt}2(-p7 zPlc#XRF5KTY{X3L$AgBy@R>gy8c(p`wqLv2RoN=gNp6aC-I~sin?D&pW^I z|N5~x%%q?(&vU>RlncjCmU_w}a%WvFfP1WJ#CSED5jpz(5w3xchB?2gPH`0V zn6q$Qife1XQQ(>s*hGnIQuh^yjRoN-5udv=dgJrX-*L2QX4E-1zcnLvAAWiacl52X zDNEW^c<}igkFu%oVYPa{`ld0ydEM*3D6+Uz`2BSn>DBM=P5p*Xu6ery=lzGxaVm1m zCwrJTCeBOnVF!gHMwN0DtoF6><(f^r_kmAB()2lB6Cdjpkt8*@92m^MdBNhko?@@jpTD|y zQ_!PM$dPMS4fB*SAygNBV>^STZr65Eo&pipkamB`yCT=3~7q(PueVTjxlfc5lSaBVz%cp zL8GNPl%XoioV^2tx7WF-_fK83Tu-gTT|(U#9$Pc9KhAz#or1mOK1;9hvE&fJwIw!~ zWAPFrPK8VGshbXt=2FsabFB~JQz`xiYMW!%oMX~wUvka9Za=n5 zw}DDcw($waulP7J)kL@I^cIv_Kd)~a3y4LT>YF_oz;W1SD%)_N%W(${_jP1V$vSiA z#xev;!ilNY!7g%;!mc>-9-rmd5^Z0e_xb);YfaYg&l%1sBU8Os<6LTJd1#UMrB(+V_W6AYyT+JW4 z;oBWcTAx>UH?I4`mt)(F^}IeWYqcBO8u*e{bFg;h>(H{Uy*YfR`txAHU+w!GzMQ*V z-^PHeJN@h=onDw^r)q!3FvT7chv&7nskAhP9qd};!Gm*Xxr6ig`btKNB4HL%yy5%y1QoT{iS2u;e0fA71k8~ zyEJ?u*zz5WCI27wzrw3)V2Ar|ym)PL}hR|vh4ixqrOk!Iv?(I%k2Ao z;F|8iR=QE(!Gledc&b(bZQ*Zj;eXfeG%YiS9ft4wux`XQR(q}3Tqx+8W2W)Tobwz_ z?t`Z3*g_MR4k4F}iI(rSefsR>P@NJIrUZ*Q#?Tb!jU#7y<2j`C{@0pBV$1y;iDQ@o z6S@9dod|=Ss?e2LW9ShXs=9kMlhj-&an9oiK3e#6z@x$M&^?I#6!znr!zadp)u3vs zdpP-9=AG2OPGw1{`1pZmyLyg{R{K8H@~P`3_-N8+V4Ms&&vy2Wm>VC>Y&svQGlws& zbR+bUeEk}>9U;qv{*0`b#3x=U$Lt(2G&v{xGkj^HXNjf7faGuY!eOiQ$5xCZ@3XXSp`pn=_^G=q&5caE)f8?6>#ZoYie-QTY8Q=W1&Hyn|z_!j`laon(Dt#bP0ws=rnli@NX0656bVdxNu4 zAStkk5=n`t;w-ZUfAstCJ@k4C2Z$E-_Qk*aOOvd}nnWW)WT@L4zvDHB&*_>tDlDI? z#OGY^UE`Y1>V4vi#Gd=G#rqiFebaL(K3Q(Ce2!pq7fy2SOM#Xx?qFcOH0@+z@xph0 z0nX~*{NlHK*=c+YSFSHvfHDl3lZPa@*s*E}^I_Jbq^#&PXF{7f~SFOr62W zWV=)MmD#~sjK%bT?w0m+t1!`v$fN}tOI%v-yOw!r$r|&H?P2&$l=*z;FopHDo+N~J z2*a+)lJAaW`WID%IDy2;LC!Ngr3RUXu)LI$k;L7Oxh!hSp>8cU3TzbUDX@tWJw=|_ z-*TL%Mha_jZEFFIO1}1;`|YUKCe*WzPdH?yr#%gp(2%eBODz$iZHG>^^9jXTRD4T5 z{qoAwl4_4_ZpQPYnz{%{ZFLi$ZReK6pYXxXw$72){m3A}ul0vm?L9nnh#heE{S`-0 zbJ+K2iMW|%DseVZ{^T4a9N~@G&N-|R`s*NIUKedpjZcJGx6zrPMk*C`?`xKHfrapZ4f zTH9jay=%L_#F1z3t7=JGFocs;D^8-TaP}qd-jfKn3L6CmD6okV14Mc6+^UmK;9D{kb}QIWa&R2g^;~f7sYY zP9s12fB!eLuo7Qnpvi_O&Y^v8xNHk$>#M9z6Px|Wvi_`3L^PUq^V#2@`knZCrz+cF z!RPZo)h^3qNaCp0Yl`!Je`b*Yw>s4%orn{O=ERJcf@*z!^xLT}G|199o-UnP_cf^x=4-;Gk^?qzgxW~5} z|kn`3aNaE8@ z2C7`!eoN?!?e6ix<-S8)!4hjV@gdtzo$X0t%q%h3QOP~)8X4K~z4jLyJ%uOBz$OYi zDjxUW`9J@wi@!lVKI0`Ogz9Kdv&3D`M8p)0YyVGOhuFM_D|=Zw6Uyi$jw0VZa9M}4 zgn33r*8UW3RuyqSlD6mbSL<81x z#`uI;VkwetH9!9~@Pz|kS@YG|Vr_g_;I_ruVQj`&#X+KKj@$L6{^XIUxr~h_HenJA zD~q!1&sSn< zRxW{gtgW3sIh*9CiU#ah{Y&pPcHjm}k{TqLN-l*t(jGoqve0agG`O%%j_;>F@rkR_ zGrO7ima?MRe(5>!#X4Qu_>B6Ak2A!n=2#!>Gk8(lA%O$kdz z+eri2_c2uw{dQyO(N@X^#IbE^`?DUF@L9M#NBzD0o!gtin`L?C2x8tMP9qDCoYCQ! zqZ{WhtoxMp6n(=Y)bNQ9IfCYJi)sTrEc>CbT%^UUcW`0>%Ka=jMUB3za|<@2PH$BZ zJCjai9Mr&9KX$yPEW@Z%?rIYLBeB7=1x+ZDL^9mq4W^R}t8 z`hHF#+81K8!|AcG_6ghE$Rb4)rK;eRguag0@qW;eu*Yvri;dwFY@PXjx?Zx2dk=H_ zy-{GJz%&ImQDT}dPf#h1qE81O=GVUA4U?a?wYp1;U`XdH%ZHsdig9)=_&pq1SgHen z5YD=-RbPMUZ{fmxMpIMsusJ7H7}!H&oMY!|-H~gWdY{NX#vt4Jq>4Qm5hrNRz+#M_ zVo6x4!cnHN5=61Y(VvZPL|mfbX3h2`@wvMkbM~J?C|J#}l?R$S? z{~N0k;j}049UQV|;AAm;$NH^avcK5zY}cPXt6s(|Ge{=u)u%+G1{VXPSp?k3L zSy;{-$8zW3WAq$dFPw>320VvT4tec=rgsYa;7aEBAi!{bBXl#bCVkPGZwYCDH2j+Z5Km*K1Bg z=t&IKmV;P?`J-bKU()$5lQ!GlF+~=k-o8udIVCDrJoTEm3n#jYrHN;^U02bIsGrGu ziF3XSVfAYDmWJxX9U`_dHQMMeajX|F+ zgZMV*$axCic7LNlPk~L8=qd8V{f?xb?Uj_T{A+G_KV_uF&P{1m*CG^Of1fI^+{U8poP&?{ zHN(my<5a^XUMI!eI|FM!s1EeUu_WA7D5-Y^)(Ae%99UvyG4>u&`)$7h_Z_{aeWLoI zh!arEPwQjSGpvgHeoxT}%b#%z#!3J9ZtGmr&?uv& z3`;bmH0^!|eJ_CDH^s&YSxhZH_#6nr$zjiy^ljGo%~?&7#TZZG$@3ZrW?+PPk$Cbv zQ+KMEBY`-C2=$+;(3MrKzID`FYr5)peqwoZ&L#D{b93egc1>t>wwuZXGu0HUK9z|| zXelQ*&jDwgUi*s8s$tTR8EgtcS7z?fBM**T5BJG=v&A3u9hSCvfMe> ztanNKM(kovnnNWg&H8RjxO3-lz-)cgPhya4gpNC6Zo3D+qpmBs!nth@_(ZulB&xTh zo3ruM-+h_zd~?o8XaeNPBHv21%nWw$d9J)CiAkP^JcNWtywFLgrz^0$STBxi^9iSW zv(M4T(`VH2d7^=<4hcW-y}xsar91=Op$Yaeog;{$bK$Co z2Ia#39z@vD-22(IX5p%S&v}39%;6_OecSv;)}m60W7Dy%w_=QYCp@95rRnUYEL1u7 z#EGmZegb>-lJo{g{!*(RFpkvATj|WfS)=ZmL7V{tD|3v6S%OcJVr;MXhUY8$;7p^O zM#h({;M97yeiC1@;T4zb*ww)Na| z7|UmH^m&-VC+Rm!ZQ{x^4Y)974nXE0%bmp_WM7V-ch;z+r{~anIAO62gw-tPr#lk^ zB#IqJ@vToNBw>-zf03Q(zM%p2!*WO2t|+)H-PKeg2MxaGpC5pL&iQX`prX%5@Hw z;9S5n?TiO(nMhbDWL3eAC<|6*rro*4jL)=ORbwZ+3VfmcIv4OvJI({W!$FSu8u+HV z5hBw5Gs*%pRcFmIV@6L$Bw@S|IOmtQy!0Dpo!$8VW3kr2Ct+x0$Z35K-}ITjhxKQL z@rCo&o>rkboxz9Y7t2GlnK2eKRqqsslVT}}3|{-S5Ps_AUcL0r$;7E64jSngrhj!y zuijCo;;7e^4kP&L8mo?96<;Jrtw_ps;v03MoWjR}3!9j0-FYq*hS1g}>$Bp^q@>vS zip%wsRg)z~)~TiVQfv?w9tp?y$xMl)EFK}oG%JK`V*2MG0!FC)eofuBY)*yDvzKV+ z{g>lOQH!N09d|P!bJlT=koEodi1;z;s792wTX%)E6D2--VC{t=%YIht#B~!DIjJpi zutFQ-t2p2OmallMzsAaOnDxcG(jhg-Ea7HumHs8({-Tv1VQV5>cF%V8^H=dTc6t(@v;-OM z9oq1_-FWPmcT0|;?lmBTOw~GvCjYV44<6L^0cWC4EsAVG`|;HbSy=b%hw~i1x~1I+ zzSsr2IV-A*9>h*#tJ}M|$Dt8>SNrOEDL!rby)dlFPz&tG9sO?C%6kW@iZ9q_08jj} zM9Y{|=PPkkXCZ5uV=9(7L2|ND?jVk5`N$c)(y|2~u_eS58O_RAC238^wc4L!*}UfP z8*5l3npGml!BK}+zdozPT)2YOZ(|b|HkH|K4$rA_VnaOh`*9}jJYORfh%j_cbHeqB z!PbZ8iM8iGwvwxs%Tx5(?r#)0MuAO~I7Xe-@6kwbKJa>y#U!;ct0US2jIE*euSO0X z`KrDazREU@=8V6jFBRV>KJo__eKya*8ydkqJ(q@!o`WxQ##=Av;^UaZ*LY;D96p(ND%%zzpxuNSUpO}H=Rfdv zb^4!T1+ssYt;0HHxw9mYeOtddIM#Yob!wUC!5AIs-|s$rS?-+MyVbU?GdXD1?eF)$ z`OjznXzSB@f>w1G=C#I$t<5Bz@S1aLqo=2L&s3b(QhFLMO>b(@;S_^X;~|ux*VN9> z=ir0W)^_P|3U<;s&vW>y7?_Sr;EnvT+R8R$9*M5z%sKG0Of+`4!tp*~QE=bUi^;X_ zv1?(&?Yk5rUpl^mC!-&yp=?{fWkiapcS6=3J-7Ay9H+k+W1B}=&!j$(HOknM-m;>N z!aP+^{3GYugq`Q?1G-lW`YCuL_q{8LC>gH@r{CV!eQePPysgegft~`JDA7}7_&1HD z&-Hz<%sQ2yFIt)V(7p%0_6hOYxppQe`k4XX;#h)*5jn`)VzipSgsuu~)tc$)28Z~V^vKGy#n-?1dM z+4p*`&r|fd-NgRp{6u^~ZLq)2@g0jfsLhOv1tZpWn+InwlqdUuZJAr|>T+}%-QoV* z-tcPzM~;Idp}7jH&i?tz@z#F2HqU(fBd>pdbLKZ#IgY?x>V-J7A8#o>TxKZs?p<>z zp1sKa<$EG&W{u`v1jgNu53TEb$Gc`0%cC3Ff8$H1BX?`LzIcyVs~k?O;iQLon|n?k zVR>}rTaxo;eGy}7zjNGyv$6d3w|O32BZ)ETPi5ixiL6`JNE>fHht@-mC(rqOCY;f8 z#F6aZe>u)P=le;_Ql$gy!zlczdb_^vzGKE|T-HafovAjLmU+(;$*dVp$=AenXR8^HED#?uk{x`^QXg)_HK>!e+k?5 zS#jJ4TYum9;*-`s(?0+AF~0N-Uz?sn^!XP{j9SC<&&Ahh5x&>5YxS#&c23UpTo+Z- zO5>m9OXx}Sn(g|W^%^??>+B&SHKiw zA*)T|6StJ@PMvcOu!GGRCE#(i32V&0AIV;GB;}tUyOZz7Ow&I)HhoEabsf}oUdQK} z-+L_!tHh~6?eE97miu+mM4vtV_bl+f+}Lux#C4XpH8kfSH;2B?xo?ZHChd&DI*=?F zT;c?p6M5U(3|#h^nS(u5UcV{eS30?_+jF54(?XIQQ%3Zz$QwZHAC1!nyEF6tRo!=EnE9+q~>O%%_Vhq+Qk>{ z4PRu;<_h{Rl-~H}0eIKD}IX<759q!Sdo>#AGyzc{FwJ4p7PoL{(Y4YbRe&#GS zq)YLohPql-!s^nVFY`0y?;SYy_m|>}j6$s~4RvmR2S@gQN+S1L=+&Mnuu*U1W#dTI}XSk{*h6X(27fQ9Gm%BOJiz|DPHs^(S zffZH&Gt-Wzm>t0<6UY& z>T!(xW}Pq=j+;v@NF(sKIvWLEDGF?&#A3<|&7#qpOFFn1;E4t_!0~!*zN6RNzO8nH zbBEWwJlppX{slL`O|SJ)=Y!hcbFO_-gl4XhacC4jdct|mHcz8CYQ31pM$;)4tJYiV zI$8XYxP>)v%KT%^EX61Nz&Tsnor5o&9a`p@sRXjt0d4(UUnE&wIzC%*Y140i_pqgS z5HIE4oWn)NN`!T0V1j6kWz`W9i`}eESYY7d^F9@$)kMYTe)8o%9@_5^KWgKd>)iJs zGW~RQGERgQwP(|sUw_0q=+CjNSrsIU1`Rfv_46a^o%bVntOVkNWWi%?ab%3a%Pm}a zj@A{IYjwdB(p>MEx5zlx?in|cA+iLu-|Az5C07;8FclwCVbyDG9iMyo<%4xW+(NMW zt=uz}A>?Sm8biZ=u5Ud&@X1yr8LNzF;**qlFTzp#cj>ifOQK%zBiq~lO#E6Za*g&y zqF|m1EH{$TdS8sT-Rf-=c$z7&i4w!JC*zXWQfzrN-0J*_c6RkCyN__<>?QU?R+Rf1 zqwqqRU4*%=nT;dIGj)#r1e(UHI-5+@TdPbsCheDSQEb6laF4-BXNj7@=UDwq?_mwF zG>&swjir5v#HMW?zvugY_Z`-2DV)*zu`_hT$7y1;P1cie*7jl_|HOZMXANL?u!&gF z8e*F5Xn$in$1E5;%j!eRD??OjVX>Lv@88O?zZ`%nIBX=RDs1Y~Im~d``mXw(_q-?i zpdZAEKI}iW-0oLLx!R|xYZ-r1^PeIQF($HGHCWbQvu+;7Cc6`HjtCDImS5(aDcDnG zHQd*jhz3}9upD4#I~j4pQEz`w81(3#CkZ3*aa1!$5%Rl-NA$8+!S{UM_XW%9w?42S z*hT%EcWC1Ct;rpyy^}L*82G+>&S;D6@AnbOzK@)1Mk_F_IqD-jp?kI*#6204j_va@ zf=iunifnj-PEG8M@s2h=qn>9^D7P^*2hGFUI0MwyWTU`SLxD|{xHDA@y739TQ+mB0 z$3AJ;Gq3Zv{Ka3lm~}a)$Bh5^$(YJua}Lb8HlO(LABeHRmpEfPuek;nmdl!vxbhs? zPA-Hy*PPZH`;@qxeO~73uTd_8%3Ao6{XEt$e*G06RxQGdk2UJP1@Ci`vdRU_@g(hmWNQo0u@+{QM`s;V=e!LhM-4-Lt5kEH-oF%5oOcevKPn zjxF&y=eVWRYvTixIGBBj_=){*n*OYxhm)U{`fNYdjH6Q$q8L+_IQ z{yDg^FWxT|o@c5$pRu?|#!7^8cpjFHD{m~8AWHgn?LF;b2!eGrBj9r-b)W{w@c-SmvAyTw@u~)=&wL@v* z)3>Cj_k?(-TmyD@KQ!nr;K+I-PDkjN^{D|QX0Byd1@GRjs-5)wkH*v;X2DW+1j4ZxUvk}%9*Pl z%hYe|?T>x@>vwGXo-XDt_vyga=iV9FpFG1Sg#8>RUk-hoN;#`d^{a-^CqCQnEsYtP zf<0B{S>cFOkCFMBg0+=x6nM2#;0Y8ZUaeEHjeDcO(?Efy^}MkQIu=GK@iXSowz10 z+1Z_F4lW$&^N*g&u%$BAa8zL}kfn-fuylJYR;8pHJXmiIOxeZVF7_`(AE;7{pSZz6 zY0A+m+s&O>jEswBxjGkRec7FikL45Xh%Djgk`-1w6Nxe=1WpM~ucZ}c%`%RhPtV3R z{P2y5WtAwHxJE38=a3Z}-Pr2DL$$-=at&PG8*lv1?~MH$Bz$F6V~Ibf&&S$$zJ;?B zi{K%?Tl2@-oO@6Itv%Vx#7&&zaJB?DN5A_}^DXJqR`=COfv@-#zv}P4=GXr1*L=x` zUi0cCz&7fQ0viQJDZola!yA78&6_bvVVI8YSEa1#UT#>nkrjk=k`1pVOx39u( zaL}P4O<#0zCDp-+c*6PW6CeKYD)=~5&|cf!ww)(;vlTL(&M>sxw7!mOSxw=y{TdO1 zV+`jGF+H^1$L44B9u`=6?q*Dj82cnN z_K*c+O^q-1oW`0TJ%4c`11G1{GXI^w`|nTUqfNzvO*zuc(E&?}^}`<4j5+GFykLK6 zGP6CJ6D4F+!n$%O!G`DkQ)eQ}w|(osG+96K5g%AUXf-nx0Xzx$U8MtH5h5LwJAdgfR-PYI2-qxoDPa~!A6=0$wZH-Gb&?r`km6R_FB z=ZV^D4dacGG0Y^|9<|heWB3i>z!}=s+KwMJBHW7F?{HM+bm!* zDsiSFPMS0ELM&(02BR6Tj^W9BlCHAOflWNIrdb(T#@xF^%uF2hY~6YGvdlB=`MQ>M z!u#PG@8RtKcHSs3N`Xz37^T!!Z==AgjsiZtKJERHNg~%Gh|eKyeE4RUaMEB2+wWZl zpr5(9loibW!bJ^ z_y3LGbzo=apm4@vg_yfPS}rZbNNk(TLu+Id8_h0m{0Y&zKP8-M%9&#JX83SXzjqED8xz%#Ct~*cxJ>YYuy(^D9|& zhR}1Uvz=jDj7H-Ith*6MC}Jb=SPYNscGdCj7`@NE$GUheC(LvEw6)tP@T^f_6D6Lt z8QsRSQQ&L}5FI#GU`K>GI?nN(AMF6`VAZy|mKYUV!^uV{>WBlzD83Ab#eNAntp^qj zeykL2U$Ro;uph4(TcS0XQ>-?>>W_c)>ki|`_K7#RL_<#tN-UWZxv;11zx?Ar9^ui( zI}-Bz_{bhFVbx)Qc>K<;B;pksJ}mHDoh9YiUo0t6rmd03@TTJ$@o)*;xw@=aH0eYJ z79yH^nr(-9Lrac-EIr10$voCR+&0l9!=K@pJJb>5Km$)qyyUr&xlD?P$R4=I`GFQg zWM2vMXYYi*A9LGD*0G$r7B3;TM>edg$tMo5NXp>#@bjNP9H-!&x^GTBqib6yPT}3| zZxndeD6okV&)SS`6kyPA8u3*e6FC0-qo4RA;UrQ&@z)}?aN}fE_>Z$3TniVt zoJlxaSH3tSi<^*fr;` zIXI)`L<#%j{OterUmfVniABtdFwslJMxrREoR?ZQHi>#!S<(P7uY2*u8=^HFtO8;@jE|EH_O|w0>`lyLEnIkwMnq|j zU-phT)*M^CBpLD)EckAls4{i0mYLUgzwh4;OF}JQdi}9aed=b!35-a5AMcr^{om?u z6xb**O@U36n5N5Cx>4XMqW~@Xl;aVfXWG6?cD%+mVb5%RM=Oa<>&X-QHZ)Cf?l5bs zv%b#AZ+`c*tJs`8uxK@5EOsP!{Kb07NJL{g0>9RgxHGf;5FfDB;T+O%r}mt0JRJJ+ z!REknZpIW&RN@1`i9R<+Dvo2{{6GDRQN|L(p(X1Or_L1xtp!O)Ik{j( zJY!4GW!;EEaB&!$vH*OrnU@le^KD~Y&&6d7XcpnfEh|rsDbL28u{F+cUn86rOwRxd zR*tPX(LC0l?AIk{7LM3Dw9yVO;-%PvYm7@wM1+3taW&EM>@|35%=Sv*h-XYhOuJvp zwsfl9IroWv5);E^old>K&Vf7ChN#KH2^UMvHL_AAUfTr{aamjKlHSLX5drP1lJze= z+xutd(D*_VrC+!sN?!_lYyWgpU=t;t?)lq#zfqu3V9LQ`ZNj|sCw}5N+nxFRW4o`l zl_TG2jxsdPKF4`*+*o@|_7~vn6gb)^nO{0f^oUwvQ43k|59^-l2+uwfBpc8{)Y%9@{=mnlo`6+ts#I6^fkFDj-8Bo{er#|tC z=s)Wb=de6(n|Si+&wS>_p+77~UDLCEaunm_CleI7a|e;add-9Q_HBRRFPxoutE~&7 zdmLgzY^BH&mSQ?9iLC}>--XgI&m~KY!x}ql(4`Sar(IU8F9bbVCSUjZFM6K1!6M>F z(KmKdFKw@NNepEH+F03ZQFw#9QD8X*Hc?_Z&9)XB1)eYp2T@93ZISn@A7eLVJ6bD#qW0~!vHMt0e(4|o=tpltqq=8< z?OjmoUUE$nYYzrYfQ2q(_w5@)oBElZRTW)?s( z7$L5;^+7AD6+pW#&cGp zO%Dg1J*z3ac1?V;@6g}|tJ!d1r6i3MtDvkVbX<$B*jtW94jN~0W9zwwj}Q4;c#gq| zxPgWbD|)ORv2E$LcvJ8=v7jew0?j(s)xkl@8nTugITuqfM#_kuSZ}b1WGR03Rs``U z>Tq5=MT5jEd>MQFLqGU~H$so?!@&v8xNPC(%sRp;QVVm%2J@+2D|5^g$*`D`Y*u#77F|vwI7M9I@W8{}EBTub=$u$QgA`*wDU<>!aXqw@(CqfV3{99QweKh9cJL(v)p)GDB=8zU$xbkhY7+{w*1Gxph>tw+OOjP%B3kxNQ(z?P>ps73aVW$TTh__ww8ZLT`#yaItKU~a z{0zFeZw$iKIaTRf?2D4bS#V}iWbNpi*mpyiJbGC>#?7MRd2d$1$Lja(m;AQpNs33r z>3-ZxV4bSVG6k3UVXZh-|C0N$+jXO%xfEw|39ON}_NFmEtV<*Hw>ldIo^}dsqQuia zgIoV!cxgzVocvg%dZb2~mB7I{CGTGg4#=z(*uH6;mRl7CniE;(IQc zHzEiziB%vEjylBLJZx_WACZE!Bg^~qh=?z17)Pdh5NC6JEIFT>O^VZ^_zcN?g7TOYFW0OHwtVNc(N(5i4srt{A|73C~$%TSYt^l zuN4NJ=xLZ~50ka0+0J`;=3}kSZd~ z|37)GFOO=@Ux|g_SQR@Yw`@o9~oHb*gIeQvO6^R#2UC{ zB4Q1(#oJ|2A8##oh|?21L^11+V;W23L8txv-!187@qDHdA`)ZL zY^SC@X(EJQQrUCyWIOm`ZMIN-tXwAX%`)qE%^k4AVQN3V91E896Ce45k=XZ=Fy$PR zGlwdfq*yI-{+zARkA3o!7txKi1l=6oC7dLUA;;rv{dz8~$J(!@*Aj`=XQ7TI@o~ue z{3pNRqA@+{*Dhs4zDIH$*cx-qb&&Y1apJl8_dFA;9hZjrC;aWaQQ%3Xz$QvOsWY?n zYoowP3ec#hbCd1UPBu8B0=60ZO)DRoy*-P+W_TnE1J>N}@~8jkL4*hvAT)_hOF9vb z$=T&S?|N4YWqwU$1{xWjU3@d|du1^sYyH3v|8UR~t6TBOX7j~uABaf6xgY=N?!K~X zB_4Z6V96w*O*WGw2Uuc4(%}j#l5O%F+US7+iYbt(sGTSC^s~C?N5ai>ifhKm$;+G! zpgUPb3_;?kXKX%)ptaa6mMm;s?h8_JaZXz6`LK73OkBAi2|P5>SYgkHXXR3s8&Tnj zGzTraRgQ&op*RUNWr>X4s87zo=?YX3_MS;R&cqZVjAz1c<2nuLcnzYtc$&<5MO>9; z!EFV2-tnv_MQ#o*uvN@G`ecTN6`yA z#a{OL@{`4>+DfcAXNA0nJs!oQ9<8J>%xus3A)JDmqf?d%+r?(l?qf%@9NSHkz62i; z25Uo%fj2bF4M2uKgUzWV<42OUSD*Fg+&LyXRBKh$)aQ7d;p3RmSh`h22(-aE<~|)2 zmuutaG`EC?$Lg}2$*|Pp;dsR1t2(tDgLCSh7zhq=Q*;z^JrWaf3J3Lo?FxnNR`Ic&0co63sI7KKo+V_iT6WnS)mD z9Phd6*?ZPdVc+_-hQ)KFpWY>x(s2aOR%fHYeNtc(CGL~j8?KE4t5JX!Pl%*r4ff?& z$A}Ui|B?UTzy@G58k=~rmNIerjNiWYSB4*W_=FdBK3R{oG_Y_AAx*gTT(y{M;**_+ zMj5M)J>;}RY?6pF@vn)`7_srab{u;;g^#$g3s?TajZj+GH4_tf=ln_fn)vKlfISSW z!NmMxE6;IqBF66(@&C%&mOwh?O{?D@VAOUK2DWeRqd!9k?apDLCa8?o>dKB48T zQn>}(lm&^XO$1vGppEIapiyMuCk2(-hc5iD|lQr5gpFa0NZh<=9t#dk!(e>OwJsEYWAaZaSJz2_}Dzw0f(^}qeDJ_I5~Zs ziznOhI|oaM_>}d8&p38!tzD>T-|gT%6^4n8j~&F@xZ%w{UgIp>`F;P}?+ZH4h0@aQ zf8xV`AohWnW5GuHMihCeB`HQ{>@YghJUh^XC}jud%m}7l_|cC5pQw|WqQDWdPuz7K z_&aU5GMR^(MQE7b9H=CH<&dLog`aq2J0hk^13+AKy!|b2xecKM6>~c&IvmlP)9N1HB&~qTW2fhfw zJ_o?UHi_?8Gi)lc2D^8x-pIXU_^{eoa$=+%$AwCdz&uu`;$yj^nGb7Bb70GLRp57Se`!K zgfpLKaVgxn`b2r^56?I;+Vk%nW8VzxfaB&|+qKHQ+r+pKXV-#x3XU}rq57vPZud6| zJY^KvM2V+t!nW>i6xdO~=T?%>JbW6jMH|{_EIpPtv#tmgzLbTT>Mu4Ua5Pq_KV$u; z+MH8{ei84`!ewdbEB!d*z&)qAUy2WVCe8#6Wl^fQz@i=Jw1Q3S?=8i(gST53b1YeC zU;{$=zaLm+B}NsiLL+Ic!{d!_0f40;&i?2e>@u# z>lH4^P2bW&aU*-yG?>{^KJ~^ToVS1e{I)(n`;Y(e&D=cC0`6`YhY)1Xv~}1&cjAhA z@umYY^fSkj?-6k{`fu*7L;c|JGz4s*QqBwdvkIOo+6|zJDmjkP`dht?0?!HsHc{eP znaOP&uQCb<&7{qH@r_@;8hbrOLQWmDhw=GutQd{!RK2OPV}9JVKJ78Ta=!I6g&bY5 z`LxwEq_ytUH3t$7S))4*a}dcaPVnq99{tJZXvq2OPvKCuOPKku9oRZ7%3R%A&Y|bF zCF`Xjmkr82S)9;Rn0c-18au_gE49Nv_VKTe?ZF;ikc?AkD;B!$_G^nlES7#aSY)vE zeRyi$zmea3(7}YyAvtD-U=ywEti5LVY2p)Sknshc525pGhIbpgtVW+InU$;=4ndd9 zPft^{VNKyImGfi%?DMe3{3JdJdf)z*uQ<@mGw|^9pTD_n>lI!Zm$L2}=S+3rY^|kJ z=CSAD61*&H)}J&DH725on9q3!Og(>>;5*iq)s6Guu{ztmjRH?21vXLQX`HpK&#!U{ z(BAnJI%c zD{bCOEvywAgAHz)*VR9X;KGr4;8Qz_&8N{50=gD4Xb)C#@KD_1I{^dvGYU3tOh>2JOUxfiL%TkUh&m*Eqg)?$3v1U(0B7}LIYHyDDsyk?h zZI?GyuP>8G*4*^CI4`kyJZ#n?Y@$>Mwv2PgMpUz&p#w3zhu#0?=#wH*(k{=L^jG z^&#t(>|yS&X?dCJ%T~ToV57h~6xc+Gb?CT3*(h*N6cA#Kh2p%CnU3D`*MIw}2^4cE z@~#VutSm)d+c9=6#}O=^9QNq|BYf5F%N$2+EtNQh>1N+9Iinw#*!$b&$iWhl@UV?o zY)NVLV~sJ|Z^Kq~9H|mdekaRy61p2RzoN@r-7Nnj4E)3nH&~N{<5`+ZMj*O~3Z}Kijfz&A}Kg z7jE6FCg3nPZLBwMzP-J9-HTrm;~Ir|yxuNDmHja|!LhQk_$AGVforY_Ru0els3q}u z-?zIP1)dHHY@)=|F;81BHwx@2;Ir>@$jA9wL}86!6_8CO5_C4Uk`~`@mIKLK*Tntt zkA5`RzFnfyFWg{r`7q1elrar&|MqXcgA>b7gKL{EVh1*eHuzM(jLlYXG`m=ezW!hP z>}Q{oc$Q`uyP=M~1c)@)Yiy73Xj*h(T_vWK-<%TsB$e4>>PS*Z8i#Q6nOF}u!#~+{v2&R+$gZ802U8hja_nftg}tkBND?Q z`KxW=(gCHiOjut|9cyU8>^7Sk>xyIQnsdUVUY>WA7LJ7f5B`t;JTfD(L|}9IvgbWc zK9`0`vZN4K_=I!b=^d10ut|K*jcvU_S9@*LgU|b=dxx;FeD;_AyF2(Q?*3Z8&!h5# z2WYtj7u?wYrTDP<@BFL3_0IN*l?}&w6J0n6^~Y!eRWV8E|JHYWb?nC8)2aSu30XRZ zS|1x<9kLR(%x3z_I#$QW`Kh*Fdi}u<{rG)3J;~*xz0DR(Es$=D;w&&jGI0~N>1n0t7(vAqtVeCFsF-!Wj z)x9nSHc{fb^xdFu6j+G@vI_~>t;d=<*t45H1spcgP|s432geb+TaU2Vqo2uOkM=ku z@wByd_m^IO!0z*04ywE@yfAy*2E#L^(;+y<%QVdk4j=~mC_VG)~Yq) zf2Lq>WluK+Hc{f~p1-a4uL=q@HZyj#O~)o@Y#JF&mTdFg z%^6}2#*%W*E%kJ(Q+35xa184YKGf)%eTl3~SN5ysNR{|FtW5QH ztuk6dF(B}X&3JeP_PX}<4lO_X*{hBXUhHAg@|x@$@l*T=s*750)tL0CBYO zkBHycYu2!OSoeu%$$3Py#$q3X=@}r3O&z_3&-z}u+wlEgn3z^Z9~#I-dU8 zySjg`^mRslUprQAqz_x2jRGjJi4xm7*eGxn3ixDl76@mZt6toTsqW=qdb~Gh4)wv=Cn`xXe~d@f@oUe(e2_>jewhuHkCqTV2~L<5#W~ zXY(lbWi8e|$I4n@-EfqH18Zk4^Zr`rTn9As@Z{KtWNYC7$2`6NM}FicLcJT^h`W(U zwOz9mw7I%CIj|TK8vYPt%UFbW+j~Qti1AskYm7}ihCM2B+e_AycYXBhVmE5=(H1`M zv`g^GYGiI4V!On#eCWOJeGDeiLLV-*aPUPa;na zGU0I1KJ`BL*xyU71o}@)Cqb!Cet!OgMQhsE`dc54cJ#zens01OvVMVMKNexeL_b!F z2)}OSoX9FjteZKtEg)@iy*GJXSTi``V>QC^)6qn|c4_^S;(PJciVEe7R$#&jXZZSyhrlk>qjz}VoWP+&zlciz^PAqZ3R_smp4YE? z@x|R5X}E%IYY9s8-r8re7_{2P#V3EQ@L+JvC6OfiZ2c2c;KCNJnao%G47gZq7CAV> z83qlT(^lZx#?V?(zBU9apzgoTA$~WgOBHpZ5`GJnM)P z;-!|6ss30m9ID`qFzr^!^YNZ{z3cG&OtrhDES%xsYmR;+F*}#w(l+A0_G@;{Z71jY z6?-f-d>lb#0h_D0Mmfi{P$M;b#OyU-9)lqpR<6%$C1O8@fAroaN{mu!tG7{LhyvIf znOtbM`Q{EaT3Ctpm55?fxYp{ zzSj7r2w=~JR|211pr`t?cG-9Twci%w`+VEgdu`ZL@c7}Q=e6-+BRJ)7Sb>jthP{|N zrutdSJiDA?uwg7#Q%Aw&HFD(va&-5wZIaIm=F|2Kb6WO2&-SYG|Zg4Uzo z%5fUkg9j{hMDhu@Z zyEf)6+`Is=-wz1_I{+# z6*h5(l|qO;ttHW4I)RaKsly3wL?rt7N5AfQPFBr$40|XJ3k|dEejq zy{-R$a*|&B?cw9!U&CL8_F^6LNWc739g+Em1%QZ~2ew@}cc=`SCLUTwM!S|6Jvq?Z!G!i=}t^ zHJbEG{A=Rd!MeLH;Bn}8o?Tz^ciYxOKTm7Hp6=J9F~NOZTst}$-?i~fk98}3k}0r> z5>N8%Y<+vmD8ONbR(-9kDs#gZIwvHMMS`~Se|X)$a29Jj2S3YcBl(PH>_BQpIgPZ> z$w}vzgfnuc(H4f7I9wZ}a7$&W$sd6=Qins15O0UfBa$X+_uc=?@3<;W`@YGFV|)>A z+TYy&7A8)6Jachjqa*PHYBi69*`XVp;DZMEMlZBll&} z%5mtsEgHdGnyl2C8nL*TXMOjsT8g6%L)$lYd$ZL0+x13)S33nZQR3CU0=Dt5NCDY@ zuw=q#=bW6{r}=B$PuBGu^v&1!CfMJ7W*ML7cmCDi`WWk{US?*BgpOw*lh~5zAd7^= zf>;3fumu$yKXq{dnQa1_Rqu~nCtDraO`2~xF@}f%ABQJfGTC#2Prb6l#gBVw$TGHb zmn;8;@By#=vn?F>eC~6{ZCcZ}{pH`(a_xWk=A4)fYz@aSnYa2(SMcRnoYmu(-q)*r zJarA9gqu_KDn40=F5N9%e2~8O?3%TXt;U`jmw8)51eGyrDsxgeQ8hf~PMk!aook8~ zBV|24u?T2rTyTwrRgYlW)v5U465E1)Vkpp1?9w>2(=`(x(OnEtu0`diTQ93Y^dUls z=?F)#MnB@^UwQYtSJ}3$?@hcH8trmS=1VGvzTO(YiH{M3)o{Q=EwKjNIT+qgIWGIv zTw8@@Oo`8Y&Gl=oa?f}sR9|CDzqR_c!Fikln<()(O}3Ja0%uTwri>ptzv_C}gWwEs zvrTA3f9gkn^acy))!rX#e7?Qgb+7-Tv*`Kaw|rU9NNRy&A~Dvg?r<$uKw@7BP3v)D>Y4+}gPS+M{`HgMxhz9s zUUu<3?|F>wN&k92g>UM<`I6)^EE{G05Hat5ewI9}AZCEie6s)pFe5-u|jxCaO9wZA?UY;*DqMlCfyZ zIa4?}5wFfmt^<~`wX76U8vNMDzy8r0+dpfG%UOo6OUI-=>z-%6t_ANe?=h(stUFG9 zYv2OIyR41_e3t69;P-G?{=6H!r)=|ii6yg#z5U)OFiL?g@i1g3jIqJMgOsXGi;XBqnpM5*+-WNXXGmY$h;WPGY z(39Uf%UOTe^3NJ3cCIylEHJSK_iTKu(fxI@jLJNVlD!g#>Vefewg&opv42E?KZh@I z=Q-<%OKDC$A!78fr+(9%vy}D65B=Z|-Wb!|J0S5T{-`&FU_lx20UT-s?Dy31u}^+7 z>ae^m06ke%mMi1<(0ku|l-1|Bx`wly^~xGD*_kU9$|!gx&>Pr|0tX6gqQrp|+r>tK zb0~mSs0Q)8zEtZu)fYa#!Xyud`Nm@BHOGt8;!m|5Df7961AFVpaM{GspT!YiMMhvx z)e+j;!#wr-o4@%>&;AtG?fhuR?IDpzXh{n_^@CHAlt`V!wfi==bNN?S*;68SSrD=|N6PwZg?57bnuN9=pjjgvSb)I94PJCES2b*K!yBkLjj{u=MsAcSQFNxkp<|Iv1!Zcfd;!CVo!wk zVRynYso@gNjTV>UJJmk%*~02n-AnHa4b6Cm+ek2aDa7OL`E8pw+Uu#kAda_w@Ic%E z3jimdQ+3@J9|7Mx-}Y5!(UOHq``AyR?ek!-hkCr{U0?G&v4L+me3=@ct>Yo~U7Pyn z86pP#+)w_o*=In1qym78^N-(lpGMm|$AEJStLC?U*Kdj4rfq{~H*&{ZTn}9Js4PA4 zzm8?ewdXieI!No#4zWeKW#b&aH8OA2K61)~m!q2X)x`1G?$dMV`2X3vAE5oRyCCdu zD*u92!H$X)Nkq_TK^c`&5Pt8ETE>AoW0}fSMQW|nc5KyZv7KQmQfbKi5Hd(ZiP&-tEn@BN5J_AkA_JF3Hy3 z!Ie;nEoem)5;Tf<@w`i8f^1Nbl=Dgo_!js?qRGKJ3j>Q1XBaiKVz05XC4A?Cz+%yy zx5kH#fsX-)^aA5&Ov+1kX;yPolyiM8C^H`d+F#ei~ zTL9CbF?zC92aDtvduls@-VSuoH}mcH6r2qGN_;dy!V+NdWBru(<6JogpJzRXG%#;* z)czO;eviom@xyoCnWHw9ea-z%c*|?`_-LBUuSjEK#0jzfOV~9&#!hI>=rFXFQ{Qua z_|V8R=@=61b?E$Ad-0A|dS?%9yVhGNuu|YS1=g*^aT={PRtj`bKyHT|&@cr`6)ZIl zkz_im&${%~``>@sC!xfSbLS<=lRbGBqSTnHwOkHZdeZWg<$CYHreqc-Nk&QT=itQh zaqIqp@B8elv^sr#CvoN$OfpJqF-0o@2bL9^HpG5CNk?;}Avizyp%0CoQwCIH4bB8k zg|CGjcpojx>-=5dBY7^bFLl20ueoipJScU)J)h>Bu~mJq^>5oG(S`U+%z@1iiX^`8 zNNjP=%1&!2e+KOky4J^#K6tNi-L?ki9z*bjZ&z_T*LD_XaGW@5%1|kRaY&?A?mF6RtgNGz`B(fMyIvbN`Z<3DR^QI zwD-yPe%qpKiXe>)eXyojcmYyLN9U%Cs)n=snIjA(JU-jbeg3nnV7exjK7*Lw_-EfV z%9V5nyyHYL(TCrDCFhWc+q44QT|@dSws_oYQFa+*851Kb<7ETiDuT?h{xT*Lpq1ek zcC@a~PG964!E7IV(|WADeb2Wte9&}O?O^9&SmHJMf=Hj!yL>j{4JFLHb z3Y`Cfk9KLSZ_7D^lan_;`YoqJw~}a!2FJD^CHy(({FfNf4H0e3!4qxan>&92Ye;$7 z_&lHT=?v-HTBj`q)~!TadalrJ846%KecEXwNYjP{>YPd(QA_wHJp7YipWHj&k&Q$c zAdtFFh1u86xZq;Q5Wz&T**>%E$8Dkd+D^Gn`+=3LSr@vzv-XyF(y$wq94@%bKJ);7%CT;^J7y%ugZc_fm-$9AgdZj(hJt6`w!G`L2KV zpDg~FBC#%WTue>jR*&V-Z#YG)F;0@_zAWj7V2r(IOA&MxpmJ>cq4fnRW}GGpPvDc4 z9eXOG)3cxX%xUR9eGD#Nq}yNm`8mgih+Fj$+OxoQsLX5Y7{c23AURA#j7L6H^LflUVh&k;ajaD47^$57fN^6jdbJsx&>D?0d>Wt>cV6XRA z3ak{k`6;k&C2syXUPsuR0zTO!u0G=qN@F(o%@HMlq&#qtu8Lr@ld>kQ1PN$MVci7 zy|+%(9a1tzJCt#r){b^}2&}cvN`a{qSho^WDYv$}1u202WccINOk(Z4K76`rHfbuU z_$$;=Rl0ujHJBPrrR3Ndwx58Ju`SVK<^ws*8TgCGMC%=-MOCv?=L0jii zK4N3BS3|UT_r3PZvH>HXkvODO#G01r9O3JFF8MOVbz9G4b1MBLHeT2Rz8B8RBU0tL z$Ka5!MtR>kzYsWc>c?XUxA%!b7>m`_bKZvp>rmJMZLza_66*G2e+Rx-+;PWjSVM3# z7~A-`45_`=St+nmppOFUR-%tG>-C$E0;KHhmf5H;IXtXzlCmw}kDf_7O{#st@FC#mdb07)1m~0naL3wj zpGUuCQ8zwr+C%#t^}?@$K^wRA%$64O!hp*=j4@njEIf-eCR1GGV;-XP4xv>MjqZU5 zAKdFde0bgioep&S$C%etGS|H^2FmggtPHv?O9o zS*O)G8|Kh}1imF$L4E@Q!ZPXzJW<-_v>Fd-o}jmkV*-9%4sn_7>W|1x?50I4BF)3f z3bt9pH(zm$pF|zastb14HCxz#(^X=pvA&`a#r?3Owxu$+wh}{A9v;G%*ukCA9X2iS zVGps|g5SoqTy_npx`v)4%vd(#Ku^(!__pxxKx??l=WY+j_E{W?uS8FZO$5P!(RK){ z-3zN|7#HvT;UUuj;sER(=9;MsNj$cdaT*(a!=V+ptK zoLl)3(4Duriub`RuY2~(b%7?G0FPeWY$s&6w_b=Ox(;+Lqe=zKe-wOGf$<*K|2pJB zRW1LFtr}N+Vuh|cEw5!wT$*QF>6UQ&U|l9Y5z|6ku|#-)a*WXx$@0ELUv5>H>sI1co%vgC+&)KKqz@^&TM@-}g+$NipLDhr+%7l*YWggN;lNV( z7=@4?!h_`rV=5BVu71tA_R06Y_lih0=*Qg^t17M4QuLHz*SHv4VBz)s5I*xeXM6&r zo>c*S17EpKvW?4j+-ZL2;OlDpisWNs%=*EV*?RTcVJpzpX3n*{&d&BUCh*u2FyI;D z0zDRYPQ{hhPhLaG<(Xuf=3*?IWA$k&-uT16FVhV!!EQy9I@WIJeQdk=Ve}IQTb@5| z+;i7q-3M#`NB{93%rrAU#`DZG|6_js>bi4(+dh`tS7`;Yp$xC)!YMh}wb!F5 z;FC-$DXkRNbTojG4MYbaIdm)$&{TJ9k9I6m5d(j!RP@jg;Uck*F^M1(=R>o}9)e%r zGcNEMZprn46%BkXSv8+~tRS|DbT;xVVC~_f4QV0iq7^$!Y&$%TIVx~(gPQhZeq(bZ&jEuK+K#M+DS%|$4e;4w-u;>v9*&{#=G5~%V*8iaVs`b57WZxMe)pw}&H5Bz zx2qrPwUq)lDh1Z9#Em+0YrjWQAbkd}v~agPhL$nF3+K#Cs>n#6;Fq|@1Pd)MN znU)Nyrus+JeMoCA2iX18$Now-NE4(Ko2Xu5)v}+4`GfONeE1q}*Kfu<3>ip!<%qbj zeb_e2nVS>NoQ|&x(1>e8v-gT@LxRc$7JlQ!!sW52EV(049<0jGgG89UeLNdV+ASIb znd6gx%&he!FA3HkozYulD26!jh_b}cajp@icNGn)KHIw3rg4!M8Ol9ILJUb5BpoEk zk()vqm=iuc`p;*mFIRy)C{r~%e78hVLM!ub9t6#1HVSeFgXsnJ#HKAD%=Mtgs*Hu(0h)lk~}-lX6Qnzn&?2uk2v za;=p3WBzJ#^2_z#Am&1)@ac8wyV}K+%2%7<$3@2alpNy^>j?y$1_pm$=7GH z^?H_{h4k!|@WIZsOYq~FpF0+K0+#1H&wqknKV#?pm`*n>0}ecg`mWxl?mT?PHst-J zFZGzGUu2njkG(JDDa;d%Q{ zfAb!ExM(VrG+krdT+NAXLw2jK`dyA8&bgPHKhKJCtX3;D5dp6(Y3MwEv@8Ounx8b! z7v?o+G^C_8OEC71_dfJBr=NQ8!7Jzr25k=wEBEXnFz41`K$#63=K>noo&n`$8Ey^i z(pqh$z-$VvTZ!2uTmfAF6kyy$X^%}>BKRKo?1&j|2ODkyS2 zAN&j8%YjAfn*)B^nhOByz}iM&*uFB}F(MsB9RaT82N|-(&zb8}fAx9pA!zHdA=-v9 zRuU9j_=dj2bAa7u*yWvr9`a|XZ@bS(&T$Oil>0O$wue*NPHBTCOMH)}^kr?cQeX}R z)~&=G>aA^W5DH+eMV=4@bcrsr$Hlsk;yJLzK7-sC=i;-C5F_1I#Wn&9ku?K}4dhlT z=xk(i^~s%!FB>zZzUx2RXxj&_oh1vOU4zs8Lw3$X`(Ya)0u?FbwXI~bZrc)H4ZG|2 zbAgXEda3nUkJ0$pk=NIk`h6?lT#M^da=$0aIdWX>*bsaP> zS(K38HTC__rn#SU+XTQPWf?46+pa@?kjOuA->biLxBcK&&-cal;s~1EZQCr{m^k={ z#JMs@-bzMry9VpYN`aLEgDJ3XB?gmft$5>7fbu_%xk~y&RG>8=o%C6ZINtd)e5lcg zd$cOXp%jUVG!d@4909VkDg%&u95J5GvJ=Yji^h>z2>u?}iK?`1(vNJ@U5xWM)y510*>n@De1BtD11njPQ@Rjp86(88}9KxkeJwDf#{Hw?3cX_nLjdq@m zgY!I*WsE-8&)bmSW1_fqy+;m>$J*(Ij0MWUi zgb*o~e*KweCQI^yPWl~3T$45}XH!lSmT1@npHxj!slnlpy|;^fux6xAQ)kZjthKw& zJ}ae7`;vV8@*|IQu0Q?}k!EmcM5H0a%yc>tL73=Js4?mnUmk-6I-c#>?f5vAFrOySr!Uz8tc*p8` z7c;VY;Gu_hW6;Ljz-P>5KaRnhb00oyS$%K)y*vfht;F&)UNJnU6yVn9SgI2~;rs` zekp6ydj&pj;AhE&-+SjBk9^bBfAxR=*Xi0{$j4*v{?B&X@S>|1PGtzM5A3FQjf3H& zd4tb8hxkAWI|ngCoGbZiT|iJ>;P(JldDw>8MuV+nB=w8$d*7+RtX1>9pF@H*bHVps zsSlzF!1$P47CR4Li~Ka;tbZ#7t~Uy-TZ!v!M%Ml`ra+eB|Hi*P6+eCHS2=gVh#Vv1 z=}uwacuc@?O(s?MiQj$dfBA15{QOSs2KIv8GF&CTIU10zzR=+C2MGy1T=Fr*`1rNUE6Ibq%898rCTMXS93ceZTjr`BODq-CZxo zhwUcG4!RFvjcv8CVqxsUJs&+woBA4`+!OkfS;5o?#$-SCy4T6QaA8YV4=w1#X9u1x zcmEQf$XIOLjn$TzhG2}p4VPG&0a4QGzQpemU!J3ci(S45R#B&ADOF==CD4N%f11am zv>V$8&ju}BM0YYa+7H^2v9ONS$AS}&1pUkvt;8pu{IktzNNUZ%C~)2P+Aqsn622bC z`Z@MKzq2>|=pWeOBCQ{@{)Xb?^CYAF?mdy_iY7N1YklKT7-Q;*{1x=GK8SeAXMPN< zV|C3HjV*lMFP^t!ZKmG$?e_lno%4?BdcRDC-Pev*cxgVN5BH6Hecejj$g{WheE4ph=QE>>NXmsqhZ9^GOs)l*_VLdX^j}oFtWp+D*Aff``q9 z3m%3CY^a@cHLYRl+Z01Gc#H{K8lOc*DL%njF`venzFHaIwd9^nnkdbcI+;DKJ96MR zTu=PMpWdTgX_uY1PHB9f`ov$$srWwqYrnS3v~ApDgTxAeNg|7O*fK^OC0h~rz;-{@ zkl(gdV*;}pwuoXr!&ur)xzECs_~0nlP_S|b$T#X1TJsly$6@>CS@dj(c&2}&0QOWC z7Ql99=WmnNi?nf1Kl|M|SSo|Po~ENN8_(am{Won4m@3+BwiPEQ`r^Ga3UEhdp%$L_ z7k_4_>9jWZHsk=%3Ag(l%|>7H%;wx4#k)lMJ(;}$&DzIt7pDZ(%rTO)$8N z?HBqI!`RM7nvpu;iTm{m^BX&w4F(E!CPzh?1GJ|tx&^MBNjVukVF8xFCpe-2teNdh?2MT6Bt>2L1p4Gw ziFNMjdhg}LvRsh`_w7IRQ(2-X2SC8dJ`L$p&0vHf1w8ls{oj%2)!dSh^#ZPLz=Oq= z+ab0uj8RGh4fu@DVP12v%Y-Mcjd9s!V;}95wjvDH%5IhTqAr#d3q<w=-l{d zP#kyu(C^%{-;Z`RK5Q3SYdZ#4z&ljRjGp}ow)8ZEoax9+p;sn>$*4enst#R^{0+QS-MHt1=4LTCg|&DJnz;` zU!9iMpZWO5v-X0&VoSJvu!IN*u|Z#*TkZO~mAKVr{FWLoC9fc`aV+TqM){g!%BRmr z7=DoAEJbjis`zXLzNLH#Ui$Jc%wr-g%r3jzd=q1(^FwIi6Q5l?6CF2F=97~cA1U(? z7-Q;06e%pzwQVrkz{lt(yT6VBc%&{ncJo%I6AXN=w=^OlRR;U1_y3ny%lP$6;S(qn zTYWJX$GNL=tMSoRMJ|=nFbw(2@%ii@nUbToX;4DC&FHK)9x3rL5Iajg3|+@jM5zM9 zGVY`CY-4c+V|K`m!vW=iAVNre`mDQTCGnlWnnxL*=-gn?Luv4B}{H1P4tL z$P4XiIDQ+KEgwS|+uN=>v1sl=J5rRy-P1696$aL}knh0u8voJy%RC%p*OHp9m zO59S@eX9+YBsQ^cr#_DY)P(`dQgjf>gd&}!P-$z?8kbKxDRah=KJ#UnQ`I#>2oHEZ z!!aH#F9Qgyn(?{Uij{#c412f^Km9AevSVM&VT@DY)J8+h5@7J^s8R66JhsHw*Y9$C z)@js>^&7uim0Z5VxFq~**rxB+Z!1JwIOX_A+_7~rpUTGQPRjX#>d1=g*^%`l7W7|lZT6O6$Tb*&G%K(rTtGu!oU@r8j72|vkZ_@$6`PtpF+=5QIO zK?(ycY%jVpunI#z>nO&roNACqiVe1)uQ6WvIFi^hq>wuN>n@I+ZZ@8}m=o~YBh%o@Rj2d*hB_L%;dpR^^*^LiOtRvIg7 z$k^l_8PnIXb-g!e54^8!{TRTGg)yW)8ZxlUQI*nj=X+#*4rx2KPB~xUjy)E}(E6UI z9PB*Jmv_~achpc^>P6U#sqe!vxYW(sGS6Akl8 zq4D|5V0ngWWJn%^rS)m5KkeYuFk-BziaB{!X{6!r7J?+4jwL*{3 z9t^>Gs7{QJW(oV9X};AGpkqysL$HS2i}A%ejdF;s`w;k1$Fpv|mg6yx*{3KfH7c#S zE-e9?wzA3hT+pW0v62`u73)wTZ!v` zj@BMdra+Pcax%ap4N18(n@O6!cN}Rr;)1NM*21Tbzai0#CMkH z=w96q*GCNFLe-jfewX9ez&{^rdG7+SBNs|soAbT~8+_^`eAq7IftQrSB>*)=XJ|9<=lK8rp*S5dhj?7tpWFI9Dc(Q?KX8z zX8oP_dw*;#VAmb1*HSsYmiWNx!{uIUT;(|G^Om?a^S(b%u@+9!A6(^c*QWlRRW$Uw z73{iCVApi3>$Lh^mNK@r`;wUuimNvVVJO`MDFB-e%C zB0RMUD$4cP+>rze9vUg-xY9b8uY>VXNt3DPLJx4EW@Hf=*tzh+?{K}c8gZBPEMpa0KufYEGJl`^-t!Vz!; z%w^a&l?da2|gT%(~rN%Dr07D9| zSVv}I7K*9O5XLJna0NaATU(BgFM{Bcwg)Ay*7&eR*uCecjO1X8up%omyZi6?-sfG# zYM>|fCU2j%gA{E>JH|tgJ^C$I)jc+T@y8jV#dzQvE*dz` z_T0(vLS75~$i5skfRVpzd_fCiG8ZGk9j%_$$Ie|oa4B1)AnQUdiki905@BUJ__}n} zoqLV36WHU`}ZPV~Oc8|%fR6j-+sM@h6+xG^a}L3vgvnKugiWyZ2mhMp^8 z&y~$>@%0%Uz-O$juo+K{>zsa@adzQzEsh}yVlZnw#tfjjbFLynZ{*;m3G8Iy&JJ*!sU2<3516w<-7U zE%R$Lw)3$rg9X&H1LJOubu*sL*fw}uV>pLb;_8~y^O~*BHsiV+TpI(`%UB<+sPkOG zS+^3;73EjF3sZnp`(q#eOII1jJn-Oyi%1*77+~kJxHXbtU?Dqft09tdky>*T4&$3Q z{>WEcr2rP8Dqp+yzojh2XHCZW%*X%g4v#1&(ql1HF&@TZ(T5f;x}>-Pj~1ksbdj!c zQ&cCXcbiF6Q8~c6J7w(di^ZNJcr?Z*gK%HhTjC?BmIt73Z2Hg=U*DL@>&NhkIA!g& zGPLT#$6zKIot(-cBAuEwkTpKKAJ%WR=C3;SjwuhC z7ty%_Cbm4zpH1%oMptoUqr?(m$GPWCIDmU!_vP7`WCttJr|+CpT6T1r#14=LFVS%H z!7Z&ouEslRb>zn1;oArrOhXtVTZ!vq9@btoqW~in#!SEd%rjYe&KTe7<`s`c@T$Jmw*Z1N#dvxQI>MX7?Cb1nTjZCurdQ|8sbKiUJ zxt#3Xa|%Fk!vM-QJEuOY)^J`|2Vl8%tWUO};E1;`&@A>kZ-+Z@)d6_BP~U#UXi!8T z_}~g-AiqUhDh<>a#LYFDz}|a4%pF!#$w-VLniPoHKPz3c&6tY|E$({BOLoRUaM?D5 zah`XZVAuS@VqUxGH{_bW3jUl^6>jHAw&=hGhSZ10=3)r0Urv4{Prd*Bd5xGS@3SQg zj`RgC?=E;UuCx}t(}whOY#r~=EdD6yw^YDcqLy-PTLZ1OqLmP#?R~1-HJ&w)laE9?CA5C7w91@yRw_eyY_Y%_d2&VR!w;-UsZi(zYhr z@wusKK!W*>l%yo;94gIs$wI@&&w_n(i5m=d!L{wUaSg?l`+=oRILd=|LEQNqbas1> zfj|zzThzt&52ay@IU6-3oLFg6aV(pA+(AjZGh6TembH)H#)Z8PI73S;`paJSo3CPL zYBzXWh8TBd?YCni30{J|>caO6|MJg`rs0sWW9Q3tXneN3+=+*@A6my2oA{%CZI% zqe}!dr2Sgw=App4mAHB4avce2S+ltAedH83+ZM$^IxY5k{Eo7(?Ud_SupMi(WsxY! zGyAeF^`lihd(!%@ycIrC!7?dHnx~AXs|_i9(5@^aet(R&%k|TIw2Wn*wH0Z6#;Y%N ze;IX=>bEfN3fM6|+Ml?lM3iQ#k`-e06y~uG{i+ z6m7A0E{qOs|$(Qij3v1`-xVH0B_qXr2dAcCN7jQQYsR+knHJBPz7hqLN11OW*5Q(ozDl}yzrs0{h9#^a=kQz> zt|9$d>#P)LL4kEE(SnLAjO%~`q*x4&hOh{8yZgo`pS&Vn8&-n-KI4KV6{1P{nj`zb z7Y7!n`~=S(Q{A4DJZ#DXr{5&{b=_9K8Q|15=2#n&`__?aqR(iEWiRc|jdzM2c)%P| z@_!*!4%+hTVP}j6UB_GlJ@lhFf)5K{mi4*cf^&{t(;`%nRLbZb{sUkQJs3m{p&dN5 zJ*DqZiKAy}nXlr9eLg)~!T8ZPq(C6a}z^wi4Kp_dN0Ki+wf($rNx8>xs<` zi6M5u7NaH680|3R2|RTvUqB0+y8)yz=oZQY|uwYzx^vDKG+qr~y zX*Wp$VN2s9^K83sTjO}cKl;k6*gV^(G4pk(ZuS}4j*;Y^(NWMi%AC3V5RifQJoAhx z1Mb0cAk4CwEde|KY`B^7;WFlm5VF`OWxNKPkBqzsaeV9} zA4%&j<>zVq$EVCU=rY7wjk~^rTPZM!0_#>{64};9^C{qUJ0Hq%2#g=F6F#%z=pSz} zy@rS@+!cMc7*b?5aXPqzEp3~)*p-Gyt)z!+Y>g!jEMKrF`SYKo+N7CW{tk9>z)h~q zrG0G9+@7V}EQ5x6J{Oq^p{28{4lMrkh@dd%IhkHEOfk#QB zb(Z|Y=CH-)N<<^!4+2l%fR6@E9t}s|T0J(#Ye~an{`sHmws(bJ3%qam(LaziBKmU- z-m&{qLB<%O4B9bJoI|fJ+~qnzNAF`e9CNosUHE7=6YP9=&veyUudNig3<|7UiOZn! zilc)9Y<#i99k-faB0=Xycp0&%xt!({a>=-1$&8>RUR)tb2^lDGwQK1<2tH}(KYrw! zGAkPfK%}2+i+!3~*iOe}mszG6Qeib;`uBcYj((J*hNLmJNShY;uy&7O3zt#9(G&=8n z=xeg3!*jv)G_#Ir?Sv7}9z}qG1)pn!zFsoNaQMI@Eg_ z?(s9<-Qrj@E6;b!cV>4l)~hQ8Zhi`^TZx;0j<3@Z%Bo9|P1?0o7qKmZOkz{wlaI9^ zC2oatqs)FH$0*0Tl%IX)v>|M}=itM#%ht*rI!9J1KK&)WBrUv3f;k75wxp9K+^%!d z!LkB8TtJ#b?CNL$6B>j+2%46;AL?_)Lu0^WsTpk2-UyZ)M~+(7 z^X$j7qRd;UBM3!w6CBk+ds4Qaa()EeiFZA?-o=sQK`93Z4H5B6)`+D#O82tiR4Q%R z!v7^{wgv@;7WmnguZ)mZ_L1*Vj5`~36*u{V(PY2pz3& zu;EL^5*N(WCl8)bqLXZsz)tIZcPoY{_r3Z{ljQTeHWI}GA1PyZtMOMU&qepa#%IZ8 z!V_hTuO&VPJORHhTjI0kY+r5hN<4y#=3D`!<68rg7UL5@v&19NWv~0jwE{N z?qp+!#G-PsWS_evK86oD`p}l;X1K8=J`&jY#K$Lc34Ca1T$%MN?3zWIX^oHcHSDw* zIhEsUIe)jm_{FCW|K!(a_@s}tIc&IX9fFp9_~iC@&70nok7MJ<)-!U8c<DJ&vUxZKc)}Q;Ohlz zb@rkcWl3rH_ZSmg{6`WSH``bz_o=`~3sbHi&m&DqQ;kf4FUF&k-(yJhq@i?VY!(y;{4R1NyzM))eEwS6{(FvSI?DAx zOcKwZwb5d}vcYbe8+aVqMZTaCe6D-n6ql!;CRpajJB-0wowGuY7x!D?1Bb|=7EixjLFhSDnj84g!m~Jay$o%~ zASUP$Z9ErC`K5f|qaV%q?)m$_BR}sCzWL3kY0JN%adexq{mvX=EO02Z4?+^1w0f_RrVAL%sHwz5J7V1kAUky| z(T;XikZA|Eb$Eo5klVtuYFjn;IiLlg?JzexO>V+t9DGu+_4;E>bH01hk;*GX<6s;N zYrX%CM}XV8IcS1r=jFyg@q-`w3sRw;UK?pZcMfOR+5K<*x>H9JqqAWs*IF|vux=%0 z5^!xkmIByOTZy48&sg|neHw$a^vUzOWj{Vu5?R9RIv3ECWGzRCO1R*|wzQOT>ATMa zNhS6VjyMDpl|8nrZ&R-MB$l5-c=(%0)#aR)XA0cDHvDF6S-5hukLJ#u)<>Q@w)RWr z2`T&JbIgvrytd@|SbUzFq^+4e&sv{iA0K_DFZ!`~>avDt=5@I9ME2-3Ro#VW@_ghBdj zEsq++wlYnt`M}PT*ykf{?*j+C44phb43nm^r|@8b85joaC`F`V80NV1uDfQa{B``s%#vpZ}*#BhZz774Zr!U;6Sd%}{hAl-K3R+|mLda|DH}I!ZE?fycL{uqwIbbE;EA#|K3kU@1a>Qx z3OrG^!iNnatqWU!@UfTJu)w99i;pJ6y2#mp6RU@{uw9b&4{Wq05et^XR>L+;8^@R} zqPp2|s-PW~k1cwPgY=M;G{(s89!ng*jX&!4l_ag$%p5r>;mUNv#wJZtz3uz1mweu7 zHqI&P1+FOj@Wgf6kEp9;Ach4ENodqllD?L8(UsvG138+TEFCTzbev~nsVyFM+I>M! z%~dwa+l~!hYba=7Ys~kg1I6e|8&>xs+t_+5Y7NoU<;X&bb|o!i&5rJTPXp3)QuDxJ zuHu|Ok#|8))6tjZ;4%LJUs>X7Wk9K8o+0ZXa9MLXKyNWVTIJD$=xAVEd0V{o^&-(f zmw0d1O9U(KXg=zawZWjzxNQAwTe)`cfNaM%#2{?f=D>-d>On*9`4_4-#z(sXAD8uW z8lGeIZRaC0=AC@r`r;Ymte5?r=l5#BXd77`Tx_{sxl9VITZzl0_KGV}fO58_wEhrL z8L&{sw@B}G2v6LTMb)P_ODE&vLM4MEcD~q|oEC2(JiubtLmR@c0h_;+R*6eKT*k%F zN>EJW>~bTXI%JYw{g?WU&j(j;xWHo=*BW28tKUZVl0T!&ry_a|d|aicj;C&yjh)hX z`lQsP*|s@#yQO~Hx-A`spzOPOW$a;rM$kq58lo*I`-A0-`NGzatc$qh z+VemC?1jGXv_acB9*QqXSF@2y;9@Km5+o^kr2*^;wGjAXJW6agpNYP$RTrKi*BGs^i!X6Wu~ySy2&`juu-e$~z-7JD?DT1a zj=}4?ABIk$9SmGbbKKQt>NQ&!Yr4eYyFkk9-PQ_UmTD(Ir0gG&v=V`jhJ^u~{xcG5 zh41bpm50N-lEEv9Ijx&{x|X&|49QxKkFgj{XL_IP-XAe1L<$j#pw;*Sc3=5D-}}5g z{>#GWo8f(GTin;D_4@J@Sho_((|EL2 z$CiqZNsNoTdf;+D@EISY0LDV$9$wC^xEDv zMjYxx&G@eN!?3Mr837M&tf%KaYdO|qyg7y7Onk9jfy-7% z%E7+A=FIjp@nvmQ(q?k&KO|<5evkHPEq_#ZLeNgOy|JgZ+kA`0=w&Ath3){`Y*5;YA|*X)E3Lg zz`-lq0e?$;T|LdwVJ;CL4Z&rOPvo7LM{M??Pi7Z=#$JmkPsYg5YHa&4_1U5GuL*iC zjgJj?;8F_8Ii|m3>t>tr;yIj)k3UKF6-sMR=48Je;r4!Pd|2gZqdYbq>#n3%{I&(f z#@jlS_vK)~LSebw$9k9dkKsE@+xBd9Ry@yT`@kR94*kxo<1vKAeZE2Qj^<|Xpd}Eh z!n!1;py6_EoN)UsEW?m!Xa%Z4r==E&Z_m>2(LtSBE3Fi`fhe$UC2pX}y79Y8V!=p6 zX6U6xBO5fsn#UgfmMj_aDI{fUEwR8Vu&?*oi_dM8K7I0Lv>2l;7wK|n0|JL@B*L!4 zM+4IWA1OMRq-%^)WK1R%#a44A4wj`=G#7oA1-Fac%4yQl{AHiyzi@Mg=vcq+1olfwF49=10kZ>jGn;dY(N)e_HhyjQwPYo_b^g=<|%iff|6 zd7sdB7)N=y=AOg&%X{THt-=m(0eh)a-xD--EQMo*yHemrr@*?ExY1|uhU!1t-Z%d6 z@4M<~9aW_!1lL-2=<(U%YFXE5^_wJX`}yDDqdjP?bno{mZ3S5f5<-IBA+*(PF!R!ie zkhj$79{c+iz6vyR)`O$VcO-mkY=^VvV9KD1Hl<6os8|Ag(o!&JvHgu468gh7-hGFF zpq>|^fCAjnQ*h8Svb{UEPUR*D;~CeYo^ywFWgEpc`xpM@pUcOPEgVsZh(p2<&=lBa z+P*LbHTDQ_4IfR9>)h9^E9d-O_^nuLB8D9u;E@WkUd&$@7}ia^Zy6od*QfmUKHY>M zxGlZk?ie*!Q}HpdVzAcY`8(E!@PBEMZ{!#*_xY|7LD3$?J9S9AIdvGR;VFFc=fGLp zPN%@Sm6%SuwdH6E&=!c;A?nFek3GpbLno1Pe1gMQVu?r|+*7%!Mlil8eFoY6jwaBN z2C^e#8^n6gcFf$olP`$Clt-FQzKykrx#Fg{G`_N<*)>0n_hoN21A=i+%l-}U`+Y^4ml&v(Z<^*j%2F~64g#+Y}9gX8kX7xT9%qZi%fh8ErThF{%`E%24=Y6JhajnsR$ zrqrMFx4{Rt#_dpd>Uz@Fv7zC`am0P)$&A1KwYZOG?ik8$^^GIu zW-6UZ9Q)_{V}VB>+T+<^1vlt*Hgit)$FcreDRBK#VBJbwzjJew^^D6ksgr|JdCB34 z=tCsu9&YFWBfjZPy3(!$bXw$WKWK6QMpsrso*H%!56XV5I%Swf7hSL(VO&-Wcb(^ z6I-!MSH-}G??AY6lL+_WBXuwD@91$1VY*K;NJ?Kz>S(geUjz{aO8?HVJKH&q`Sp9q z_`vA36wJ1GLWhzKx64=x8*fKk`=P9Dl zmGebgNe9X_Iyp8lU*CIebse(Gro-8h3dGcS#gj zw-T2`=oJk&WdS40823w11s)!Q}mz)NR#zr}EBKUl?4Ahuj z-#Or>@xX)ia_w3Qh?-fNBxz%a6n&dovhC(K!0wq*hw;?x!~Jjlx`Zc5_L9y0Twf4D zPvKz~p60E$|2cbX8v9&dkRN~LE3fp~Uzaq?sk;)s%ZlW}Hr?E2e~Pjr3!MkP>=wNc zjuI?<*35gJc%pZX%ldQAtxN0bZf^9&-?j9-*k-xsC~nOqFhQoY$FAV_MTZLZ-H+6XUlL&VpXN`Fip2PdJWmpb5 zOr-bY*E2-K(cAHXH}rjJ@!Y_!tkrH&3and+TXfw1g zNN<*spm3oJsjd<}<$o+XL0PZoS!AG_L2y=K00jQ;dl=NcSr zygAIDo#&$qKy}TSV!oNRUp|3fojOb%}DQ`q=J{lH#_Yks-@XkJC&JJfdUJw9#a^OUy_ zX-x3iLX3rXsJ`t)q-z7;`_uF0dqm@c#)sOExfh~{c%Gz@)4ugwff*q{1xO}If?5Sl>_SK>E@BGSm-Ifr%2PTq zzY~kmiUn`c^)+KDr z`^M%vdt+H-ThJAY88*12<=7I3v{lgupH}Qs3w(Vr9{=f|zEpfUd+>H{8IZ7t@9W## z>(*n~@}ud}>FGokD%rNPq&>rGI%H(GuEQ`;JuF*937az3*WUBRd(R}8v3B~(m;Sxq zHu}B5pM&Nr-Y)@r2)8S-~jb*W;7E zk>(Mnv#^B1g)n^v&-1(V4FyK86dRuTPQoqz=yqZ z`H6Dw(^1xIasGpE{;E?34mB=zzb#!9qp#ssTH|AcP#XnBn<*uq55^lL%5d32*wRFH zUy@5K+!WYdZLnG8n8Jt$`{FuVYOGvW+bP$A4LeKhG`dA@1?+b8qvd*e`0ZD+;ak0J zf9F5>W4m#e_^|MIot>S=A;vye={Tm0SsT*&7~kFh$G@MSduur7cd6J&&nYRtqXj;! zzO;s6s22TD(zb1)2u;PbX;+S~gdgV&^=ubg;DWIa-)2kpnZ$Pga+7Do7DSAGMl3Ow zDEUq$7P$!?&mH3&?~{NZrJ%Q@_@$xa)Y-}*B;ZM!m4L5|=fU+)Jo#svJ@0%>N<2~v z%5~!KZ4x?T>R7tk$?szf@ZB!P?sbl8$3F7!~jxK+o3run)8jSoP?4 zwx!?Fw>3V-619e7Nqn9;TR}F|42x(UXqV90Vf^aE*yonkI(o+1?<#3Iya)8_EN$WD zZbhX-i}l}y&$U=r&TZ#HL#o88j?FW}rI|6P=Z#_C1-wTz6VlWry6@K97`n%L*<{py zeM&`l$0?Bo`*;S27y7bY%lY~kMBBQ7j|9JNZtk$dj&~$AO*s!a&raRo$Ztkq;a}E; zuWl~;cxXzey!VJjXaR{vz^dUd@!Rtzn$!}oSC}gWx+t)2CAz4xUTa7JY$2_LpoQGe zoXd#W5XBA@3AfL9{i$0j(nq|Yje?*84EiqH5&$ib3XyF>dq zwO-JZ4gM0^v$|j5pK*5;y{WF(>bGqM%|H&nFcaug?|*+{mrrw4BW!yd?Dm75^$xY| z^&Xvx?nLw=nw$EFZFRgyJrA@5T%jG>IcPBA+8j4&TP7l($SfYuU*NAxTget8A{s(l zmHLpWe4DL2-@BIl8|Kn_ZNF0BW~RWpmAIK__c&t)70lGpg4TIV+i}CkZ^JeHV|-|f7~`LdPo$T# zv(MVC&lsmtUIF;HrE9FcuG`tP@BKqTL_W@s!P^B^Kmxh@$4p;wG+EX=xo zJZIKP%tNe8W8lh*PH30cZ4F@b@7r?D5Az>K*E|?IR(>@2szHl+s6ehiR|>2Y=%B#5 zmFOVKt$9hrh$#x(vDAVC8{ulj8F5K)!%zI}lSQ;W zjMLbC8&{0)fd?O){i*L-J8&5bTnwwkxOYC*wX+L$eND74DX{_<_f9DX{jTfI{ViTU zcU7=Og9)t_EKbOyY-h>!}ZXM+$539a$Tq6lutoi;tOi3Qe!RVDX2HVQ3xN+^)J2aM~Po z-EV!(yM=+?W%%;7?)wUFrNH$-fpsf!J6}{mE#+LZIx5+C+c{sc$K_LcZrMdRulJIOD@xS(fZ#TRFv z%JB()+t;^N*GqgP#kO#^0ePFB3?IuaK?|HEK8839VuHR=w!jlGN_^m#TQOTz!9}C) z)bjD4{^o21l(%N-IgD{AZNY4hxXkmbnu^0Vo}ngfNY_>mE`C(Dol|jWXU)Iu-S6I8 z_vLuX^Qq6fEv+qyccpDY3DY?Zm1jF`vv=Pj*S!;_jE&Z&#_{0&e<+JWyvIUJhjw?T z_o^)%dNbmjg3mgL1C3t(%2(!;rBh(7ZB`1*rNFwCm`lN%qJ4UOexs-zGLLc-TXIlx z1(bm>mr8ZxQ{uB2_O}cL9HfS9V_S~Vn8a5vjS&K$lFMK#d?c^sn0&%S3GrD8oRJb~ zE>2R|G61fv(lbv#oySrJkZdtN5>K(%V+_ia|6xbBmiQid%UiO2d*G5|fz-U*M(3z5 z4c3cDP2ocW-@UK<@+7%G*^MjaNT1x#>A|)atzVH}%56^6?<)Bvg&`Gx9RY5Gg{kWY zJ!!UV=NUqU@zq&!Rj%vL`HimLFEy(bzjGSGIS8t@d}_*f47%ntd}-SvsPIsFR$x1S z(ir7uBH@VjRF9I&~Qdb#`{ZMbF{SJ^AEm)--K3MsI3WotsPSSvDj|u7h3^OQYRv9a3EMWJIylHX_DQ;)1Vrw%fp?%s~JX4tN}!-(`*Y z=h#vaU20tLxjr954dHw|=VRJjBUP(w&;1?aGH=#m4w$;7=MrnvdMMXK;NwO+cPvBN zZ^m?6&aqI}M_>JLv>JPiuUwmfC)Ta9q)FYcF~2u!WHUbNb3@ntHRW0>e6fDYvBbH1 zt#EbGEbzEDcRbpo*XEhtr&oz*&boj@Uk}a8K0U|G{KdL`V}WC{E>BK2bc^v`gfZp{ z+QfRx<4o_)?s;j0Z)o45Pci19?WWcUbpR}l?QQx_~vSFu-HSRcOF=OM7rXU2^ERYk*=={bCbQ&zNTV5bN_pEYXjy!=(K%Gsv9Lj+`wXRIy*UwxgS6AglZ z*j-w-^MPGFW8frEulHg$4%LCHs&i4)b;kaNbE>vwEUvZw^+ADk zD{+0y!*kFJO8$ti?e)EcLcSc9tj6&QRbKnlr!KtK%KCH8#h%ap1jQ$6Nqp?h%dssv zzOxtoJ6E}1*7!sU#lVRW(c$Bt= zL*4R+^aYD)JC^-;68vECN!5uj+#dpGNF9;PB1+V5Ov$*)R>OB=2#g_hvWP25>7}9E z;n}VYYi0=H4Y4x=@_}xaf8gp4QWj9>I3g*f0WH-NQoI&3u@a^!#$tB|pyZ6@XC5BU%woK@GS@;;Lb>Z{;Sko{2{?EPo znm4`abdI4;IYum6iBIZ5`3|tDB09zT&1_d%-(}me=lY*>J8YlF47+$9yMB+w!G|QY z5&9hck7uIqH&^c=?(`(b=x-Y3?eNfV32kb?Qg-1JZoxf4bZ@3HjDAt-w0$FsnH$66c{4)t!xwxo?DW%-qtK7H7krR*pf14!Kz?v>oI@_G7Hj8(`VA~rlyQ^lA_&iwZJ ze&;{=V;Lv6=vMBQ0zJl z8!LWohSWQ4N51imPd<6YkZ)_VQ7@Id)LIC_?sbe z*61XBw#&i`z(LcqCzhEAzi!4*nbwjPtmQVW#7ip;irl=q!u#v{e79#Goz(*4i@Mv`y<@g-OI7l;n zuGB&Ixmz@!Lxv2FJ{#MZU#v~^W63fCd@k*qVGN|Y=={8Y`?s#X_o1(8#?tiBhfOxt zS}@kWI<2nT29l^V_Oay$*VfybEsq>As{vA5ZR?W6TaE^XRJJj(o}>Kqv)?^y9Wn6X z(wdD}I@m^Aq`(#BoZ+EQ*Ej;UQV>_qKP~K(b!*G>1K;=A+21TjysE~9W{k!}c=DW< z5sAi(%~t-dv$KN%VMQ`)kPufB=betLl zw#3V)q8tZ}j=;n5T`23eIJb?s^UmL%4K-~e`rg$oaIG+IEDEe!i5qLaX7m=@PV(c( z%JTH7^SO20o#I^Yxf0I6<8?#Q9L5x((5{u)n^agc5N(@E_f^C0Ql7gu);+N99ixWK zg7{n{cyUnn+d5$b#SqVIcDm24u7)w@x6e+tGf((P24D7fo}UdD>Q8uP4HG_6uhf@W z?4fzdxDK%Fj3ThT*rnDs#(ne&`*4PGeRW!1j{u)oQPMr``##OlO zmiTBvhp?yE%OUFu4P#s}Ux5#O4r$nijHRx_r8@9L8Te==>iScEd&lK~u*LTXo_wTh zw7zrS+xjL=BmaeqQuZsbeYI!4-5<20n&cS@g5LHrL0EHibc3d~*UP zd~#-3quDYb;iLT8{7yG1UfiOsDxKgycfjQWBAtE{Anr7*DZ@EmN71@X8YFRa4h*_518aFUrCH zVysH9(4{0a`b{e1vk=*t;bdebtM8H$8=qF6z;dkjzW(*iKJQ&~B9amMBo^L(VWA2`R9BCOGt z@C7)AFYXgh&>GR)j=@#e4R}LK8c9Z3IlZ0~d(~kJTH}b{S&TFk(ZG7%+aelu9hdRF z9@x#b+Dd`ziUR9a;<}oSwKFRPRtgNKfY{}vU}IRd;n2@(%8JYVRa6vGI9t@QcE}6@ zXmdms;v3>%v=!&dxSzL)g(MSBlD_jE9X^BWP*{MFB!;BaR`@JQ7325r{$VuvZcZ<^ zl_F#D7~!A~cW2^~GF6=WaKusm7NqtVIzBqa75%&Y#h-JP2%&7Z$zYte*%kfD-+YdS zY~3zsR!c<1IfEja$*u!yj1EQh{?kO87+dadf6YdB&Nb7v>y3c+gV~Oczr_&x_X8Ss zC!SYs$-`RAtCikESFFc%%Q26RYwH`20$=>R&;Hpj`tASxi$4F6FS_wMzxH^gz)FG3 zq`*?{`Q;cG$Cc@*`|d!&mfmL$ky6@-yj~+a8B&_utKM4y=d%mecQyq5pI&Ys-}aw>AaVt;DT8 z|Lf>i3Jjot&oP4-vGW;k^)+E6p_{Ve5PA=69Sh63+R(j1Qw=c&5 zDK!Z0%l)HG8uk60UO)Ktu@hTuMfxYk&gG_ ziRNy~p^eEjEgSWm)2669By55-pP%}~U)x(l#_VA9)q|q*vK#@OH>!(V z<*f^cuoFtgUrTEQt8T16R|>2YxDF|>ZY8e6Nm;wJQsB9#fGq)|n)@IA+I(Q&u)y3G zTlY@dJ?v(%LuDzNREZDE_4c3oktROM?__D7>PeL>q=H zC3}uN&MawEI8w5^)>m*vK8fmrTVg-4o2471s5a%;oeO3>G(r}GA8iCAEz@8HTU+N> z0hgPxpuX8HIWUCo{UdxpND+NwY)~&?4 z3RViN6sRb`?Nd|}TM6#g)$Kt)1$>l+^;l(fa^Tfu+s-~*gph21f2?739}6 z@@B_*&e^c-8~CEekgk_FMd*>|r4If~BK|W!_a}FCheZQBx9`vF_qI>5jSBV?3;fgv zzT+zTI|R<$-AiooP;G%WF_tpA4?5Xy+m5#P#$BR8{lX&Jnt=C@vClL_(@|`ue}SE)(a~I zRtij}z`B)~OuDt{;UeNugO1Uew$qV|2_Z{N1dJJW|%TGB&WJ(?WGQ|1xg@^B3emyF~#Rcz8X-u3!Qfm?+F z>sI1cndx<$wXr**Iz2zw&z5IUf_5VM%=9U<^b;e)~&4 zZ-X-#${aGv!Fok{!kqr93rTV9gHL_uZY+WlV`bguyAor>s&z@Nzw*=1USX#n^|~bD z1*&|SwGUfbpiA60myv-NJVsB9o#J3x7&!|7748tl0RCWC~Kl-HP?-tdpU@&Z2iZQq{yzz0(5 z&+~)P)eu&`?!Vv1@O8DBbIq0qefq{X2Xp2$ATL0#vz`B*Vx#n{n%}RlS0wm2t3}}ibvrYwg#6p#9Sz^2D47&ob z9t-4%o#jt(2#7i!q^R2dW?0hY)*J9iW;2Yq&{O)n#}WAl zNO`f@B;0&9HX4qN8#vZojQtXvpsr}bCcV6V>^!HyO5>@0Bedgd664SA*afg0pfl0r zY=DL!nn@wYmHFAve5RLmdh9*#$?TjhRmoZh8#^ZDS=N4}J{n{6p{wM+Z=5t&?vvb` zTU%9I$*wkC*K+!_1gF+Y)OD?8ZuKPi0)jI1g1=AeRJZq=)+I^puV&3jSB2`DZ7CdC z^RZpO{6VT}y+6%2Haq*Q^wv7Rfd;gb+Sr1>@khSmC|%B5pPa<{%6=N*N>1t{zp@{quD zEo{*th9%g?E?BMydsw;|chwzoO%`u<)zWqK)qlF1GVvlm}PZx&^HSO^JGAC4nU7wnW*UzQScYB-&Pq z&lZDgZysC9_FQjk%DUybb@k4NA3kL;l*f}`h;-$!RpE!C53wa==%s!(C`}@#y+u)3~*j?#~0*4t|GO}g{wWI zo5vn~bWd83l`nH1&?>Y@uZ1PXQjrMvvC%JoKId= zv&t`~t6kqUz9rTn+YFxeN~h zi?s`BbzlA3uml_P{7*l-%P5PTWFy~Y|6jwzc42P?&t!AX&A4aSC>xL3UE7ai{mKSk|KH!-3#=P$tP}VcgB_YbfrlA74P|A+ zNG+P{B4FhxPiejxi80ojVswQ5G*N%^wv(gl&~ssnI_Dkcy&#*rkObwpmpsqm z0hUCt!d@wGol#)jN?d0XvUX>sz&r}bhRbGqY^L3*SkE)qrpQZ)mDyzD9S;rLz&L7? zefPGb>@^H<8|P4*Ia_aHw>PZ&roY;6B=N&=Zj3;*J^W`Z=z-01u8bCee^V!}c9%o( z!L>O~PD)$>HL@fhGGb9z+szzA%kDC0wkf;o=+B`sZZtB(*G#O9K5Rj&4Sn}(1up&R zy1!q0^J_!L!*d;VO1cKEjejUE7>CdvymLCloF^KlXMPN>z*qKrGwSaD`WEYWXqy_+ z;k8oNUVpC?SSfI$P+;9k+$b}(_H(7cQ3?cmhlLr+ULOUzt$qv^r{M}7UgM%US4c3lf=yKAn;;u}7`u72`o z_(+m&U!-$)?by~GA75Yl&Gn1xsH^_w*&9~3t9@LHF&=uB0~cD1o9n=`Uz*3h?NIaw zu9n-8@_h;RW__J+&o*mozn^n|S$|!44$*op&b9qYfu$+1ZY7qc@CtvWz~U5O^urBq zjA6snPUT27zw}F+=rsS&e{=QDyY4z=EEGrBtxtttwv%@tN8l+8gt+Twc5rJj#Mr59 zbLgDQFQc5=7>Zv)WI|>nQyyWU!)PJgr5Sn*@n7H;%#aC-#5hOa7@sld2?MA1yzjqY z@Ue@k6!_GA%e^nm#w_ZJX64$Uznr^ZODhIS47T`xXwQ&};S=K@a~1fx@CHm7va{dG z7Dim}DNF4tu{MmS*r6k~$l`JvnsC5%|t+8o2D9YaDs9SnxFVeBXP zFr%>&Zr^zrsA)SaJ>UkCb$S2W-gY{Kh9z$JVool@WjDr$KK!oAzASMb_#XbJ->_Tb zOW+BZIa*hmyDkI6sME5H=kJ(NYAc+cKZc!C109d;hwa=U5BF;ceq}wANcTxPX*R=3X&&lwljSve25q=N}BTkx8G$;~vg_jB>d?O;48`hw-Ptwtgd6ag(x7R3fIJsKlRip3697vagcb+U2rS|a7ixG8e5 zU^__pNIyx%;vg9#`Nk>;;#$HybWW?F4OeFDeH?;_y3UQZ&y7)V%7@>6Wy3xlLQ>p| z=szUq3=JHcya@k$mIT)ti=t^hC4Dx|`g+&zl*`}>7gd3ABlxPoo!r!o>wmuEj-#v> zxI^g~v@8SJk~Rw%npplpxZsoXLV4(qcE$Sog@5_yu1Y-@bwzAiA_%5+Y&~!j4qQqR zy!4~&LwSGdIn4zZ{W=43tQF7PP}oD zE^*H2AxE`JQfI8$5;#hjwpYfMH4o*Dh0bls;H%RJE`iRxGl&!Pwlqvz0vDLJ*v3`| z?Oh6=?GSwOnJk5)gloI&-F4`hE9=iW_lz?deCq>Wz4$YypBrPN+tPOL{g*;Zk@CF% zFT;4(_yn}RtW50iiG*l#1+Y%+{-bl)TERC3 zox~21y!%~Br=#wWl5P8IU-c!K-No{Hl?O{r3nI%d_B|U?Fsj%dSYP*t;D|c54W&Os zz=~2X2X(Q#*=VQ0r;gy40qa;vgMmH)yQ@4awm&}8B(gtz=bhPTNd3HJNp|ALaG^gI zK5)=Z(A=0ihZJt^kpN?@N_aVCTQSbJzw2E`@$C#-w(UrEOPJZ%WqZ$@mN=*2vsH|- zDQkSRFWHb~i?a-#TjT57R+at7mW7W=7d~}uHKR|Zp;(P?7(J5_*rVUFgC|I9*&pY& z=Uw=+?fQ1yo{4OfRX6VVUE*VOS68uxjsU~9qx_U;ooMu;AD-K~UEgmh^t6-FyU(~x z-j0AR=q>-7_${+NtwA6B`a0hyw0Ypc2YcJl3dR;O@I<-P*2DIRFHhi6dWU}M!3X!A z%X&O>e$ybDLkG8V=_u#GTidP_crGchZY7>e>aVz03XG-z2_pZ5I1Wi`)D_2_!3mcA z7&5TrTzD{LJC5U>Z~eNxPb|rNVnaJ&Nv~CYj*sEXhC*AGYp-2(EwkhmFvb<7n=y8% zs&e5c$>bqCaWBQ7--9K4ZFU#l_6rRXTh~d05^1@W2-saE7uK>}Y%`*6>55&}tIy^1 zh&HlM%Yor=9@iNJ)!C6VYgf>GXr}U#oVvCmiBw}pJAui>*T#k>1Cx(&TT(^D4 zzGnlVv{fv(PJz#sHM`YE;{#=hua+{CmIlnelJ;zz3+~pBBaJ1$D~i4`IFg~f3)dzO z$d{%Rn8^=l#2+7$fY)UtDdnMYGGtQ5F*N5$-4)M(bdYcO(Ld0cmNDvIOBwIud@WZB z+zb?0w-PtQEUsg?1t@^Ez&cAG<{*t1ZHI4x-z5orrl)=m15TDQZ`qgl9G7M3LTsDD z_8&_;HLGkb6)bT%x9x~*!z7nEDo(mCI?~g>@+*7ek1_O>42Ht(8f+{-X>XQ}Z^w+y zDizPP6WqTUc5O8YgK>rdRgCFv?|ye~%Rnm|CM2Bjg$AbVcV_F|PX=5=?Wy;oFr~J9 zUjnAA*7^ZoS4QUSo}BKhV@{*pp^~n5@#lY~G2H@;`o_A}t- z?bo)SrDlrHgzZboCL1SXz#Qy%+eTl;7=@JEyAq%N*jktPz^)It#sp0bpA2wsfANcV z^B3cXlQzQr@}Ql{eT|9(%kudv*Gt*l7Syx%{NMPur`F{&AOCoch8FW#);)C2DAD!+ zjo=;POL7R_v3vRbxSf-%)j8H+=Kbu>zr{C1gfj;nO^+=dT6zYTrX{1+SldgF4P8Qn z^AkV*N3YVB&!N?jwhTgPG~-({q|I9A@2o{jkbfNjoa?tk*;+a ziCjz4FE-av_t-%BT==y6%*Wxg5$935mOm*4KyAH-qmHt;-FYgwYJ8S9MJ{_&c(l_Sapo){Rn<6|# zUN;@@99yzYA70(No(_$(>z?4fjP1O0z&5XpJL6hO%}=A2u5!4yRRgb&f1oa3j8wQ$#(v>$RoQDnnGfzL=J4U%Z z%tto(I)dYvoxJBT#@Gjno3A>KzB+L|Z*$^DN9mdO9P;%kwLWFltc^*yJX_`UspoQ5 zcn9$J;FnRtA(75emlD1){t_jO`aCpLjI_Wmw?p0s=xA)mLQK?f5B(;6_FmZYq1tXO zTpRMu_(lJhR@Q(yZz*E5~x09f0u6sRb$ZY3&$tUp%@EJFc)2&BWL zT?`U@F6UrkB*VafJG8Qf#buaOFH7ALJCHw>b)^Bnu?eJ|40{3=V;jaVb%c`a@|kd{ zHx(b&Iq=kF9i(&)KI|Cwgru{^6TdSnwWX15MClGXW_)Of4Gg$S1}}9=GcX0GMc2|k zUj5pr=FrrYw*1=CWHE%mu0HnYqdNnI=npo$H15LMWS8PYNQ!%TEhjtyuZ(hpZegSl z_si0l?|uF6AN4HM_-a>wY%i&Axiy8;9E5RM(fxy`jB)Z-U^~9I{@5SBdacLSUGy!k z%xEVx0Uw)#z&maVxLneQF;PdgW|wMwW7E!!?a0lJPdE(kM{~%SDx%GerPG-D@jTq~ z_kYKz=V%O$vhJ;dZ?2JWNwY)e-c`gsm$6n3^<&Pxbt^H4dTZO20)r_)O3lc@YlBPk zSeE7(B5aJU3VU!zZAYD5L5~4BaQ}@@K6!=I6RXP&I7?+KJMGeDoAmwwxP!MCPuWtG z@EBvf=1p(f9g&tx9Z^umu)Za7QhWwrB`)V&C!?NEH1;6s|@_ zvD7pyqPavcSoh!fBVQq(z%FmYF?3jWbjonjb~SKFmnLJjEn!@%OHsMH+*1$XBss3( z(E3D#F_D|F^mQFHAB#&ra>}^vy2H}wbFGPB8=ocS+qGe<6xqfPjiu{eiSJO5TVtW6 znHnS*Zo!B7@C2=B&bs>4>KZ9{;8IQvIy|&LqF@C+#*Is7F)lNn_i1v5_I*e_>oLZc ze8v`?JN*e<-t~;(hV*e>ovj#*Z^kA3#rTK-XlR#48Sg3X|K6iZ<0D@5&N!wGU%DSH z;ICVW7F1ketQ6>_fX_4b8*A5FX~Yfiu(B`vyT2vFtX(-bY_611w@qdZzz7`r4ptoe zA&;+q|_) zbFZE82D=u0+0btvN=ft0`TqUxU$<@Cio0)~1k5gc(U)`j_W;>m-oOZ&bl2Ui_e9S$ z=DG%YpW_1O@ZS)8QKySuaqk@Ge0(5m*89chY%0F4zLz}>y1~1_vAH`IU(C(1capUl zV+@?ubo8U^c`5rF=dr%ZKF7VfEODOCSFE+P#s|Dd1K-ehgMLMu66cWq>i>D$dNhZB z8>?ezKhL2Ze%8DxY2P-We?6k zMF7*5gdnY!BuB}|q6I#RUfHjWA>h+W2#CrSo*|X%+z3;jMvakVk{<)* zt_VhVbFogw0f9$JN}mF>?#3c2P)ICFc(f~kH9Il zVr8Z9M`Dl`_&hJ#k-U%bTi^#kqX*8tuYY~#It<+W6GAIE)uvz8YPe{|1hWpA483jntLaImN zxTK_obPIf?0fgi*e$N>cDUop;g0&f2w-kB|ZgAj2FGnBFkgWFIAC7aQ*#IZ-)-dDV zhNi?$lGY*gu3>lm4)~?iGbE_S;(n}?P2DaiW3OG5U>e^>;=a-P)Z-nC zVZZ)GP164Tw!dPHw1wIaE#|AgM%z1=j&G+CXgiKRXx5%N?7QFkI(lws+8S~{YeoMrbM9KwYu!q;q~;21rND_;`Rt}k`$rlBuOxXP z#!Bsjq%;t0?YXqF!+rRlenU=GC0!cH8%b+KHX(^7U3Z-gzUWT@XK2aBUHiU+OCi_p zfJXu!b*H?3`Hn>UuJWwXK9F>WzXV!DKZceiK6d+ZHjsD+J?oTR;L1;+Z?%Ma2+k5; zxEZtm4SZcWBf^Jc3O%K23p$>?=tWuLoF&##1-pgKk0knz!s?eG$WsliTY*c?E>V%Q~piSzQpI5D}4Z> zj*{*C_k8d3M)|r#C%EMYtU7Ou+!?ei?|9^!@^<^4yMOmpQL~O|76Nux8CpI#`?7(1 z-s2t91!K&$(yt(|y)lhP)}<8+S}K>uM^jsBBbUa990KMV+a_c}cQEUbWII@0NQ$88 zWsEQ0BYggjMOQo6@qssKmyB;{deDJjd#mx~%6X!m{>ulogS`Nr;~VQ%;yIpx75&Xb z0ZMyrnq{ORu9FNa7+JI&t5jlA>0tjDS_EvkMV#@`Jj?IA%#%Qoe?IQ{jygO3qYSuf7Bi|>r_!BwU$1Q)(n{NQixq}j_~`9*_* zq(=XSl%5BMR%ln&H@DaVYpT3H_x$sJ^?%9RuWYcheZI@}JH{9;`eHrGoe;n4(iY0| zW;|#f<5Jf2uiMtY^E)ZgcL+T~|k*STB9=|B;{uRflhEdeuRIpMy&4 zR^mAz`ik;qr2so-8i2m&4+3(w8hZ{Bxn%?1@Q=Q7?{!N7EE-T|jZ?}p{uFaw$8eE$ z1$&S6D77nWs53jV?T_;*5taCs$ef&6KCpuQOXnrNpkY=9r+ZO9t`8~sWyqkauMT#{ z<@mbtA;1^)l|%4Eovg{&(uYQehJd|uz!PLN>P9&g9|>0knl-Kn+M8LO?Rep>SuE+_ zqCaECCm%_kgXFd@MPBq@S!NnGpZtc<_AF@<%K7C}RJJ>G4qxtHqNgB=WnG#cu;TvS zc((L+Ei^S{+pcr)L?a7$H9l?BiNM5N_@sZMN$7%KU!x5R7--ssFWN{ISFRyaP2(Dh zD;tU=9P#|{`xuflCGd|ev!<+Ae^a!3V{p;}2Q8ILN5m@MAD4kIZ^?@JtGCc&Xf>p+ zG^9DgSfXX#rVVK{vyOKejrY(sJrmv9d?E$bt;9sat<6>n>{EbIkPQmU~1-VXa!8wOy3?>hUe z(=2UGpD@xe#t`xb2sCOd0AG%)J`BaKmQ2492veFg1|EWAssJCjl+En^xoNxZ^TMqa z3zUqLMs3riMFaDx4}3@3ru@Y#rQt^4D}4;$^JgBl8>3}1j&N_yxhXcF@_&X}IX6g> zrq_ylF6ms|jgNs_(d^)rFpxQ-0&0ebXLYXa(Zg$eN^WU%0}?99UaL7Z09V8)1y&8Sknb z>xCG>c(HWWWzcdAi|5(S?b7)A@X1}_nU8u=qVd?=0=7BYvw80e&gz|lb>u=Xv;z8? z^C>p#_rC7SGrl3|5`)(DI*sc?%2*3ogEXYUj5_G#S+!ouY~C|K9&E~>&D?q4AFSBu zX5dR}{8I59%3$AUXc-Lc8+u+AK1gi`?>aj>Z8?trpS}AJx^FuR!hTg$f*q?^Eg}kb z)c#QGOl>=&FZIfumF15G~ZeUTeXMv`osVD z561RtweN7&esOlH&*!egzrKv6;>qi|IkIO**7+wt_TL}HM??sJ={>$5`q3|Xl65Cx zIOpCAQ~kxd)w)x4Y8gvd)|cIgCF(1ldFB8k_8eIdN7AyCFe>b+>nm1;{n#3-9Q3I8 zu$@!wvTQBBh+(hpZLfZ+!+57+@)tSSNV;A-Q!~kWFf8M{t~RwXTCw!ZGW6&ogT%#)qDIX zu!$0n-<)qFz7q=klArif4}9jZ#x(U)Y=%$isfiS`(+~Gnhkp0uUK^}FM-YxlG#_&s z&V*afLdR)WSx;ie=k~e1!G^Va+mO}J@@yYTI@@zv%Pdc;754z9+zvZ%{#y0 zvk$nZvJ3_5wQsY8$20AFx+UIBqS?fo1Isyu?}k&s5riyMJ>6(6X`Zubge>m+-{Uh6 zp|NkzW9Q~@Wu9M`(1&xd;8aa{edRey}fyrYk}6+6XFByeCzk2?G!#*ZDMZD#b^epu$rJ} z>nBzi&OTA_6h5MU;zFCzWWH70nR6hi_&7E?5*Mq>T;g4Q@Bdo*h7fmS4x8xOKX=c0 zD}SGfC(ozs#O?jKgbz;QP0Vqly}H)0ck9FU?(=-jU&Ng>7(}t9c7FFPCejCRC_Yu53*gulT)p8}gGaXt+<2pa_&1%!5r zm1qX_JALi3K(GS%TwxutjZ0ygr%n#-U05F)9!?Fi?9d`|WIAUj>#6==p|L@BD86di z?&=(1yvOiixu@VxmC@F*bjWzpJKU%bAJ$RM{i*iKh)JWn*`~wF_x->ROv1y~n#ZNx zYV|o-u(=MEa`6wwh6OCehf7*YS-qA-Jz{W(C5Kk?7uYXaQgMNa{~)Y!m}{rHbRutgN> zgh5H%>Rtj-&KV5Zml$l-niD7ZO@o^nVPy{YSB)*#zfMRC!u!A~e6NU~SsQuE!-gvoX(YLD+%0ahv$446xHT9`0{A z6-hKJLzZjvxyJHXVJ1Ffs18rH&Lyw!8(*qk?qhtfd*v$+zX$KcKK|*FbOb9BVefwn z_YuPoX%+VPh=!LQPld+;G_A~@@6uztYj~R| zan}^y;6LINkTHed_tG%2E^qs$Z)#SHYe$Tw`g6}Xjxc8XL`}U`Wrz~a7)~wibuZ+0 zs@@#@S`HU0LB3k2_F0I~$^B>LP_JcR8r|(xh83Se2V7Xh_kZZ~0;kt&?GMX?@MJ8S zJsRHrj(3EAIJ}WTs8*i1ZY)ucIA*5hO4=neT_{gIEnH0e5tq!cE;XD1vvu-CN7bw7ovkn+%mw?4<` zyTG2hT*5cnZz%|NwEw4zzaRK_NfV!H^!%1vbK!(5%L>zw18 z%et0XtNL}CFq%aq#x@iuF@O(C%9`dk(#Jv&t6BAkZKDoO$Hr$IoDW$zSOhZTm42kt zA&Beb&?pD50;Dt_sO_X?{&(PMt7bpd2bFnpfeAVChx8w82ks
_^IcEBWLrFeZ;CaQ6gBC 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 sJOO(0vGo?~&w4;li2wiq diff --git a/paddle/trainer/tests/sample_trainer_config_opt_a.conf b/paddle/trainer/tests/sample_trainer_config_opt_a.conf index b1744db8d6..8ece96f595 100644 --- a/paddle/trainer/tests/sample_trainer_config_opt_a.conf +++ b/paddle/trainer/tests/sample_trainer_config_opt_a.conf @@ -15,12 +15,16 @@ from paddle.trainer_config_helpers import * ################################### Data Configuration ################################### -TrainData(ProtoData(files = "trainer/tests/mnist.list")) +TrainData(SimpleData( + files = "trainer/tests/sample_filelist.txt", + feat_dim = 3, + context_len = 0, + buffer_capacity = 1000000)) ################################### Algorithm Configuration ################################### settings(batch_size = 1000, learning_method = MomentumOptimizer(momentum=0.5, sparse=False)) ################################### Network Configuration ################################### -data = data_layer(name ="input", size=784) +data = data_layer(name ="input", size=3) fc1 = fc_layer(input=data, size=800, bias_attr=True, diff --git a/paddle/trainer/tests/sample_trainer_config_opt_b.conf b/paddle/trainer/tests/sample_trainer_config_opt_b.conf index b1744db8d6..8ece96f595 100644 --- a/paddle/trainer/tests/sample_trainer_config_opt_b.conf +++ b/paddle/trainer/tests/sample_trainer_config_opt_b.conf @@ -15,12 +15,16 @@ from paddle.trainer_config_helpers import * ################################### Data Configuration ################################### -TrainData(ProtoData(files = "trainer/tests/mnist.list")) +TrainData(SimpleData( + files = "trainer/tests/sample_filelist.txt", + feat_dim = 3, + context_len = 0, + buffer_capacity = 1000000)) ################################### Algorithm Configuration ################################### settings(batch_size = 1000, learning_method = MomentumOptimizer(momentum=0.5, sparse=False)) ################################### Network Configuration ################################### -data = data_layer(name ="input", size=784) +data = data_layer(name ="input", size=3) fc1 = fc_layer(input=data, size=800, bias_attr=True, From 3a507b44bdf41f082145e8c028adfb976c8571ac Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Mon, 13 Nov 2017 17:55:08 +0800 Subject: [PATCH 04/43] add conv3d_trans_cudnn_op --- paddle/operators/CMakeLists.txt | 33 +++++++++++-------- ...cudnn_op.cc => conv_transpose_cudnn_op.cc} | 11 +++++++ ...cudnn_op.cu => conv_transpose_cudnn_op.cu} | 5 +++ 3 files changed, 36 insertions(+), 13 deletions(-) rename paddle/operators/{conv2d_transpose_cudnn_op.cc => conv_transpose_cudnn_op.cc} (82%) rename paddle/operators/{conv2d_transpose_cudnn_op.cu => conv_transpose_cudnn_op.cu} (97%) diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 709f7de2e4..71740b8b0c 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -55,6 +55,18 @@ function(op_library TARGET) set(pybind_flag 1) endif() + if ("${TARGET}" STREQUAL "compare_op") + set(pybind_flag 1) + file(APPEND ${pybind_file} "USE_OP(less_than);\nUSE_OP(equal);\n") + endif() + + # conv_op contains several operators + if ("${TARGET}" STREQUAL "conv_op") + set(pybind_flag 1) + # It's enough to just adding one operator to pybind + file(APPEND ${pybind_file} "USE_OP(conv2d);\n") + endif() + # pool_op contains several operators if ("${TARGET}" STREQUAL "pool_op") set(pybind_flag 1) @@ -62,9 +74,11 @@ function(op_library TARGET) file(APPEND ${pybind_file} "USE_OP(pool2d);\n") endif() - if ("${TARGET}" STREQUAL "compare_op") + # pool_cudnn_op contains several operators + if ("${TARGET}" STREQUAL "pool_cudnn_op") set(pybind_flag 1) - file(APPEND ${pybind_file} "USE_OP(less_than);\nUSE_OP(equal);\n") + # It's enough to just adding one operator to pybind + file(APPEND ${pybind_file} "USE_OP(pool2d_cudnn);\n") endif() # pool_with_index_op contains several operators @@ -74,25 +88,18 @@ function(op_library TARGET) file(APPEND ${pybind_file} "USE_OP(max_pool2d_with_index);\n") endif() - # conv_op contains several operators - if ("${TARGET}" STREQUAL "conv_op") - set(pybind_flag 1) - # It's enough to just adding one operator to pybind - file(APPEND ${pybind_file} "USE_OP(conv2d);\n") - endif() - # conv_transpose_op contains several operators if ("${TARGET}" STREQUAL "conv_transpose_op") set(pybind_flag 1) # It's enough to just adding one operator to pybind file(APPEND ${pybind_file} "USE_OP(conv2d_transpose);\n") endif() - - # pool_cudnn_op contains several operators - if ("${TARGET}" STREQUAL "pool_cudnn_op") + + # conv_transpose_cudnn_op contains two operators + if ("${TARGET}" STREQUAL "conv_transpose_cudnn_op") set(pybind_flag 1) # It's enough to just adding one operator to pybind - file(APPEND ${pybind_file} "USE_OP(pool2d_cudnn);\n") + file(APPEND ${pybind_file} "USE_OP(conv2d_transpose_cudnn);\n") endif() # save_restore_op contains several operators diff --git a/paddle/operators/conv2d_transpose_cudnn_op.cc b/paddle/operators/conv_transpose_cudnn_op.cc similarity index 82% rename from paddle/operators/conv2d_transpose_cudnn_op.cc rename to paddle/operators/conv_transpose_cudnn_op.cc index fce1357ce5..7ec3319cd0 100644 --- a/paddle/operators/conv2d_transpose_cudnn_op.cc +++ b/paddle/operators/conv_transpose_cudnn_op.cc @@ -48,3 +48,14 @@ REGISTER_OP_CPU_KERNEL( REGISTER_OP_CPU_KERNEL( conv2d_transpose_cudnn_grad, ops::GemmConvTransposeGradKernel); + +REGISTER_OP(conv3d_transpose_cudnn, ops::ConvTransposeOp, + ops::CudnnConv3DTransposeOpMaker, conv3d_transpose_cudnn_grad, + ops::ConvTransposeOpGrad); + +REGISTER_OP_CPU_KERNEL( + conv3d_transpose_cudnn, + ops::GemmConvTransposeKernel); +REGISTER_OP_CPU_KERNEL( + conv3d_transpose_cudnn_grad, + ops::GemmConvTransposeGradKernel); diff --git a/paddle/operators/conv2d_transpose_cudnn_op.cu b/paddle/operators/conv_transpose_cudnn_op.cu similarity index 97% rename from paddle/operators/conv2d_transpose_cudnn_op.cu rename to paddle/operators/conv_transpose_cudnn_op.cu index 694526ec01..cd31896f2c 100644 --- a/paddle/operators/conv2d_transpose_cudnn_op.cu +++ b/paddle/operators/conv_transpose_cudnn_op.cu @@ -237,3 +237,8 @@ REGISTER_OP_GPU_KERNEL(conv2d_transpose_cudnn, ops::CudnnConvTransposeOpKernel); REGISTER_OP_GPU_KERNEL(conv2d_transpose_cudnn_grad, ops::CudnnConvTransposeGradOpKernel); + +REGISTER_OP_GPU_KERNEL(conv3d_transpose_cudnn, + ops::CudnnConvTransposeOpKernel); +REGISTER_OP_GPU_KERNEL(conv3d_transpose_cudnn_grad, + ops::CudnnConvTransposeGradOpKernel); From 6fb4bb8efea3c21ef33b8568069c1cbc2a38a381 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Mon, 13 Nov 2017 17:58:44 +0800 Subject: [PATCH 05/43] add conv3d_trans_cudnn_op unit test --- paddle/operators/conv_transpose_cudnn_op.cc | 19 ++++++++++++++++++- .../tests/test_conv3d_transpose_op.py | 6 ++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/paddle/operators/conv_transpose_cudnn_op.cc b/paddle/operators/conv_transpose_cudnn_op.cc index 7ec3319cd0..dbd1bc3c3b 100644 --- a/paddle/operators/conv_transpose_cudnn_op.cc +++ b/paddle/operators/conv_transpose_cudnn_op.cc @@ -23,7 +23,24 @@ class CudnnConv2DTransposeOpMaker : public Conv2DTransposeOpMaker { framework::OpAttrChecker* op_checker) : Conv2DTransposeOpMaker(proto, op_checker) { AddAttr>("dilations", "dilations of convolution operator.") - .SetDefault(std::vector{1, 1}); + .SetDefault({1, 1}); + AddAttr("workspace_size_MB", + "workspace size for cudnn, in MB, " + "workspace is a section of GPU memory which will be " + "allocated/freed each time the operator runs, larger " + "workspace size can increase performance but also requires " + "better hardward. This size should be carefully setted.") + .SetDefault(4096); + } +}; + +class CudnnConv3DTransposeOpMaker : public Conv3DTransposeOpMaker { + public: + CudnnConv3DTransposeOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : Conv3DTransposeOpMaker(proto, op_checker) { + AddAttr>("dilations", "dilations of convolution operator.") + .SetDefault({1, 1, 1}); AddAttr("workspace_size_MB", "workspace size for cudnn, in MB, " "workspace is a section of GPU memory which will be " diff --git a/python/paddle/v2/framework/tests/test_conv3d_transpose_op.py b/python/paddle/v2/framework/tests/test_conv3d_transpose_op.py index 132fe79314..73ee260c5a 100644 --- a/python/paddle/v2/framework/tests/test_conv3d_transpose_op.py +++ b/python/paddle/v2/framework/tests/test_conv3d_transpose_op.py @@ -93,5 +93,11 @@ class TestConv3dTransposeOp(OpTest): self.op_type = "conv3d_transpose" +# ------------ test_cudnn ------------ +class TestCudnn(TestConv3dTransposeOp): + def init_op_type(self): + self.op_type = "conv3d_transpose_cudnn" + + if __name__ == '__main__': unittest.main() From b103072dc805ec74727fae37492a5e6d184e6992 Mon Sep 17 00:00:00 2001 From: guosheng Date: Sat, 11 Nov 2017 10:00:29 +0800 Subject: [PATCH 06/43] Fix data order of H0 in GRU Operator --- paddle/operators/gru_op.h | 49 +++++++++++++------ .../paddle/v2/framework/tests/test_gru_op.py | 18 ++++--- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/paddle/operators/gru_op.h b/paddle/operators/gru_op.h index ba90ec9816..b2cf358994 100644 --- a/paddle/operators/gru_op.h +++ b/paddle/operators/gru_op.h @@ -14,6 +14,7 @@ #pragma once +#include "paddle/operators/lstm_op.h" #include "paddle/operators/math/gru_compute.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/sequence2batch.h" @@ -24,20 +25,12 @@ namespace paddle { namespace operators { -using Tensor = framework::Tensor; -using LoDTensor = framework::LoDTensor; - -template -using EigenMatrix = framework::EigenMatrix; - template class GRUKernel : public framework::OpKernel { public: void BatchCompute(const framework::ExecutionContext& context) const { auto* input = context.Input("Input"); auto* h0 = context.Input("H0"); - const T* h0_data = h0 ? h0->data() : nullptr; auto* weight = context.Input("Weight"); const T* weight_data = weight->data(); auto* bias = context.Input("Bias"); @@ -74,7 +67,18 @@ class GRUKernel : public framework::OpKernel { gru_value.gateWeight = const_cast(weight_data); gru_value.stateWeight = const_cast(weight_data + 2 * frame_size * frame_size); - gru_value.prevOutValue = const_cast(h0_data); + Tensor ordered_h0; + const size_t* order = batch_gate->lod()[2].data(); + if (h0) { + // Since the batch computing for GRU reorders the input sequences + // according to their length. The initialized cell state also needs + // to reorder. + ReorderInitState(context.device_context(), *h0, order, + &ordered_h0, true); + gru_value.prevOutValue = ordered_h0.data(); + } else { + gru_value.prevOutValue = nullptr; + } auto batch_starts = batch_gate->lod()[0]; size_t num_batch = batch_starts.size() - 1; for (size_t n = 0; n < num_batch; n++) { @@ -110,7 +114,6 @@ class GRUGradKernel : public framework::OpKernel { public: void BatchCompute(const framework::ExecutionContext& context) const { auto* h0 = context.Input("H0"); - const T* h0_data = h0 ? h0->data() : nullptr; auto* weight = context.Input("Weight"); const T* weight_data = weight->data(); auto* batch_gate = context.Input("BatchGate"); @@ -143,6 +146,16 @@ class GRUGradKernel : public framework::OpKernel { zero(context.device_context(), &batch_reset_hidden_prev_grad, static_cast(0.0)); + Tensor ordered_h0, ordered_h0_grad; + const size_t* order = batch_gate->lod()[2].data(); + if (h0) { + ReorderInitState(context.device_context(), *h0, order, + &ordered_h0, true); + } + if (h0_grad) { + ordered_h0_grad.mutable_data(h0_grad->dims(), context.GetPlace()); + } + bool is_reverse = context.Attr("is_reverse"); batch_hidden_grad.set_lod(batch_hidden->lod()); to_batch(context.device_context(), *hidden_grad, batch_hidden_grad, false, @@ -185,11 +198,13 @@ class GRUGradKernel : public framework::OpKernel { batch_reset_hidden_prev_grad.Slice(bstart, bend); gru_grad.resetOutputGrad = reset_hidden_prev_grad_t.data(); if (n == 0) { - gru_value.prevOutValue = const_cast(h0_data); - if (h0_grad) { - T* h0_grad_data = h0_grad->mutable_data(context.GetPlace()); - zero(context.device_context(), h0_grad, static_cast(0.0)); - gru_grad.prevOutGrad = h0_grad_data; + if (h0) { + gru_value.prevOutValue = ordered_h0.data(); + } else { + gru_value.prevOutValue = nullptr; + } + if (h0 && h0_grad) { + gru_grad.prevOutGrad = ordered_h0_grad.data(); } else { gru_grad.prevOutGrad = nullptr; } @@ -220,6 +235,10 @@ class GRUGradKernel : public framework::OpKernel { auto place = context.GetEigenDevice(); d_b.device(place) = d_g.sum(Eigen::array({{0}})); } + if (h0 && h0_grad) { + ReorderInitState(context.device_context(), ordered_h0_grad, + order, h0_grad, false); + } } void Compute(const framework::ExecutionContext& context) const override { diff --git a/python/paddle/v2/framework/tests/test_gru_op.py b/python/paddle/v2/framework/tests/test_gru_op.py index b2474cff94..2bb78d10e0 100644 --- a/python/paddle/v2/framework/tests/test_gru_op.py +++ b/python/paddle/v2/framework/tests/test_gru_op.py @@ -6,7 +6,8 @@ from test_lstm_op import identity, sigmoid, tanh, relu class TestGRUOp(OpTest): - batch_size = 9 + lod = [[0, 2, 6, 9]] + batch_size = lod[0][-1] frame_size = 5 activate = { 'identity': identity, @@ -35,7 +36,7 @@ class TestGRUOp(OpTest): seq_starts[sorted_seqs[i]] + batch_idx) idx_in_seq.append(idx) idx_in_seq_list.append(idx_in_seq) - return idx_in_seq_list + return idx_in_seq_list, sorted_seqs def gru_step(self, x, h_p, w, b): batch_size = x.shape[0] @@ -66,8 +67,8 @@ class TestGRUOp(OpTest): batch_hidden = self.outputs['BatchHidden'] hidden = self.outputs['Hidden'] idx_in_seq_list = self.idx_in_seq_list - h_p = self.inputs['H0'] if self.inputs.has_key('H0') else np.zeros( - (len(idx_in_seq_list[0]), self.frame_size)) + h_p = self.inputs['H0'][self.sorted_seqs] if self.inputs.has_key( + 'H0') else np.zeros((len(idx_in_seq_list[0]), self.frame_size)) num_batch = len(idx_in_seq_list) end_idx = 0 for batch_idx in range(num_batch): @@ -84,8 +85,9 @@ class TestGRUOp(OpTest): return batch_gate, batch_reset_hidden_prev, hidden def set_data(self): - lod = [[0, 2, 6, self.batch_size]] - self.idx_in_seq_list = self.seq_to_batch(lod, self.is_reverse) + lod = self.lod + self.idx_in_seq_list, self.sorted_seqs = self.seq_to_batch( + lod, self.is_reverse) batch_size = self.batch_size frame_size = self.frame_size input = np.random.rand(batch_size, frame_size * 3).astype('float64') @@ -146,8 +148,8 @@ class TestGRUOpReverse(TestGRUOp): def set_confs(self): self.is_reverse = True self.attrs = { - 'activation': 'identity', - 'gate_activation': 'sigmoid', + 'activation': 'tanh', + 'gate_activation': 'tanh', 'is_reverse': self.is_reverse } From cd6d69a95fb1c71aed1d4ada065d91baa61ddffa Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Tue, 14 Nov 2017 20:36:47 +0800 Subject: [PATCH 07/43] modify the test config for test_CompareSparse.cpp --- paddle/gserver/tests/CMakeLists.txt | 12 ++ paddle/gserver/tests/sequence_lstm.conf | 64 ++++++++ .../tests/test_CompareSparse.cpp | 3 +- paddle/trainer/tests/CMakeLists.txt | 10 -- .../sample_trainer_config_compare_sparse.conf | 154 ------------------ 5 files changed, 77 insertions(+), 166 deletions(-) create mode 100644 paddle/gserver/tests/sequence_lstm.conf rename paddle/{trainer => gserver}/tests/test_CompareSparse.cpp (98%) delete mode 100644 paddle/trainer/tests/sample_trainer_config_compare_sparse.conf diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index 232fa01568..0ce7ee208b 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -95,3 +95,15 @@ add_test(NAME test_PyDataProvider2 COMMAND .set_python_path.sh -d ${PADDLE_SOURCE_DIR}/paddle/gserver/tests:${PADDLE_SOURCE_DIR}/python ${CMAKE_CURRENT_BINARY_DIR}/test_PyDataProvider2 WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle ) + +################# test_CompareSparse ################## +add_unittest_without_exec(test_CompareSparse + test_CompareSparse.cpp) +if(NOT ON_TRAVIS) + add_test(NAME test_CompareSparse + COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests + ./.set_port.sh -p port -n 6 + ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) +endif() diff --git a/paddle/gserver/tests/sequence_lstm.conf b/paddle/gserver/tests/sequence_lstm.conf new file mode 100644 index 0000000000..f49a827f22 --- /dev/null +++ b/paddle/gserver/tests/sequence_lstm.conf @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle.trainer_config_helpers import * + +######################## data source ################################ +dict_path = 'gserver/tests/Sequence/tour_dict_phrase.dict' +dict_file = dict() +for line_count, line in enumerate(open(dict_path, "r")): + dict_file[line.strip()] = line_count + +define_py_data_sources2( + train_list='gserver/tests/Sequence/train.list', + test_list=None, + module='sequenceGen', + obj='process', + args={"dict_file": dict_file}) + +settings(batch_size=5) +######################## network configure ################################ +dict_dim = len(open(dict_path, 'r').readlines()) +word_dim = 128 +hidden_dim = 256 +label_dim = 3 +sparse_update = get_config_arg("sparse_update", bool, False) + +data = data_layer(name="word", size=dict_dim) + +emb = embedding_layer( + input=data, + size=word_dim, + param_attr=ParamAttr(sparse_update=sparse_update)) + +with mixed_layer(size=hidden_dim * 4) as lstm_input: + lstm_input += full_matrix_projection(input=emb) + +lstm = lstmemory( + input=lstm_input, + act=TanhActivation(), + gate_act=SigmoidActivation(), + state_act=TanhActivation()) + +lstm_last = last_seq(input=lstm) + +with mixed_layer( + size=label_dim, act=SoftmaxActivation(), bias_attr=True) as output: + output += full_matrix_projection(input=lstm_last) + +outputs( + classification_cost( + input=output, label=data_layer( + name="label", size=1))) diff --git a/paddle/trainer/tests/test_CompareSparse.cpp b/paddle/gserver/tests/test_CompareSparse.cpp similarity index 98% rename from paddle/trainer/tests/test_CompareSparse.cpp rename to paddle/gserver/tests/test_CompareSparse.cpp index 5f1834bd73..c6e07650fc 100644 --- a/paddle/trainer/tests/test_CompareSparse.cpp +++ b/paddle/gserver/tests/test_CompareSparse.cpp @@ -22,8 +22,7 @@ limitations under the License. */ using namespace paddle; // NOLINT using namespace std; // NOLINT -static const string& configFile1 = - "trainer/tests/sample_trainer_config_compare_sparse.conf"; +static const string& configFile1 = "gserver/tests/sequence_lstm.conf"; DECLARE_bool(use_gpu); DECLARE_string(config); diff --git a/paddle/trainer/tests/CMakeLists.txt b/paddle/trainer/tests/CMakeLists.txt index f01ad4142d..441df2b57b 100644 --- a/paddle/trainer/tests/CMakeLists.txt +++ b/paddle/trainer/tests/CMakeLists.txt @@ -47,16 +47,6 @@ add_test(NAME test_CompareTwoOpts --num_passes=1 --need_high_accuracy=0 WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) -################# test_CompareSparse ################## -add_unittest_without_exec(test_CompareSparse - test_CompareSparse.cpp) -if(NOT ON_TRAVIS) - add_test(NAME test_CompareSparse - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ./.set_port.sh -p port -n 6 - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) -endif() ################# test_recurrent_machine_generation ############### add_unittest_without_exec(test_recurrent_machine_generation test_recurrent_machine_generation.cpp) diff --git a/paddle/trainer/tests/sample_trainer_config_compare_sparse.conf b/paddle/trainer/tests/sample_trainer_config_compare_sparse.conf deleted file mode 100644 index 92f32a18c0..0000000000 --- a/paddle/trainer/tests/sample_trainer_config_compare_sparse.conf +++ /dev/null @@ -1,154 +0,0 @@ -#edit-mode: -*- python -*- -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#Todo(luotao02) This config is only used for unitest. It is out of date now, and will be updated later. - -# Note: when making change to this file, please make sure -# sample_trainer_config_rnn.conf is changed accordingly so that the uniitest -# for comparing these two nets can pass (test_CompareTwoNets) - -default_initial_std(0.1) -default_device(0) - -word_dim = 999 -l1 = 0 -l2 = 0 - -model_type("nn") - -sparse_update = get_config_arg("sparse_update", bool, False) - -TrainData(ProtoData( - type = "proto_sequence", - files = ('trainer/tests/train_sparse.list'), - )) - -Settings( - algorithm='sgd', - batch_size=100, - learning_rate=0.0001, - learning_rate_decay_a=4e-08, - learning_rate_decay_b=0.0, - learning_rate_schedule='poly', -) - - -wordvec_dim = 32 -layer2_dim = 16 -layer3_dim = 16 -hidden_dim = 32 - -slot_names = ["qb", "qw", "tb", "tw"] - -def ltr_network(network_name, - word_dim=word_dim, - wordvec_dim=wordvec_dim, - layer2_dim=layer2_dim, - layer3_dim=layer3_dim, - hidden_dim=hidden_dim, - slot_names=slot_names, - l1=l1, - l2=l2): - - slotnum = len(slot_names) - for i in xrange(slotnum): - Inputs(slot_names[i] + network_name) - for i in xrange(slotnum): - Layer( - name = slot_names[i] + network_name, - type = "data", - size = word_dim, - device = -1, - ) - Layer( - name = slot_names[i] + "_embedding_" + network_name, - type = "mixed", - size = wordvec_dim, - bias = False, - device = -1, - inputs = TableProjection(slot_names[i] + network_name, - parameter_name = "embedding.w0", - decay_rate_l1=l1, - sparse_remote_update = True, - sparse_update = sparse_update, - ), - ) - Layer( - name = slot_names[i] + "_rnn1_" + network_name, - type = "recurrent", - active_type = "tanh", - bias = Bias(initial_std = 0, - parameter_name = "rnn1.bias"), - inputs = Input(slot_names[i] + "_embedding_" + network_name, - parameter_name = "rnn1.w0") - ) - Layer( - name = slot_names[i] + "_rnnlast_" + network_name, - type = "seqlastins", - inputs = [ - slot_names[i] + "_rnn1_" + network_name, - ], - ) - - Layer( - name = "layer2_" + network_name, - type = "fc", - active_type = "tanh", - size = layer2_dim, - bias = Bias(parameter_name = "layer2.bias"), - inputs = [Input(slot_name + "_rnnlast_" + network_name, - parameter_name = "_layer2_" + slot_name + ".w", - decay_rate = l2, - initial_smart = True) for slot_name in slot_names] - ) - Layer( - name = "layer3_" + network_name, - type = "fc", - active_type = "tanh", - size = layer3_dim, - bias = Bias(parameter_name = "layer3.bias"), - inputs = [ - Input("layer2_" + network_name, - parameter_name = "_layer3.w", - decay_rate = l2, - initial_smart = True), - ] - ) - Layer( - name = "output_" + network_name, - type = "fc", - size = 1, - bias = False, - inputs = [ - Input("layer3_" + network_name, - parameter_name = "_layerO.w"), - ], - ) - - -ltr_network("left") -ltr_network("right") -Inputs("label") -Layer( - name = "label", - type = "data", - size = 1, - ) -Outputs("cost", "qb_rnnlast_left") -Layer( - name = "cost", - type = "rank-cost", - inputs = ["output_left", "output_right", "label"], - ) From 74912c7d4ed83c78c4c3076d306fae3923c5432f Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Wed, 15 Nov 2017 15:37:40 +0800 Subject: [PATCH 08/43] fix data layout --- paddle/operators/conv_transpose_cudnn_op.cu | 20 +++++++++++++------- paddle/platform/cudnn_helper.h | 13 +++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/paddle/operators/conv_transpose_cudnn_op.cu b/paddle/operators/conv_transpose_cudnn_op.cu index cd31896f2c..00e0ec255d 100644 --- a/paddle/operators/conv_transpose_cudnn_op.cu +++ b/paddle/operators/conv_transpose_cudnn_op.cu @@ -54,15 +54,21 @@ class CudnnConvTransposeOpKernel : public framework::OpKernel { ScopedTensorDescriptor output_desc; ScopedFilterDescriptor filter_desc; ScopedConvolutionDescriptor conv_desc; - DataLayout layout = DataLayout::kNCHW; + DataLayout layout; + + if (strides.size() == 2U) { + layout = DataLayout::kNCHW; + } else { + layout = DataLayout::kNCDHW; + } - // N, M, H, W + // (N, M, H, W) or (N, M, D, H, W) cudnnTensorDescriptor_t cudnn_input_desc = input_desc.descriptor( layout, framework::vectorize2int(input->dims())); - // N, C, O_h, O_w + // (N, C, O_h, O_w) or (N, C, O_d, O_h, O_w) cudnnTensorDescriptor_t cudnn_output_desc = output_desc.descriptor( layout, framework::vectorize2int(output->dims())); - // M, C, K_h, K_w + // (M, C, K_h, K_w) or (M, C, K_d, K_h, K_w) cudnnFilterDescriptor_t cudnn_filter_desc = filter_desc.descriptor( layout, framework::vectorize2int(filter->dims())); cudnnConvolutionDescriptor_t cudnn_conv_desc = @@ -136,13 +142,13 @@ class CudnnConvTransposeGradOpKernel : public framework::OpKernel { ScopedConvolutionDescriptor conv_desc; DataLayout layout = DataLayout::kNCHW; - // Input: (N, M, H, W) + // Input: (N, M, H, W) or (N, M, D, H, W) cudnnTensorDescriptor_t cudnn_input_desc = input_desc.descriptor( layout, framework::vectorize2int(input->dims())); - // Output: (N, C, O_H, O_W) + // Output: (N, C, O_h, O_w) or (N, C, O_d, O_h, O_w) cudnnTensorDescriptor_t cudnn_output_desc = output_desc.descriptor( layout, framework::vectorize2int(output_grad->dims())); - // Filter (M, C, K_H, K_W) + // Filter (M, C, K_h, K_w) or (M, C, K_d K_h, K_w) cudnnFilterDescriptor_t cudnn_filter_desc = filter_desc.descriptor( layout, framework::vectorize2int(filter->dims())); diff --git a/paddle/platform/cudnn_helper.h b/paddle/platform/cudnn_helper.h index ce3421a3cb..8d75fceae8 100644 --- a/paddle/platform/cudnn_helper.h +++ b/paddle/platform/cudnn_helper.h @@ -1,11 +1,8 @@ /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -63,9 +60,10 @@ inline const char* cudnnGetErrorString(cudnnStatus_t status) { } \ } while (false) -enum class DataLayout { +enum class DataLayout { // Not use kNHWC, kNCHW, + kNCDHW, kNCHW_VECT_C, }; @@ -107,12 +105,15 @@ class CudnnDataType { } }; -inline cudnnTensorFormat_t GetCudnnTensorFormat(const DataLayout& order) { +inline cudnnTensorFormat_t GetCudnnTensorFormat( + const DataLayout& order) { // Not use switch (order) { case DataLayout::kNHWC: return CUDNN_TENSOR_NHWC; case DataLayout::kNCHW: return CUDNN_TENSOR_NCHW; + case DataLayout::kNCDHW: + return CUDNN_TENSOR_NCHW; // TODO(chengduoZH) : add CUDNN_TENSOR_NCDHW default: PADDLE_THROW("Unknown cudnn equivalent for order"); } @@ -139,7 +140,7 @@ class ScopedTensorDescriptor { strides[i] = dims[i + 1] * strides[i + 1]; } // Update tensor descriptor dims setting if groups > 1 - // FIXME(typhoonzero): Assume using NCHW order + // FIXME(typhoonzero): Assume using NCHW or NCDHW order std::vector dims_with_group(dims.begin(), dims.end()); // copy if (groups > 1) { dims_with_group[1] = dims_with_group[1] / groups; From 212f6eae774438693231cc90ae0a81a561331dc1 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 15 Nov 2017 18:04:11 +0800 Subject: [PATCH 09/43] modify the test config for test_CompareTwoNets.cpp --- paddle/gserver/tests/CMakeLists.txt | 9 + paddle/gserver/tests/sequence_recurrent.py | 56 ++++++ .../gserver/tests/sequence_recurrent_group.py | 70 +++++++ .../tests/test_CompareTwoNets.cpp | 11 +- paddle/trainer/tests/CMakeLists.txt | 8 - .../tests/sample_trainer_config_qb_rnn.conf | 154 --------------- .../tests/sample_trainer_config_rnn.conf | 180 ------------------ 7 files changed, 142 insertions(+), 346 deletions(-) create mode 100644 paddle/gserver/tests/sequence_recurrent.py create mode 100644 paddle/gserver/tests/sequence_recurrent_group.py rename paddle/{trainer => gserver}/tests/test_CompareTwoNets.cpp (95%) delete mode 100644 paddle/trainer/tests/sample_trainer_config_qb_rnn.conf delete mode 100644 paddle/trainer/tests/sample_trainer_config_rnn.conf diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index 45edef017e..09e1b949c2 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -111,3 +111,12 @@ if(NOT ON_TRAVIS) ${CMAKE_CURRENT_BINARY_DIR}/test_CompareSparse WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() + +################ test_CompareTwoNets ###################### +add_unittest_without_exec(test_CompareTwoNets + test_CompareTwoNets.cpp) +add_test(NAME test_CompareTwoNets + COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d + ${PADDLE_SOURCE_DIR}/python:${PADDLE_SOURCE_DIR}/paddle/gserver/tests + ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoNets + WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) diff --git a/paddle/gserver/tests/sequence_recurrent.py b/paddle/gserver/tests/sequence_recurrent.py new file mode 100644 index 0000000000..4895df186b --- /dev/null +++ b/paddle/gserver/tests/sequence_recurrent.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle.trainer_config_helpers import * + +######################## data source ################################ +dict_path = 'gserver/tests/Sequence/tour_dict_phrase.dict' +dict_file = dict() +for line_count, line in enumerate(open(dict_path, "r")): + dict_file[line.strip()] = line_count + +define_py_data_sources2( + train_list='gserver/tests/Sequence/train.list', + test_list=None, + module='sequenceGen', + obj='process', + args={"dict_file": dict_file}) + +settings(batch_size=5) +######################## network configure ################################ +dict_dim = len(open(dict_path, 'r').readlines()) +word_dim = 128 +hidden_dim = 128 +label_dim = 3 + +# This config is designed to be equivalent with sequence_recurrent_group.py + +data = data_layer(name="word", size=dict_dim) + +emb = embedding_layer( + input=data, size=word_dim, param_attr=ParamAttr(name="emb")) + +recurrent = recurrent_layer(input=emb, bias_attr=False, act=SoftmaxActivation()) + +recurrent_last = last_seq(input=recurrent) + +with mixed_layer( + size=label_dim, act=SoftmaxActivation(), bias_attr=True) as output: + output += full_matrix_projection(input=recurrent_last) + +outputs( + classification_cost( + input=output, label=data_layer( + name="label", size=1))) diff --git a/paddle/gserver/tests/sequence_recurrent_group.py b/paddle/gserver/tests/sequence_recurrent_group.py new file mode 100644 index 0000000000..a1d54542e3 --- /dev/null +++ b/paddle/gserver/tests/sequence_recurrent_group.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from paddle.trainer_config_helpers import * + +######################## data source ################################ +dict_path = 'gserver/tests/Sequence/tour_dict_phrase.dict' +dict_file = dict() +for line_count, line in enumerate(open(dict_path, "r")): + dict_file[line.strip()] = line_count + +define_py_data_sources2( + train_list='gserver/tests/Sequence/train.list', + test_list=None, + module='sequenceGen', + obj='process', + args={"dict_file": dict_file}) + +settings(batch_size=5) +######################## network configure ################################ +dict_dim = len(open(dict_path, 'r').readlines()) +word_dim = 128 +hidden_dim = 128 +label_dim = 3 + +# This config is designed to be equivalent with sequence_recurrent.py + +data = data_layer(name="word", size=dict_dim) + +emb = embedding_layer( + input=data, size=word_dim, param_attr=ParamAttr(name="emb")) + + +def step(y): + mem = memory(name="rnn_state", size=hidden_dim) + with mixed_layer( + name="rnn_state", + size=hidden_dim, + bias_attr=False, + act=SoftmaxActivation()) as out: + out += identity_projection(input=y) + out += full_matrix_projection( + input=mem, param_attr=ParamAttr(name="___recurrent_layer_0__")) + return out + + +recurrent = recurrent_group(name="rnn", step=step, input=emb) + +recurrent_last = last_seq(input=recurrent) + +with mixed_layer( + size=label_dim, act=SoftmaxActivation(), bias_attr=True) as output: + output += full_matrix_projection(input=recurrent_last) + +outputs( + classification_cost( + input=output, label=data_layer( + name="label", size=1))) diff --git a/paddle/trainer/tests/test_CompareTwoNets.cpp b/paddle/gserver/tests/test_CompareTwoNets.cpp similarity index 95% rename from paddle/trainer/tests/test_CompareTwoNets.cpp rename to paddle/gserver/tests/test_CompareTwoNets.cpp index 94f65e545d..801d960756 100644 --- a/paddle/trainer/tests/test_CompareTwoNets.cpp +++ b/paddle/gserver/tests/test_CompareTwoNets.cpp @@ -30,8 +30,6 @@ DECLARE_bool(use_gpu); DECLARE_string(config); DECLARE_string(nics); -DEFINE_string(config_file_a, "", "config of one network to compare"); -DEFINE_string(config_file_b, "", "config of another network to compare"); DEFINE_bool(need_high_accuracy, false, "whether need to run in double accuracy"); @@ -42,6 +40,10 @@ DEFINE_double( DECLARE_bool(thread_local_rand_use_global_seed); DECLARE_int32(seed); +static const string& config_file_a = "gserver/tests/sequence_recurrent.py"; +static const string& config_file_b = + "gserver/tests/sequence_recurrent_group.py"; + struct ComData { vector outArgs; vector parameters; @@ -66,6 +68,7 @@ void calcGradient(ComData& data, const string configFile) { DataBatch dataBatch; int32_t batchSize = trainer.getConfig().opt_config().batch_size(); + trainer.getDataProvider()->reset(); trainer.getDataProvider()->setSkipShuffle(); trainer.getDataProvider()->getNextBatch(batchSize, &dataBatch); @@ -167,11 +170,11 @@ void compareGradient(ComData& comDataA, ComData& comDataB) { TEST(Trainer, create) { ComData dataA; - calcGradient(dataA, FLAGS_config_file_a); + calcGradient(dataA, config_file_a); LOG(INFO) << "\n\nforwardBackward of Network A is finished\n\n"; ComData dataB; - calcGradient(dataB, FLAGS_config_file_b); + calcGradient(dataB, config_file_b); LOG(INFO) << "\n\nforwardBackward of the Network B is finished\n\n"; compareGradient(dataA, dataB); diff --git a/paddle/trainer/tests/CMakeLists.txt b/paddle/trainer/tests/CMakeLists.txt index 441df2b57b..3168f3c0ff 100644 --- a/paddle/trainer/tests/CMakeLists.txt +++ b/paddle/trainer/tests/CMakeLists.txt @@ -28,14 +28,6 @@ if(WITH_PYTHON) ${PADDLE_SOURCE_DIR}/paddle/.set_port.sh -p port ${CMAKE_CURRENT_BINARY_DIR}/test_TrainerOnePass WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() -################ test_CompareTwoNets ###################### -add_unittest_without_exec(test_CompareTwoNets - test_CompareTwoNets.cpp) -add_test(NAME test_CompareTwoNets - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoNets - --config_file_a=trainer/tests/sample_trainer_config_qb_rnn.conf --config_file_b=trainer/tests/sample_trainer_config_rnn.conf - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) ############### test_CompareTwoOpts ################### add_unittest_without_exec(test_CompareTwoOpts diff --git a/paddle/trainer/tests/sample_trainer_config_qb_rnn.conf b/paddle/trainer/tests/sample_trainer_config_qb_rnn.conf deleted file mode 100644 index d19222360c..0000000000 --- a/paddle/trainer/tests/sample_trainer_config_qb_rnn.conf +++ /dev/null @@ -1,154 +0,0 @@ -#edit-mode: -*- python -*- -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#Todo(luotao02) This config is only used for unitest. It is out of date now, and will be updated later. - -# Note: when making change to this file, please make sure -# sample_trainer_config_rnn.conf is changed accordingly so that the uniitest -# for comparing these two nets can pass (test_CompareTwoNets) - -default_initial_std(0.1) -default_device(0) - -word_dim = 1451594 -l1 = 0 -l2 = 0 - -model_type("nn") - -sparse_update = get_config_arg("sparse_update", bool, False) - -TrainData(ProtoData( - type = "proto_sequence", - files = ('trainer/tests/train.list'), - )) - -Settings( - algorithm='sgd', - batch_size=100, - learning_rate=0.0001, - learning_rate_decay_a=4e-08, - learning_rate_decay_b=0.0, - learning_rate_schedule='poly', -) - - -wordvec_dim = 128 -layer2_dim = 96 -layer3_dim = 96 -hidden_dim = 128 - -slot_names = ["qb", "qw", "tb", "tw"] - -def ltr_network(network_name, - word_dim=word_dim, - wordvec_dim=wordvec_dim, - layer2_dim=layer2_dim, - layer3_dim=layer3_dim, - hidden_dim=hidden_dim, - slot_names=slot_names, - l1=l1, - l2=l2): - - slotnum = len(slot_names) - for i in xrange(slotnum): - Inputs(slot_names[i] + network_name) - for i in xrange(slotnum): - Layer( - name = slot_names[i] + network_name, - type = "data", - size = word_dim, - device = -1, - ) - Layer( - name = slot_names[i] + "_embedding_" + network_name, - type = "mixed", - size = wordvec_dim, - bias = False, - device = -1, - inputs = TableProjection(slot_names[i] + network_name, - parameter_name = "embedding.w0", - decay_rate_l1=l1, - sparse_remote_update = True, - sparse_update = sparse_update, - ), - ) - Layer( - name = slot_names[i] + "_rnn1_" + network_name, - type = "recurrent", - active_type = "tanh", - bias = Bias(initial_std = 0, - parameter_name = "rnn1.bias"), - inputs = Input(slot_names[i] + "_embedding_" + network_name, - parameter_name = "rnn1.w0") - ) - Layer( - name = slot_names[i] + "_rnnlast_" + network_name, - type = "seqlastins", - inputs = [ - slot_names[i] + "_rnn1_" + network_name, - ], - ) - - Layer( - name = "layer2_" + network_name, - type = "fc", - active_type = "tanh", - size = layer2_dim, - bias = Bias(parameter_name = "layer2.bias"), - inputs = [Input(slot_name + "_rnnlast_" + network_name, - parameter_name = "_layer2_" + slot_name + ".w", - decay_rate = l2, - initial_smart = True) for slot_name in slot_names] - ) - Layer( - name = "layer3_" + network_name, - type = "fc", - active_type = "tanh", - size = layer3_dim, - bias = Bias(parameter_name = "layer3.bias"), - inputs = [ - Input("layer2_" + network_name, - parameter_name = "_layer3.w", - decay_rate = l2, - initial_smart = True), - ] - ) - Layer( - name = "output_" + network_name, - type = "fc", - size = 1, - bias = False, - inputs = [ - Input("layer3_" + network_name, - parameter_name = "_layerO.w"), - ], - ) - - -ltr_network("left") -ltr_network("right") -Inputs("label") -Layer( - name = "label", - type = "data", - size = 1, - ) -Outputs("cost", "qb_rnnlast_left") -Layer( - name = "cost", - type = "rank-cost", - inputs = ["output_left", "output_right", "label"], - ) diff --git a/paddle/trainer/tests/sample_trainer_config_rnn.conf b/paddle/trainer/tests/sample_trainer_config_rnn.conf deleted file mode 100644 index b720d4d5a6..0000000000 --- a/paddle/trainer/tests/sample_trainer_config_rnn.conf +++ /dev/null @@ -1,180 +0,0 @@ -#edit-mode: -*- python -*- -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#Todo(luotao02) This config is only used for unitest. It is out of date now, and will be updated later. - -# Note: when making change to this file, please make sure -# sample_trainer_config_qb_rnn.conf is changed accordingly so that the uniitest -# for comparing these two nets can pass (test_CompareTwoNets) - -default_initial_std(0.1) -default_device(0) - -word_dim = 1451594 -l1 = 0 -l2 = 0 - -model_type("recurrent_nn") - -sparse_update = get_config_arg("sparse_update", bool, False) - -TrainData(ProtoData( - type = "proto_sequence", - files = ('trainer/tests/train.list'), - )) - -Settings( - algorithm='sgd', - batch_size=100, - learning_rate=0.0001, - learning_rate_decay_a=4e-08, - learning_rate_decay_b=0.0, - learning_rate_schedule='poly', -) - - -wordvec_dim = 128 -layer2_dim = 96 -layer3_dim = 96 -hidden_dim = 128 - -slot_names = ["qb", "qw", "tb", "tw"] - -def SimpleRecurrentLayer(name, - size, - active_type, - bias, - input_layer_name, - parameter_name, - seq_reversed = False): - RecurrentLayerGroupBegin(name + "_layer_group", - in_links=[input_layer_name], - out_links=[name], - seq_reversed=seq_reversed) - memory_name = Memory(name=name, size=size) - Layer( - name = name, - type = "mixed", - size = size, - active_type = active_type, - bias = bias, - inputs = [IdentityProjection(input_layer_name), - FullMatrixProjection(memory_name, - parameter_name = parameter_name, - ), - ] - ) - RecurrentLayerGroupEnd(name + "_layer_group") - - -def ltr_network(network_name, - word_dim=word_dim, - wordvec_dim=wordvec_dim, - layer2_dim=layer2_dim, - layer3_dim=layer3_dim, - hidden_dim=hidden_dim, - slot_names=slot_names, - l1=l1, - l2=l2): - - slotnum = len(slot_names) - for i in xrange(slotnum): - Inputs(slot_names[i] + network_name) - for i in xrange(slotnum): - Layer( - name = slot_names[i] + network_name, - type = "data", - size = word_dim, - device = -1, - ) - Layer( - name = slot_names[i] + "_embedding_" + network_name, - type = "mixed", - size = wordvec_dim, - bias = False, - device = -1, - inputs = TableProjection(slot_names[i] + network_name, - parameter_name = "embedding.w0", - decay_rate_l1=l1, - sparse_remote_update = True, - sparse_update = sparse_update, - ), - ) - SimpleRecurrentLayer( - name = slot_names[i] + "_rnn1_" + network_name, - size = hidden_dim, - active_type = "tanh", - bias = Bias(initial_std = 0, - parameter_name = "rnn1.bias"), - input_layer_name = slot_names[i] + "_embedding_" + network_name, - parameter_name = "rnn1.w0", - ) - Layer( - name = slot_names[i] + "_rnnlast_" + network_name, - type = "seqlastins", - inputs = [ - slot_names[i] + "_rnn1_" + network_name, - ], - ) - Layer( - name = "layer2_" + network_name, - type = "fc", - active_type = "tanh", - size = layer2_dim, - bias = Bias(parameter_name = "layer2.bias"), - inputs = [Input(slot_name + "_rnnlast_" + network_name, - parameter_name = "_layer2_" + slot_name + ".w", - decay_rate = l2, - initial_smart = True) for slot_name in slot_names] - ) - Layer( - name = "layer3_" + network_name, - type = "fc", - active_type = "tanh", - size = layer3_dim, - bias = Bias(parameter_name = "layer3.bias"), - inputs = [ - Input("layer2_" + network_name, - parameter_name = "_layer3.w", - decay_rate = l2, - initial_smart = True), - ] - ) - Layer( - name = "output_" + network_name, - type = "fc", - size = 1, - bias = False, - inputs = [ - Input("layer3_" + network_name, - parameter_name = "_layerO.w"), - ], - ) - - -ltr_network("left") -ltr_network("right") -Inputs("label") -Layer( - name = "label", - type = "data", - size = 1, - ) -Outputs("cost", "qb_rnnlast_left") -Layer( - name = "cost", - type = "rank-cost", - inputs = ["output_left", "output_right", "label"], - ) From 75426e013a8af9a327a1c47008719053a4df8dff Mon Sep 17 00:00:00 2001 From: guosheng Date: Thu, 16 Nov 2017 11:24:08 +0800 Subject: [PATCH 10/43] Refine GRU Operator --- paddle/operators/gru_op.h | 1 + python/paddle/v2/framework/tests/test_gru_op.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/paddle/operators/gru_op.h b/paddle/operators/gru_op.h index b2cf358994..9fb60e20d1 100644 --- a/paddle/operators/gru_op.h +++ b/paddle/operators/gru_op.h @@ -154,6 +154,7 @@ class GRUGradKernel : public framework::OpKernel { } if (h0_grad) { ordered_h0_grad.mutable_data(h0_grad->dims(), context.GetPlace()); + zero(context.device_context(), &ordered_h0_grad, static_cast(0.0)); } bool is_reverse = context.Attr("is_reverse"); diff --git a/python/paddle/v2/framework/tests/test_gru_op.py b/python/paddle/v2/framework/tests/test_gru_op.py index 2bb78d10e0..fa2c5a53ec 100644 --- a/python/paddle/v2/framework/tests/test_gru_op.py +++ b/python/paddle/v2/framework/tests/test_gru_op.py @@ -149,7 +149,7 @@ class TestGRUOpReverse(TestGRUOp): self.is_reverse = True self.attrs = { 'activation': 'tanh', - 'gate_activation': 'tanh', + 'gate_activation': 'sigmoid', 'is_reverse': self.is_reverse } From 9acfba82a37d06aeafaaacccc30b6e2df56354ed Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 11:46:31 +0800 Subject: [PATCH 11/43] add input index choice for mkldnn_concat --- paddle/gserver/layers/MKLDNNLayer.cpp | 7 +++++-- paddle/gserver/layers/MKLDNNLayer.h | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index e75ac5ba46..0d063a89cc 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -138,8 +138,11 @@ void MKLDNNLayer::backward(const UpdateCallback& callback) { } } -void MKLDNNLayer::reshapeInput(int& batchsize, int& height, int& width) { - const Argument& input = inputLayers_[0]->getOutput(); +void MKLDNNLayer::reshapeInput(int& batchsize, + int& height, + int& width, + size_t inputIdx) { + const Argument& input = inputLayers_[inputIdx]->getOutput(); batchsize = input.getBatchSize(); int h = input.getFrameHeight(); int w = input.getFrameWidth(); diff --git a/paddle/gserver/layers/MKLDNNLayer.h b/paddle/gserver/layers/MKLDNNLayer.h index 7479c34c92..4c42df1bee 100644 --- a/paddle/gserver/layers/MKLDNNLayer.h +++ b/paddle/gserver/layers/MKLDNNLayer.h @@ -178,7 +178,10 @@ protected: /** * reshape the input image sizes and input batchsize */ - void reshapeInput(int& batchsize, int& height, int& width); + void reshapeInput(int& batchsize, + int& height, + int& width, + size_t inputIdx = 0); /** * reshape output image sizes From c66b5ce2c11ecc00b1211c7ae9c762880c6ed4e4 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 11:55:12 +0800 Subject: [PATCH 12/43] add mkldnn concat layer --- paddle/gserver/layers/MKLDNNConcatLayer.cpp | 190 ++++++++++++++++++++ paddle/gserver/layers/MKLDNNConcatLayer.h | 129 +++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 paddle/gserver/layers/MKLDNNConcatLayer.cpp create mode 100644 paddle/gserver/layers/MKLDNNConcatLayer.h diff --git a/paddle/gserver/layers/MKLDNNConcatLayer.cpp b/paddle/gserver/layers/MKLDNNConcatLayer.cpp new file mode 100644 index 0000000000..64946508d2 --- /dev/null +++ b/paddle/gserver/layers/MKLDNNConcatLayer.cpp @@ -0,0 +1,190 @@ +/* Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "MKLDNNConcatLayer.h" + +using namespace mkldnn; // NOLINT +typedef memory::format format; + +namespace paddle { + +REGISTER_LAYER(mkldnn_concat, MKLDNNConcatLayer); + +bool MKLDNNConcatLayer::init(const LayerMap& layerMap, + const ParameterMap& parameterMap) { + if (!MKLDNNLayer::init(layerMap, parameterMap)) { + return false; + } + CHECK_GT(inputLayers_.size(), 1UL); + CHECK(!biasParameter_); + return true; +} + +void MKLDNNConcatLayer::reshape( + int& bs, int& ic, int& ih, int& iw, int oc, int& oh, int& ow) { + reshapeInput(bs, ih, iw); + ic = inputLayers_[0]->getSize() / ih / iw; + CHECK_EQ((size_t)ic * ih * iw, inputLayers_[0]->getSize()); + CHECK_EQ(inputElemenCnt_, (size_t)bs * ic * ih * iw); + CHECK_GT(inputLayers_.size(), 1UL); + channels_.resize(inputLayers_.size()); + channels_[0] = ic; + oc = ic; + for (size_t i = 1; i < inputLayers_.size(); i++) { + int batchsize, height, witdh; + reshapeInput(batchsize, height, witdh, i); + CHECK_EQ(bs, batchsize); + CHECK_EQ(ih, height); + CHECK_EQ(iw, witdh); + + channels_[i] = inputLayers_[i]->getSize() / height / witdh; + CHECK_EQ((size_t)channels_[i] * height * witdh, inputLayers_[i]->getSize()); + oc += channels_[i]; + } + oh = ih; + ow = iw; + reshapeOutput(oh, ow); + resizeOutput(bs, oc * oh * ow); +} + +void MKLDNNConcatLayer::resetFwd(std::vector& pipeline, + MKLDNNMatrixPtr& in, + MKLDNNMatrixPtr& wgt, + MKLDNNMatrixPtr& bias, + MKLDNNMatrixPtr& out) { + resetFwdBuffers(inVals_, out); + in = inVals_[0]; + + std::shared_ptr fwdPD; + resetFwdPD(fwdPD, inVals_, out); + + resetFwdPipeline(pipeline, fwdPD, inVals_, out); +} + +void MKLDNNConcatLayer::resetBwd(std::vector& pipeline, + MKLDNNMatrixPtr& in, + MKLDNNMatrixPtr& wgt, + MKLDNNMatrixPtr& bias, + MKLDNNMatrixPtr& out) { + resetBwdBuffers(inGrads_, out); + in = inGrads_[0]; + + resetBwdPipeline(pipeline, bwds_, inGrads_, out); +} + +void MKLDNNConcatLayer::resetFwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& out) { + inputs.resize(inputLayers_.size()); + bool has8c = false, has16c = false, hasnc = false; + for (size_t i = 0; i < inputs.size(); i++) { + resetInValue(inputs[i], nullptr, i); + CHECK(inputs[i]); + auto dm = inputs[i]->getDims(); + // inputs format can be different, but ndims must equal + CHECK(i == 0 || dm.size() == inputs[0]->getDims().size()); + CHECK_EQ(bs_, dm[0]); + CHECK_EQ(channels_[i], dm[1]); + if (dm.size() > 2) { + CHECK_EQ(ih_, dm[2]); + CHECK_EQ(iw_, dm[3]); + } + if (inputs[i]->getFormat() == format::nc) { + hasnc = true; + } + if (inputs[i]->getFormat() == format::nChw8c) { + has8c = true; + } + if (inputs[i]->getFormat() == format::nChw16c) { + has16c = true; + } + } + + format outFmt; + if (has16c && oc_ % 16 == 0) { + outFmt = format::nChw16c; + } else if (has8c && oc_ % 8 == 0) { + outFmt = format::nChw8c; + } else if (hasnc) { + CHECK(oh_ == 1 && ow_ == 1); + outFmt = format::nc; + } else { + outFmt = format::nchw; + } + memory::dims outDims = + hasnc ? memory::dims{bs_, oc_} : memory::dims{bs_, oc_, oh_, ow_}; + auto outPD = MKLDNNMatrix::createPrimitiveDesc(outDims, outFmt, engine_); + resetOutValue(out, outPD); +} + +void MKLDNNConcatLayer::resetFwdPD(std::shared_ptr& pd, + std::vector& inputs, + MKLDNNMatrixPtr out) { + std::vector srcPDs; + for (size_t i = 0; i < inputs.size(); i++) { + srcPDs.push_back(inputs[i]->getPrimitiveDesc()); + } + CHECK(out); + pd.reset(new concat::primitive_desc(out->getMemoryDesc(), axis_, srcPDs)); + CHECK_PRIMITIVE_DESC_EQ(out, pd->dst_primitive_desc()); +} + +void MKLDNNConcatLayer::resetFwdPipeline( + std::vector& pipeline, + std::shared_ptr& pd, + std::vector& inputs, + MKLDNNMatrixPtr& out) { + std::vector srcs; + for (size_t i = 0; i < inputs.size(); i++) { + srcs.push_back(*(inputs[i])); + } + fwd_.reset(new concat(*pd, srcs, *out)); + pipeline.push_back(*fwd_); +} + +void MKLDNNConcatLayer::resetBwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& out) { + CHECK(outVal_); + resetOutGrad(out, outVal_->getPrimitiveDesc()); + CHECK(out); + + inputs.resize(inputLayers_.size()); + for (size_t i = 0; i < inputs.size(); i++) { + CHECK(inVals_[i]); + resetInGrad(inputs[i], inVals_[i]->getPrimitiveDesc(), i); + CHECK_PRIMITIVE_DESC_EQ(inputs[i], inVals_[i]->getPrimitiveDesc()); + } +} + +void MKLDNNConcatLayer::resetBwdPipeline( + std::vector& pipeline, + std::vector>& prims, + std::vector& inputs, + MKLDNNMatrixPtr& out) { + // reset the backward primitives + memory::dims offsets = {0, 0, 0, 0}; + prims.resize(inputs.size()); + CHECK_EQ(inputs.size(), channels_.size()); + for (size_t i = 0; i < inputs.size(); i++) { + auto viewPD = view::primitive_desc( + out->getPrimitiveDesc(), inputs[i]->getDims(), offsets); + auto bwdPD = reorder::primitive_desc(viewPD.dst_primitive_desc(), + inputs[i]->getPrimitiveDesc()); + prims[i].reset(new reorder(bwdPD, *out, *(inputs[i]))); + offsets[axis_] += channels_[i]; + // push to pipeline + pipeline.push_back(*prims[i]); + } +} + +} // namespace paddle diff --git a/paddle/gserver/layers/MKLDNNConcatLayer.h b/paddle/gserver/layers/MKLDNNConcatLayer.h new file mode 100644 index 0000000000..ad70ec0ceb --- /dev/null +++ b/paddle/gserver/layers/MKLDNNConcatLayer.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "MKLDNNLayer.h" +#include "mkldnn.hpp" + +namespace paddle { + +/** + * @brief A subclass of MKLDNNLayer Concatenate layer. + * + * The config file api is mkldnn_concat + */ +class MKLDNNConcatLayer : public MKLDNNLayer { +protected: + std::vector inVals_; + std::vector inGrads_; + std::vector> bwds_; + // input channel numbers + std::vector channels_; + + // concat_dimension in MKLDNN + // if axis_ == 0, concat batchsize + // if axis_ == 1, concat channel (default) + int axis_; + +public: + explicit MKLDNNConcatLayer(const LayerConfig& config) + : MKLDNNLayer(config), axis_(1) {} + + ~MKLDNNConcatLayer() {} + + bool init(const LayerMap& layerMap, + const ParameterMap& parameterMap) override; + + void reshape( + int& bs, int& ic, int& ih, int& iw, int oc, int& oh, int& ow) override; + + void resetFwd(std::vector& pipeline, + MKLDNNMatrixPtr& in, + MKLDNNMatrixPtr& wgt, + MKLDNNMatrixPtr& bias, + MKLDNNMatrixPtr& out) override; + + void resetBwd(std::vector& pipeline, + MKLDNNMatrixPtr& in, + MKLDNNMatrixPtr& wgt, + MKLDNNMatrixPtr& bias, + MKLDNNMatrixPtr& out) override; + + void printSizeInfo() override { + CHECK_EQ(channels_.size(), inputLayers_.size()); + for (size_t i = 0; i < channels_.size(); ++i) { + VLOG(MKLDNN_SIZES) << "Input " << i << ", " << inputLayers_[i]->getName() + << ": " << bs_ << ", " << channels_[i] << ", " << ih_ + << ", " << iw_; + } + VLOG(MKLDNN_SIZES) << "Output: " << bs_ << ", " << oc_ << ", " << oh_ + << ", " << ow_; + } + + void printValueFormat() override { + for (size_t i = 0; i < inVals_.size(); ++i) { + VLOG(MKLDNN_FMTS) << "Input " << i << inputLayers_[i]->getName() << ": " + << inVals_[i]->getFormat() << " >>>"; + } + if (outVal_) { + VLOG(MKLDNN_FMTS) << outVal_->getFormat() << " >>> "; + } + if (extOutVal_) { + VLOG(MKLDNN_FMTS) << extOutVal_->getFormat(); + } + } + + void printGradFormat() override { + if (extOutGrad_) { + VLOG(MKLDNN_FMTS) << extOutGrad_->getFormat(); + } + if (outGrad_) { + VLOG(MKLDNN_FMTS) << outGrad_->getFormat() << " <<< "; + } + for (size_t i = 0; i < inGrads_.size(); ++i) { + VLOG(MKLDNN_FMTS) << "Input " << i << inputLayers_[i]->getName() << ": " + << inGrads_[i]->getFormat() << "<<<"; + } + } + +protected: + /** + * Forward functions: reset buffers(inputs, output, bias), + * reset primitive descriptor, + * reset pipeline. + */ + void resetFwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& out); + void resetFwdPD(std::shared_ptr& pd, + std::vector& inputs, + MKLDNNMatrixPtr out); + void resetFwdPipeline(std::vector& pipeline, + std::shared_ptr& pd, + std::vector& inputs, + MKLDNNMatrixPtr& out); + + /** + * Backward functions: reset buffers(inputs, output, bias) + * reset primitives and pipeline + */ + void resetBwdBuffers(std::vector& inputs, + MKLDNNMatrixPtr& out); + void resetBwdPipeline(std::vector& pipeline, + std::vector>& prims, + std::vector& inputs, + MKLDNNMatrixPtr& out); +}; + +} // namespace paddle From 40a486d86520a74bbcfcbfe94ef51fa34a8c1226 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 16:21:39 +0800 Subject: [PATCH 13/43] add mkldnn_concat unit test --- paddle/gserver/tests/test_MKLDNN.cpp | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/paddle/gserver/tests/test_MKLDNN.cpp b/paddle/gserver/tests/test_MKLDNN.cpp index a859e34c89..42644e9601 100644 --- a/paddle/gserver/tests/test_MKLDNN.cpp +++ b/paddle/gserver/tests/test_MKLDNN.cpp @@ -313,6 +313,47 @@ TEST(MKLDNNLayer, AddtoLayer) { testAddtoLayer({4, 12, 1, 1}, 3); } +static void getMKLDNNConcatConfig(TestConfig& cfg, + const std::vector& inputs) { + CHECK_GE(inputs.size(), 2) << "at least two inputs"; + int oc = inputs[0].ic; + for (size_t i = 1; i < inputs.size(); ++i) { + CHECK_EQ(inputs[i].bs, inputs[0].bs); + CHECK_EQ(inputs[i].ih, inputs[0].ih); + CHECK_EQ(inputs[i].iw, inputs[0].iw); + oc += inputs[i].ic; + } + cfg.biasSize = 0; + cfg.layerConfig.set_type("mkldnn_concat"); + cfg.layerConfig.set_size(oc * inputs[0].ih * inputs[0].iw); + cfg.layerConfig.set_active_type("relu"); + for (size_t i = 0; i < inputs.size(); ++i) { + std::stringstream ss; + ss << "layer_" << i; + cfg.inputDefs.push_back( + {INPUT_DATA, + ss.str(), + (size_t)(inputs[i].ic) * inputs[i].ih * inputs[i].iw, + 0}); + LayerInputConfig* input = cfg.layerConfig.add_inputs(); + ImageConfig* img_conf = input->mutable_image_conf(); + img_conf->set_channels(inputs[i].ic); + img_conf->set_img_size_y(inputs[i].ih); + img_conf->set_img_size(inputs[i].iw); + } +} + +void testConcatLayer(const std::vector& inputs) { + TestConfig dnnConfig; + getMKLDNNConcatConfig(dnnConfig, inputs); + RUN_MKLDNN_TEST_LAYER(dnnConfig, "concat", inputs[0]) +} + +TEST(MKLDNNLayer, ConcatLayer) { + testConcatLayer({{64, 128, 1, 1}, {64, 32, 1, 1}, {64, 64, 1, 1}}); + testConcatLayer({{32, 100, 8, 8}, {32, 10, 8, 8}}); +} + void testActivation(std::string actType, const testImageDesc& pm) { // TODO(TJ): remove me when paddle support elu activation if (actType == "mkldnn_elu") { From 0321e1f861c7d02086a510c2b32bc60112c9b64a Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Thu, 16 Nov 2017 16:52:39 +0800 Subject: [PATCH 14/43] Fix bilinear_tensor_product_op in debug mode. --- paddle/operators/bilinear_tensor_product_op.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/paddle/operators/bilinear_tensor_product_op.h b/paddle/operators/bilinear_tensor_product_op.h index ffa4f43a32..ee9636e9fb 100644 --- a/paddle/operators/bilinear_tensor_product_op.h +++ b/paddle/operators/bilinear_tensor_product_op.h @@ -63,6 +63,7 @@ class BilinearTensorProductKernel : public framework::OpKernel { math::gemm(ctx.device_context(), CblasNoTrans, CblasNoTrans, batch_size, y_dim, x_dim, 1, x->data(), weight_mat.data(), 0, left_mul.data()); + Eigen::array shape({{static_cast(out->dims()[0]), 1}}); output_col_vec.device(place) = (left_mul_mat * y_mat).sum(Eigen::DSizes(1)); } @@ -174,7 +175,7 @@ class BilinearTensorProductGradKernel : public framework::OpKernel { // Caculate the gradient of Input(Bias). if (d_bias) { d_bias->mutable_data(ctx.GetPlace()); - auto d_bias_mat = EigenMatrix::From(*d_bias); + auto d_bias_mat = framework::EigenVector::Flatten(*d_bias); d_bias_mat.device(place) = d_out_mat.sum(Eigen::DSizes(0)); } } From 49ac0480e60c5e5ecb122b0c3d8e54bb99232061 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Thu, 16 Nov 2017 16:55:25 +0800 Subject: [PATCH 15/43] fix a lot of warnings -Wunused-but-set-variable and Wunused-variable when compile openBlas --- cmake/external/openblas.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/external/openblas.cmake b/cmake/external/openblas.cmake index 324e29f931..2253807981 100644 --- a/cmake/external/openblas.cmake +++ b/cmake/external/openblas.cmake @@ -29,7 +29,7 @@ IF(NOT ${CBLAS_FOUND}) "${CBLAS_INSTALL_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}openblas${CMAKE_STATIC_LIBRARY_SUFFIX}" CACHE FILEPATH "openblas library." FORCE) - SET(OPENBLAS_CC "${CMAKE_C_COMPILER}") + SET(OPENBLAS_CC "${CMAKE_C_COMPILER} -Wno-unused-but-set-variable -Wno-unused-variable") IF(CMAKE_CROSSCOMPILING) SET(OPTIONAL_ARGS HOSTCC=${HOST_C_COMPILER}) From 19c989ac159dc831248edc694654d309956ad3e9 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 17:24:19 +0800 Subject: [PATCH 16/43] fix error and pass unit test --- paddle/gserver/layers/MKLDNNConcatLayer.cpp | 18 +++++++++++++++--- paddle/gserver/layers/MKLDNNConcatLayer.h | 8 ++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/paddle/gserver/layers/MKLDNNConcatLayer.cpp b/paddle/gserver/layers/MKLDNNConcatLayer.cpp index 64946508d2..c9099297cc 100644 --- a/paddle/gserver/layers/MKLDNNConcatLayer.cpp +++ b/paddle/gserver/layers/MKLDNNConcatLayer.cpp @@ -40,7 +40,9 @@ void MKLDNNConcatLayer::reshape( CHECK_GT(inputLayers_.size(), 1UL); channels_.resize(inputLayers_.size()); channels_[0] = ic; - oc = ic; + // need change the output channel, so use oc_ instead + // TODO(TJ): change API, use &oc + oc_ = ic; for (size_t i = 1; i < inputLayers_.size(); i++) { int batchsize, height, witdh; reshapeInput(batchsize, height, witdh, i); @@ -50,12 +52,12 @@ void MKLDNNConcatLayer::reshape( channels_[i] = inputLayers_[i]->getSize() / height / witdh; CHECK_EQ((size_t)channels_[i] * height * witdh, inputLayers_[i]->getSize()); - oc += channels_[i]; + oc_ += channels_[i]; } oh = ih; ow = iw; reshapeOutput(oh, ow); - resizeOutput(bs, oc * oh * ow); + resizeOutput(bs, oc_ * oh * ow); } void MKLDNNConcatLayer::resetFwd(std::vector& pipeline, @@ -88,6 +90,9 @@ void MKLDNNConcatLayer::resetFwdBuffers(std::vector& inputs, inputs.resize(inputLayers_.size()); bool has8c = false, has16c = false, hasnc = false; for (size_t i = 0; i < inputs.size(); i++) { + // resetInValue will use ic_ so temporary change as current input's channel + // TODO(TJ): change ic_ as vector then can remove channels_ + ic_ = channels_[i]; resetInValue(inputs[i], nullptr, i); CHECK(inputs[i]); auto dm = inputs[i]->getDims(); @@ -109,6 +114,8 @@ void MKLDNNConcatLayer::resetFwdBuffers(std::vector& inputs, has16c = true; } } + // change back, ic_ always save the input 0 size + ic_ = channels_[0]; format outFmt; if (has16c && oc_ % 16 == 0) { @@ -161,9 +168,14 @@ void MKLDNNConcatLayer::resetBwdBuffers(std::vector& inputs, inputs.resize(inputLayers_.size()); for (size_t i = 0; i < inputs.size(); i++) { CHECK(inVals_[i]); + // resetInGrad will use inVal_ + // TODO(TJ): change move inVals_ to MKLDNNLayer ans remove inVal_ + inVal_ = inVals_[i]; resetInGrad(inputs[i], inVals_[i]->getPrimitiveDesc(), i); CHECK_PRIMITIVE_DESC_EQ(inputs[i], inVals_[i]->getPrimitiveDesc()); } + // change back, inVal_ always save the input 0 + inVal_ = inVals_[0]; } void MKLDNNConcatLayer::resetBwdPipeline( diff --git a/paddle/gserver/layers/MKLDNNConcatLayer.h b/paddle/gserver/layers/MKLDNNConcatLayer.h index ad70ec0ceb..d5749d327e 100644 --- a/paddle/gserver/layers/MKLDNNConcatLayer.h +++ b/paddle/gserver/layers/MKLDNNConcatLayer.h @@ -74,8 +74,8 @@ public: void printValueFormat() override { for (size_t i = 0; i < inVals_.size(); ++i) { - VLOG(MKLDNN_FMTS) << "Input " << i << inputLayers_[i]->getName() << ": " - << inVals_[i]->getFormat() << " >>>"; + VLOG(MKLDNN_FMTS) << "Input " << i << ", " << inputLayers_[i]->getName() + << ": " << inVals_[i]->getFormat() << " >>>"; } if (outVal_) { VLOG(MKLDNN_FMTS) << outVal_->getFormat() << " >>> "; @@ -93,8 +93,8 @@ public: VLOG(MKLDNN_FMTS) << outGrad_->getFormat() << " <<< "; } for (size_t i = 0; i < inGrads_.size(); ++i) { - VLOG(MKLDNN_FMTS) << "Input " << i << inputLayers_[i]->getName() << ": " - << inGrads_[i]->getFormat() << "<<<"; + VLOG(MKLDNN_FMTS) << "Input " << i << ", " << inputLayers_[i]->getName() + << ": " << inGrads_[i]->getFormat() << "<<<"; } } From 739858c8899c33f1116cf5c599b13229a28659aa Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 17:26:39 +0800 Subject: [PATCH 17/43] add python interface for mkldnn_concat --- python/paddle/trainer/config_parser.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 43d02bf70e..7ffb9d279a 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -3488,11 +3488,17 @@ def ExpressionLayer(name, inputs, **xargs): @config_layer('concat') class ConcatenateLayer(LayerBase): + layer_type = 'concat' + def __init__(self, name, inputs, bias=False, **xargs): config_assert(inputs, 'inputs cannot be empty') config_assert(not bias, 'ConcatenateLayer cannot support bias.') + use_mkldnn = bool(int(g_command_config_args.get("use_mkldnn", 0))) + if self.layer_type == "mkldnn_concat": + config_assert(use_mkldnn, "mkldnn_concat only support MKLDNN") + self.layer_type = 'mkldnn_concat' if use_mkldnn else 'concat' super(ConcatenateLayer, self).__init__( - name, 'concat', 0, inputs=inputs, **xargs) + name, self.layer_type, 0, inputs=inputs, **xargs) size = 0 for input_index in xrange(len(self.inputs)): assert self.get_input_layer(0).height == self.get_input_layer( @@ -3512,6 +3518,11 @@ class ConcatenateLayer(LayerBase): self.set_layer_size(size) +@config_layer('mkldnn_concat') +class MKLDNNConcatLayer(ConcatenateLayer): + layer_type = 'mkldnn_concat' + + # like concat layer, but each input layer was processed by a Projection. @config_layer('concat2') class ConcatenateLayer2(LayerBase): From 01fa4cc73d37671eaec9e58dfc4d2bc76df9a15e Mon Sep 17 00:00:00 2001 From: qingqing01 Date: Thu, 16 Nov 2017 17:32:32 +0800 Subject: [PATCH 18/43] Remove the unused code. --- paddle/operators/bilinear_tensor_product_op.h | 1 - 1 file changed, 1 deletion(-) diff --git a/paddle/operators/bilinear_tensor_product_op.h b/paddle/operators/bilinear_tensor_product_op.h index ee9636e9fb..1113a4c6f3 100644 --- a/paddle/operators/bilinear_tensor_product_op.h +++ b/paddle/operators/bilinear_tensor_product_op.h @@ -63,7 +63,6 @@ class BilinearTensorProductKernel : public framework::OpKernel { math::gemm(ctx.device_context(), CblasNoTrans, CblasNoTrans, batch_size, y_dim, x_dim, 1, x->data(), weight_mat.data(), 0, left_mul.data()); - Eigen::array shape({{static_cast(out->dims()[0]), 1}}); output_col_vec.device(place) = (left_mul_mat * y_mat).sum(Eigen::DSizes(1)); } From 24819df05538980e5d705ef159b51d885ab2a207 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Thu, 16 Nov 2017 17:35:44 +0800 Subject: [PATCH 19/43] Fix cos_sim_op in debug mode. --- paddle/operators/cos_sim_op.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/cos_sim_op.h b/paddle/operators/cos_sim_op.h index 68c56f531f..62a4e484ec 100644 --- a/paddle/operators/cos_sim_op.h +++ b/paddle/operators/cos_sim_op.h @@ -132,7 +132,7 @@ class CosSimGradKernel : public framework::OpKernel { // compute dy if (out_grad_y) { out_grad_y->mutable_data(context.GetPlace()); - auto dy = EigenMatrix::Reshape(*out_grad_y, 1); + auto dy = EigenVector::Flatten(*out_grad_y); auto grad = x / norm_prod_bcast - z_bcast * y_bcast / y_snorm_bcast; dy.device(place) = (dz_bcast * grad).sum(Eigen::array({{0}})); } From 18f0c40a97ee81f222731dd16aff719e8cbbd27d Mon Sep 17 00:00:00 2001 From: "Yang Yang(Tony)" Date: Thu, 16 Nov 2017 02:19:18 -0800 Subject: [PATCH 20/43] feature/while_grad_op (#5554) * first commit * Python API for while op * Python Unittest for simple while_op forward * fix out to be list * Fix UT * VarType * Fix several bugs * Fix bug * Fix bug * Fix Bug * Fix bug * Fix unittest * Remove debug log * Add comments * add PADDLE_ENFORCE * while_grad_op first commit * Add `BlockDescBind::FindRecursiveOrCreateVar()` and fix bugs * not sure how to setdim of while outputs * push for test * add executor vlog * fix bug of while_op cond * Several enhancement for code 1. Backward always infer shape & infer var type. Since there are RENAME variables will be created when creating backward operator, but their shape & var types are not inferenced. 2. Never use SomePtr-> directly, since every pointer could be nullptr if it is a function return value. Add `detail::Ref` to cast pointer to reference safely. 3. Enhance error message for backward. 4. Infer data type of variable in `sum` and `tensor_write` * Fix bugs of while_op gradient * Fix several bugs of while_op grad * fix fill zeros like * fix 3 >= 3 * fix place holder shouldn't be null * fail on sum op * Fix SumOp of TensorList * clean up * pass while test * fix test_array_write_read * pass sum op * Support int/int64 for fill_constant_batch_size_like * Fix compile --- paddle/framework/backward.cc | 68 +++++-- paddle/framework/data_type.h | 2 + paddle/framework/ddim.cc | 3 +- paddle/framework/executor.cc | 1 + paddle/framework/op_desc.cc | 24 ++- paddle/framework/op_desc.h | 4 + paddle/framework/operator.cc | 13 -- paddle/framework/scope.cc | 3 +- paddle/framework/shape_inference.h | 7 +- paddle/operators/array_operator.h | 1 + paddle/operators/detail/safe_ref.h | 31 +++ .../fill_constant_batch_size_like_op.cc | 5 +- .../fill_constant_batch_size_like_op.cu.cc | 5 +- paddle/operators/fill_zeros_like_op.cc | 7 +- paddle/operators/fill_zeros_like_op.cu.cc | 7 +- paddle/operators/math/math_function.cc | 2 + paddle/operators/math/math_function.cu | 2 + paddle/operators/sum_op.cc | 37 +++- .../operators/tensor_array_read_write_op.cc | 24 ++- paddle/operators/while_op.cc | 184 +++++++++++++++--- python/paddle/v2/fluid/framework.py | 35 +++- python/paddle/v2/fluid/net_drawer.py | 4 + python/paddle/v2/fluid/tests/test_while_op.py | 13 +- 23 files changed, 378 insertions(+), 104 deletions(-) create mode 100644 paddle/operators/detail/safe_ref.h diff --git a/paddle/framework/backward.cc b/paddle/framework/backward.cc index b3b9c45ded..00d9dd238e 100644 --- a/paddle/framework/backward.cc +++ b/paddle/framework/backward.cc @@ -270,6 +270,19 @@ static bool AllGradInSet(const std::vector& names, return false; } } + if (VLOG_IS_ON(10)) { + std::ostringstream sout; + sout << "All input {"; + for (auto& name : names) { + sout << name << ","; + } + sout << "} is in {"; + for (auto& name : set) { + sout << name << ","; + } + sout << "}"; + VLOG(10) << sout.str(); + } return true; } @@ -290,14 +303,12 @@ static void CreateGradVarInBlock( auto ops = block_desc->AllOps(); for (size_t op_index = grad_op_start_index; op_index < ops.size(); ++op_index) { - bool need_infer_shape = false; std::unordered_set new_vars; ForEachVarName(ops[op_index]->Outputs(), [&](const std::string& grad_var_name) { if (block_desc->HasVar(grad_var_name)) { return false; } - need_infer_shape = true; auto var = block_desc->Var(grad_var_name); new_vars.insert(var->Name()); auto it = param_name_map.find(grad_var_name); @@ -311,23 +322,21 @@ static void CreateGradVarInBlock( grad_record.op_idx_ = static_cast(op_index); return false; /* not break */ }); - if (need_infer_shape) { - ops[op_index]->InferVarType(block_desc); - for (auto& arg : ops[op_index]->OutputArgumentNames()) { - if (new_vars.find(arg) == new_vars.end()) { - continue; - } - auto pname = FwdName(arg); - auto* param = block_desc->FindVarRecursive(pname); - auto* grad = block_desc->FindVar(arg); - if (param == nullptr) { - grad->SetDataType(DataType::FP32); - } else { - grad->SetDataType(param->GetDataType()); - } + ops[op_index]->InferVarType(block_desc); + for (auto& arg : ops[op_index]->OutputArgumentNames()) { + if (new_vars.find(arg) == new_vars.end()) { + continue; + } + auto pname = FwdName(arg); + auto* param = block_desc->FindVarRecursive(pname); + auto* grad = block_desc->FindVar(arg); + if (param == nullptr) { + grad->SetDataType(DataType::FP32); + } else { + grad->SetDataType(param->GetDataType()); } - ops[op_index]->InferShape(*block_desc); } + ops[op_index]->InferShape(*block_desc); } } @@ -387,6 +396,7 @@ std::vector> MakeBlockBackward( ProgramDescBind& program_desc, int block_idx, std::unordered_set* no_grad_vars, std::unordered_map* grad_to_var) { + VLOG(5) << "MakeBlockBackward"; BlockDescBind* cur_block = program_desc.MutableBlock(block_idx); std::vector op_descs = cur_block->AllOps(); std::unordered_map> dup_out_ops; @@ -394,9 +404,10 @@ std::vector> MakeBlockBackward( std::vector> backward_descs; for (auto it = op_descs.rbegin(); it != op_descs.rend(); ++it) { + VLOG(5) << "Making backward " << (*it)->Type() << " op"; std::vector> op_grads; - if ((*it)->Type() == "recurrent") { + if ((*it)->Type() == "recurrent" || (*it)->Type() == "while") { int step_block_idx = (*it)->GetBlockAttr("step_block"); BlockDescBind* backward_block = CreateStepBlock( program_desc, no_grad_vars, grad_to_var, step_block_idx); @@ -410,6 +421,15 @@ std::vector> MakeBlockBackward( op_grads = MakeOpGrad(*it, no_grad_vars, grad_to_var); } + if (VLOG_IS_ON(10)) { + std::ostringstream sout; + sout << "Made "; + for (auto& op_grad : op_grads) { + sout << op_grad->Type() << " "; + } + VLOG(10) << sout.str(); + } + for (const auto& desc : op_grads) { for (const std::string& out_name : desc->OutputArgumentNames()) { if (out_name.find("@GRAD") == std::string::npos) { @@ -425,6 +445,8 @@ std::vector> MakeBlockBackward( op_grads.begin(), op_grads.end(), std::back_inserter(backward_descs), [](std::unique_ptr& ptr) { return std::move(ptr); }); } + + VLOG(5) << "Appending Sums"; // Check whether some variables are written more than once std::list>> pending_sum_ops; for (const auto& dup : dup_out_ops) { @@ -432,16 +454,22 @@ std::vector> MakeBlockBackward( const std::vector dup_op = dup.second; if (out_name != kEmptyVarName && dup_op.size() > 1) { std::vector sum_op_inputs; + std::string next_g_name = out_name; for (size_t i = 0; i < dup_op.size(); ++i) { + VLOG(10) << backward_descs[dup_op[i]]->Type() << " has " << out_name + << " duplicated"; std::string new_name = out_name + "@RENAME@" + std::to_string(i); - backward_descs[dup_op[i]]->Rename(out_name, new_name); + backward_descs[dup_op[i]]->RenameOutput(out_name, new_name); + backward_descs[dup_op[i]]->RenameInput(out_name, next_g_name); sum_op_inputs.emplace_back(new_name); + next_g_name = sum_op_inputs.back(); } std::unique_ptr sum_op(new OpDescBind( "sum", {{"X", sum_op_inputs}}, {{"Out", {out_name}}}, {})); pending_sum_ops.push_back({dup_op.back(), std::move(sum_op)}); } } + pending_sum_ops.sort( [](const std::pair>& a, const std::pair>& b) { @@ -452,6 +480,8 @@ std::vector> MakeBlockBackward( std::move(p.second)); } + VLOG(5) << "MakeBlockBackward Finished"; + return backward_descs; } diff --git a/paddle/framework/data_type.h b/paddle/framework/data_type.h index 3ec88d7a72..be144d8fc0 100644 --- a/paddle/framework/data_type.h +++ b/paddle/framework/data_type.h @@ -29,6 +29,8 @@ inline DataType ToDataType(std::type_index type) { return DataType::INT32; } else if (typeid(int64_t).hash_code() == type.hash_code()) { return DataType::INT64; + } else if (typeid(bool).hash_code() == type.hash_code()) { + return DataType::BOOL; } else { PADDLE_THROW("Not supported"); } diff --git a/paddle/framework/ddim.cc b/paddle/framework/ddim.cc index 53b899a239..8b6f42b82d 100644 --- a/paddle/framework/ddim.cc +++ b/paddle/framework/ddim.cc @@ -60,8 +60,7 @@ void make_ddim(DDim& ddim, const int64_t* dims, int n) { ddim = make_dim<9>(dims); break; default: - throw std::invalid_argument( - "Dynamic dimensions must have between [1, 9] dimensions."); + PADDLE_THROW("Dynamic dimensions must have between [1, 9] dimensions."); } } diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index 2fcf41d69f..adedd8cb0e 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -120,6 +120,7 @@ void Executor::Run(const ProgramDescBind& pdesc, Scope* scope, int block_id, for (auto& op_desc : block.AllOps()) { auto op = paddle::framework::OpRegistry::CreateOp(*op_desc); + VLOG(10) << op->DebugString(); op->Run(*local_scope, *device); } if (create_local_scope) { diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc index 39c8def82e..48cd131550 100644 --- a/paddle/framework/op_desc.cc +++ b/paddle/framework/op_desc.cc @@ -235,6 +235,23 @@ void OpDescBind::Rename(const std::string &old_name, need_update_ = true; } +void OpDescBind::RenameOutput(const std::string &old_name, + const std::string &new_name) { + for (auto &output : outputs_) { + std::replace(output.second.begin(), output.second.end(), old_name, + new_name); + } + need_update_ = true; +} + +void OpDescBind::RenameInput(const std::string &old_name, + const std::string &new_name) { + for (auto &input : inputs_) { + std::replace(input.second.begin(), input.second.end(), old_name, new_name); + } + need_update_ = true; +} + struct SetAttrDescVisitor : public boost::static_visitor { explicit SetAttrDescVisitor(OpDesc::Attr *attr) : attr_(attr) {} mutable OpDesc::Attr *attr_; @@ -448,7 +465,12 @@ const std::vector &CompileTimeInferShapeContext::Outputs( DDim CompileTimeInferShapeContext::GetDim(const std::string &name) const { auto var = block_.FindVarRecursive(name); PADDLE_ENFORCE(var != nullptr, "Cannot find variable %s", name); - return framework::make_ddim(var->Shape()); + try { + return framework::make_ddim(var->Shape()); + } catch (...) { + VLOG(5) << "GetDim of variable " << name << " error"; + std::rethrow_exception(std::current_exception()); + } } void CompileTimeInferShapeContext::SetDim(const std::string &name, diff --git a/paddle/framework/op_desc.h b/paddle/framework/op_desc.h index e3e96441bb..da032319af 100644 --- a/paddle/framework/op_desc.h +++ b/paddle/framework/op_desc.h @@ -73,6 +73,10 @@ class OpDescBind { void Rename(const std::string &old_name, const std::string &new_name); + void RenameOutput(const std::string &old_name, const std::string &new_name); + + void RenameInput(const std::string &old_name, const std::string &new_name); + // Only be used in C++ const AttributeMap &GetAttrMap() const; diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 3276f8af39..93467ab8ac 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -403,19 +403,6 @@ class RuntimeInferShapeContext : public InferShapeContext { void OperatorWithKernel::Run(const Scope& scope, const platform::DeviceContext& dev_ctx) const { - if (VLOG_IS_ON(1)) { - auto inputs = this->InputVars(); - auto outputs = this->OutputVars(true); - std::ostringstream sout; - sout << "Run operator " << this->Type() << " From ["; - std::ostream_iterator out_it(sout, ","); - std::copy(inputs.begin(), inputs.end(), out_it); - sout << "] to ["; - std::copy(outputs.begin(), outputs.end(), out_it); - sout << "]"; - VLOG(1) << sout.str(); - } - RuntimeInferShapeContext infer_shape_ctx(*this, scope); this->InferShape(&infer_shape_ctx); diff --git a/paddle/framework/scope.cc b/paddle/framework/scope.cc index 9428b8a07e..9ad6272c99 100644 --- a/paddle/framework/scope.cc +++ b/paddle/framework/scope.cc @@ -38,11 +38,12 @@ Scope& Scope::NewScope() const { Variable* Scope::Var(const std::string& name) { auto iter = vars_.find(name); if (iter != vars_.end()) { + VLOG(3) << "Get existing variable " << name; return iter->second; } Variable* v = new Variable(); vars_[name] = v; - VLOG(3) << "Create variable " << name << " on scope"; + VLOG(3) << "Create variable " << name; v->name_ = &(vars_.find(name)->first); return v; } diff --git a/paddle/framework/shape_inference.h b/paddle/framework/shape_inference.h index 7d36ead2ca..05dc47f06a 100644 --- a/paddle/framework/shape_inference.h +++ b/paddle/framework/shape_inference.h @@ -53,6 +53,10 @@ class InferShapeContext { virtual bool IsRuntime() const = 0; + // Note: In while op, we need this to be public + void SetDims(const std::vector &names, + const std::vector &dims); + protected: virtual framework::DDim GetDim(const std::string &name) const = 0; virtual void SetDim(const std::string &name, const framework::DDim &dim) = 0; @@ -60,9 +64,6 @@ class InferShapeContext { std::vector GetDims( const std::vector &names) const; - void SetDims(const std::vector &names, - const std::vector &dims); - std::vector GetVarTypes( const std::vector &names) const; diff --git a/paddle/operators/array_operator.h b/paddle/operators/array_operator.h index 666043e824..233a81198e 100644 --- a/paddle/operators/array_operator.h +++ b/paddle/operators/array_operator.h @@ -42,6 +42,7 @@ class ArrayOp : public framework::OperatorBase { } else { offset = static_cast(*i_tensor.data()); } + VLOG(10) << " Offset = " << offset; return offset; } }; diff --git a/paddle/operators/detail/safe_ref.h b/paddle/operators/detail/safe_ref.h new file mode 100644 index 0000000000..b71af17309 --- /dev/null +++ b/paddle/operators/detail/safe_ref.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#pragma once + +namespace paddle { +namespace operators { +namespace detail { +/** + * Get Reference From Pointer with check. The error message is printf format, + * and passed by `args` + */ +template +inline T &Ref(T *ptr, ARGS &&... args) { + PADDLE_ENFORCE(ptr != nullptr, args...); + return *ptr; +} +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/fill_constant_batch_size_like_op.cc b/paddle/operators/fill_constant_batch_size_like_op.cc index 85871ebbfc..985b5d1e86 100644 --- a/paddle/operators/fill_constant_batch_size_like_op.cc +++ b/paddle/operators/fill_constant_batch_size_like_op.cc @@ -101,4 +101,7 @@ REGISTER_OPERATOR(fill_constant_batch_size_like, REGISTER_OP_CPU_KERNEL( fill_constant_batch_size_like, ops::FillConstantBatchSizeLikeOpKernel, - ops::FillConstantBatchSizeLikeOpKernel); + ops::FillConstantBatchSizeLikeOpKernel, + ops::FillConstantBatchSizeLikeOpKernel, + ops::FillConstantBatchSizeLikeOpKernel); diff --git a/paddle/operators/fill_constant_batch_size_like_op.cu.cc b/paddle/operators/fill_constant_batch_size_like_op.cu.cc index 87e3697e28..9e7a1eeab8 100644 --- a/paddle/operators/fill_constant_batch_size_like_op.cu.cc +++ b/paddle/operators/fill_constant_batch_size_like_op.cu.cc @@ -19,4 +19,7 @@ namespace ops = paddle::operators; REGISTER_OP_GPU_KERNEL( fill_constant_batch_size_like, ops::FillConstantBatchSizeLikeOpKernel, - ops::FillConstantBatchSizeLikeOpKernel); + ops::FillConstantBatchSizeLikeOpKernel, + ops::FillConstantBatchSizeLikeOpKernel, + ops::FillConstantBatchSizeLikeOpKernel); diff --git a/paddle/operators/fill_zeros_like_op.cc b/paddle/operators/fill_zeros_like_op.cc index 8ab39d4fb0..95fb5932b8 100644 --- a/paddle/operators/fill_zeros_like_op.cc +++ b/paddle/operators/fill_zeros_like_op.cc @@ -54,5 +54,8 @@ namespace ops = paddle::operators; REGISTER_OP_WITHOUT_GRADIENT(fill_zeros_like, ops::FillZerosLikeOp, ops::FillZerosLikeOpMaker); REGISTER_OP_CPU_KERNEL( - fill_zeros_like, - ops::FillZerosLikeKernel); + fill_zeros_like, ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel); diff --git a/paddle/operators/fill_zeros_like_op.cu.cc b/paddle/operators/fill_zeros_like_op.cu.cc index 2adb40cf90..1501a17441 100644 --- a/paddle/operators/fill_zeros_like_op.cu.cc +++ b/paddle/operators/fill_zeros_like_op.cu.cc @@ -17,5 +17,8 @@ namespace ops = paddle::operators; REGISTER_OP_GPU_KERNEL( - fill_zeros_like, - ops::FillZerosLikeKernel); + fill_zeros_like, ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel, + ops::FillZerosLikeKernel); diff --git a/paddle/operators/math/math_function.cc b/paddle/operators/math/math_function.cc index 5ee0917886..2e333a8cde 100644 --- a/paddle/operators/math/math_function.cc +++ b/paddle/operators/math/math_function.cc @@ -250,6 +250,8 @@ void axpy(const platform::DeviceContext& context, template struct SetConstant; template struct SetConstant; template struct SetConstant; +template struct SetConstant; +template struct SetConstant; #define DEFINE_CPU_TRANS(RANK) \ template struct Transpose; \ diff --git a/paddle/operators/math/math_function.cu b/paddle/operators/math/math_function.cu index 38c04b97f9..58356a4b77 100644 --- a/paddle/operators/math/math_function.cu +++ b/paddle/operators/math/math_function.cu @@ -256,6 +256,8 @@ void axpy(const platform::DeviceContext& context, template struct SetConstant; template struct SetConstant; template struct SetConstant; +template struct SetConstant; +template struct SetConstant; #define DEFINE_GPU_TRANS(RANK) \ template struct Transpose; \ diff --git a/paddle/operators/sum_op.cc b/paddle/operators/sum_op.cc index 9837f325e3..c2b7632b28 100644 --- a/paddle/operators/sum_op.cc +++ b/paddle/operators/sum_op.cc @@ -12,6 +12,7 @@ limitations under the License. */ #include "paddle/operators/sum_op.h" #include #include "paddle/framework/var_type_inference.h" +#include "paddle/operators/detail/safe_ref.h" namespace paddle { namespace operators { @@ -59,13 +60,16 @@ class SumOp : public framework::OperatorWithKernel { x_vars[0]->Get().value().type()), ctx.device_context()); } else if (x_vars[0]->IsType()) { - auto& array = x_vars[0]->Get(); - for (auto& each : array) { - if (each.numel() != 0) { - return framework::OpKernelType(framework::ToDataType(each.type()), - ctx.device_context()); + for (auto& x_var : x_vars) { + auto& array = x_var->Get(); + for (auto& each : array) { + if (each.numel() != 0) { + return framework::OpKernelType(framework::ToDataType(each.type()), + ctx.device_context()); + } } } + PADDLE_THROW("Cannot find the input data type by all input data"); } PADDLE_THROW("Unexpected branch. Input type is %s", x_vars[0]->Type().name()); @@ -96,6 +100,11 @@ class SumOpVarTypeInference : public framework::VarTypeInference { auto& inputs = op_desc.Input("X"); auto var_type = framework::VarDesc::SELECTED_ROWS; + for (auto& name : op_desc.Input("X")) { + VLOG(10) << name << " " + << block->FindRecursiveOrCreateVar(name)->GetType(); + } + bool any_input_is_lod_tensor = std::any_of( inputs.begin(), inputs.end(), [block](const std::string& name) { return block->FindRecursiveOrCreateVar(name)->GetType() == @@ -103,7 +112,7 @@ class SumOpVarTypeInference : public framework::VarTypeInference { }); auto is_tensor_array = [block](const std::string& name) { - return block->FindRecursiveOrCreateVar(name)->GetType() == + return detail::Ref(block->FindRecursiveOrCreateVar(name)).GetType() == framework::VarDesc::LOD_TENSOR_ARRAY; }; @@ -113,14 +122,26 @@ class SumOpVarTypeInference : public framework::VarTypeInference { std::all_of(inputs.begin(), inputs.end(), is_tensor_array); if (any_input_is_tensor_array) { - PADDLE_ENFORCE(all_inputs_are_tensor_array); + if (!all_inputs_are_tensor_array) { + std::ostringstream os; + for (auto& each : inputs) { + os << " " << each << " type is " + << detail::Ref(block->FindRecursiveOrCreateVar(each)).GetType() + << "\n"; + } + PADDLE_ENFORCE(all_inputs_are_tensor_array, + "Not all inputs are tensor array:\n%s", os.str()); + } var_type = framework::VarDesc::LOD_TENSOR_ARRAY; } else if (any_input_is_lod_tensor) { var_type = framework::VarDesc::LOD_TENSOR; } auto out_var_name = op_desc.Output("Out").front(); - block->FindRecursiveOrCreateVar(out_var_name)->SetType(var_type); + auto& out_var = detail::Ref(block->FindRecursiveOrCreateVar(out_var_name)); + out_var.SetType(var_type); + auto& in_var = detail::Ref(block->FindVarRecursive(inputs.front())); + out_var.SetDataType(in_var.GetDataType()); } }; diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index 62e15604c4..ae1b48d7a8 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/operators/array_operator.h" - +#include "paddle/operators/detail/safe_ref.h" namespace paddle { namespace operators { @@ -33,6 +33,8 @@ class WriteToArrayOp : public ArrayOp { auto *out = scope.FindVar(Output("Out"))->GetMutable(); if (offset >= out->size()) { + VLOG(10) << "Resize " << Output("Out") << " from " << out->size() + << " to " << offset + 1; out->resize(offset + 1); } auto *out_tensor = &out->at(offset); @@ -85,11 +87,15 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { public: void operator()(const framework::OpDescBind &op_desc, framework::BlockDescBind *block) const override { - for (auto &out_var : op_desc.OutputArgumentNames()) { - VLOG(10) << "Set Variable " << out_var << " as LOD_TENSOR_ARRAY"; - block->FindRecursiveOrCreateVar(out_var)->SetType( - framework::VarDesc::LOD_TENSOR_ARRAY); - } + auto x_name = op_desc.Input("X")[0]; + auto out_name = op_desc.Output("Out")[0]; + VLOG(10) << "Set Variable " << out_name << " as LOD_TENSOR_ARRAY"; + auto &out = detail::Ref(block->FindRecursiveOrCreateVar(out_name), + "Cannot found %s", out_name); + out.SetType(framework::VarDesc::LOD_TENSOR_ARRAY); + auto &x = + detail::Ref(block->FindVarRecursive(x_name), "Cannot found %s", x_name); + out.SetDataType(x.GetDataType()); } }; @@ -107,11 +113,11 @@ class ReadFromArrayOp : public ArrayOp { auto &x_array = x->Get(); auto *out = scope.FindVar(Output("Out")); PADDLE_ENFORCE(out != nullptr, "Out must be set"); - auto *out_tesnor = out->GetMutable(); + auto *out_tensor = out->GetMutable(); size_t offset = GetOffset(scope, dev_ctx); PADDLE_ENFORCE_LT(offset, x_array.size()); - out_tesnor->CopyFrom(x_array[offset], dev_ctx.GetPlace(), dev_ctx); - out_tesnor->set_lod(x_array[offset].lod()); + out_tensor->CopyFrom(x_array[offset], dev_ctx.GetPlace(), dev_ctx); + out_tensor->set_lod(x_array[offset].lod()); } }; diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 4ca6c8507a..dcc59f5ff2 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -14,8 +14,10 @@ #include #include "paddle/framework/executor.h" +#include "paddle/framework/lod_tensor_array.h" #include "paddle/framework/op_registry.h" #include "paddle/framework/operator.h" +#include "paddle/operators/detail/safe_ref.h" namespace paddle { namespace operators { @@ -26,8 +28,9 @@ using LoDTensor = framework::LoDTensor; constexpr char kStepBlock[] = "step_block"; constexpr char kCondition[] = "Condition"; constexpr char kStepScopes[] = "StepScopes"; -constexpr char kParamGrads[] = "X@Grad"; constexpr char kParameters[] = "X"; +constexpr char kParamGrads[] = "X@GRAD"; +constexpr char kOutputs[] = "Out"; class WhileOp : public framework::OperatorBase { public: @@ -71,9 +74,9 @@ class WhileOpMaker : public framework::OpProtoAndCheckerMaker { kCondition, "(Bool) An scalar. When it's False, the While Op will be terminated.") .AsDuplicable(); - AddOutput("Out", + AddOutput(kOutputs, "A set of variables, which will be assigned with values " - "generated by perators inside the block of While Op.") + "generated by the operators inside the block of While Op.") .AsDuplicable(); AddOutput(kStepScopes, "(StepScopeVar) A vector of local scope, which size equals the " @@ -104,17 +107,64 @@ class WhileGradOp : public framework::OperatorBase { auto *step_scopes = scope.FindVar(Input(kStepScopes))->GetMutable(); + auto outside_og_names = Inputs(framework::GradVarName(kOutputs)); + auto inside_og_names = + Attr>("original_output_grad"); + + PADDLE_ENFORCE_EQ(outside_og_names.size(), inside_og_names.size()); + for (auto cur_scope_iter = step_scopes->rbegin(); cur_scope_iter != step_scopes->rend(); ++cur_scope_iter) { + VLOG(3) << "Start backward at time_step " + << cur_scope_iter - step_scopes->rbegin(); + framework::Scope &cur_scope = **cur_scope_iter; + // Link OG from outside to inside + for (size_t i = 0; i < outside_og_names.size(); ++i) { + auto outside_og_name = outside_og_names[i]; + auto inside_og_name = inside_og_names[i]; + VLOG(10) << "Linking outside " << outside_og_name << " --> inside " + << inside_og_name; + auto &og_outside = detail::Ref(scope.FindVar(outside_og_name)); + auto &og_inside = detail::Ref(cur_scope.Var(inside_og_name)); + if (og_outside.Type().hash_code() == + typeid(framework::LoDTensor).hash_code()) { + auto &outside_tensor = og_outside.Get(); + auto &inside_tensor = + detail::Ref(og_inside.GetMutable()); + inside_tensor.set_lod(outside_tensor.lod()); + inside_tensor.ShareDataWith(outside_tensor); + } else if (og_outside.Type().hash_code() == + typeid(framework::LoDTensorArray).hash_code()) { + auto &outside_array = og_outside.Get(); + auto &inside_array = + detail::Ref(og_inside.GetMutable()); + VLOG(10) << outside_og_name << " size = " << outside_array.size(); + inside_array.resize(outside_array.size()); + + for (size_t j = 0; j < inside_array.size(); ++j) { + VLOG(10) << j << " " << outside_array[j].numel(); + if (outside_array[j].numel() != 0) { + inside_array[j].set_lod(outside_array[j].lod()); + inside_array[j].ShareDataWith(outside_array[j]); + } else { + PADDLE_ENFORCE_EQ(inside_array[j].numel(), 0); + } + } + } + } + executor.Run(*program, *cur_scope_iter, block->ID(), false); auto &pg_names = Outputs(kParamGrads); auto &p_names = Inputs(kParameters); PADDLE_ENFORCE_EQ(pg_names.size(), p_names.size()); - for (size_t prog_id = 0; prog_id < pg_names.size(); ++prog_id) { - auto inside_grad_name = framework::GradVarName(p_names[prog_id]); + for (size_t param_id = 0; param_id < pg_names.size(); ++param_id) { + if (pg_names[param_id] == framework::kEmptyVarName) { + continue; // iterator doesn't have gradient + } + auto inside_grad_name = framework::GradVarName(p_names[param_id]); - // // TODO(tonyyang-savil: Not sure we need the following + // // TODO(tonyyang-svail): Not sure we need the following // // If does not compute gradient of that variable inside rnn, // just // // continue @@ -126,7 +176,7 @@ class WhileGradOp : public framework::OperatorBase { // zero gradient variable in step 0 if (cur_scope_iter == step_scopes->rbegin()) { auto *var = (*cur_scope_iter)->FindVar(inside_grad_name); - PADDLE_ENFORCE_NOT_NULL(var); + PADDLE_ENFORCE_NOT_NULL(var, "Can not find var %s", inside_grad_name); if (var->IsType()) { auto &inside_tensor = var->Get(); framework::AttributeMap attrs; @@ -135,27 +185,18 @@ class WhileGradOp : public framework::OperatorBase { attrs["value"] = 0.0f; auto zero_op = framework::OpRegistry::CreateOp( - "fill_constant", {}, {{"Out", {pg_names[prog_id]}}}, attrs); + "fill_constant", {}, {{"Out", {pg_names[param_id]}}}, attrs); zero_op->Run(scope, dev_ctx); } } // sum gradient - auto *outside_var = scope.FindVar(pg_names[prog_id]); - PADDLE_ENFORCE_NOT_NULL(outside_var); - auto &outside_tensor = *outside_var->GetMutable(); - - std::string result_var_name; - auto *local_result_var = (*cur_scope_iter)->Var(&result_var_name); - auto &local_result_tensor = - *local_result_var->GetMutable(); - - local_result_tensor.ShareDataWith(outside_tensor); - + auto new_inside_name = cur_scope.Rename(inside_grad_name); auto sum_op = framework::OpRegistry::CreateOp( - "sum", {{"X", {result_var_name, inside_grad_name}}}, - {{"Out", {result_var_name}}}, {}); - sum_op->Run(**cur_scope_iter, dev_ctx); + "sum", {{"X", {pg_names[param_id], new_inside_name}}}, + {{"Out", {pg_names[param_id]}}}, {}); + sum_op->Run(cur_scope, dev_ctx); + cur_scope.Rename(new_inside_name, inside_grad_name); } } } @@ -169,29 +210,110 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { virtual std::unique_ptr Apply() const { auto *grad = new framework::OpDescBind(); grad->SetType("while_grad"); - for (auto &input_param : this->InputNames()) { - grad->SetInput(input_param, this->Input(input_param)); - grad->SetOutput(framework::GradVarName(input_param), - this->InputGrad(input_param)); + grad->SetInput(kParameters, Input(kParameters)); + grad->SetOutput( + framework::GradVarName(kParameters), + InputGrad(kParameters, /*do not drop empty gradient*/ false)); + grad->SetInput(kOutputs, Output(kOutputs)); + + // OG should be re-calculated by step blocks, since many outputs of while op + // do not need to calculate gradients. + std::unordered_set block_ins; + { + for (auto &p : Input(kParameters)) { + block_ins.insert(p); + } + for (auto &o : Output(kOutputs)) { + block_ins.insert(o); + } } + std::unordered_set extra_inputs; + for (size_t i = 0; i < grad_block_[0]->OpSize(); ++i) { + for (auto &input_name : grad_block_[0]->Op(i)->InputArgumentNames()) { + if (block_ins.find(input_name) != block_ins.end()) { + continue; + } + extra_inputs.insert(input_name); + } - for (auto &output_param : this->OutputNames()) { - grad->SetInput(output_param, this->Output(output_param)); - if (output_param != kStepScopes) { - grad->SetInput(framework::GradVarName(output_param), - this->OutputGrad(output_param)); + for (auto &output_name : grad_block_[0]->Op(i)->OutputArgumentNames()) { + block_ins.insert(output_name); } } + + std::vector extra_inputs_list; + extra_inputs_list.resize(extra_inputs.size()); + std::copy(extra_inputs.begin(), extra_inputs.end(), + extra_inputs_list.begin()); + grad->SetInput(framework::GradVarName(kOutputs), extra_inputs_list); + grad->SetInput(kStepScopes, Output(kStepScopes)); grad->SetAttrMap(this->Attrs()); grad->SetBlockAttr(kStepBlock, *grad_block_[0]); + // record the original output gradient names, since the gradient name of + // while operator could be renamed. + grad->SetAttr("original_output_grad", extra_inputs_list); return std::unique_ptr(grad); } }; +class WhileGradOpVarTypeInference : public framework::VarTypeInference { + public: + void operator()(const framework::OpDescBind &op_desc, + framework::BlockDescBind *block) const override { + auto p_names = op_desc.Input(kParameters); + auto pg_names = op_desc.Output(framework::GradVarName(kParameters)); + + for (size_t i = 0; i < p_names.size(); ++i) { + auto &p_var = detail::Ref(block->FindVarRecursive(p_names[i])); + auto *g_var = block->FindVarRecursive(pg_names[i]); + if (g_var != nullptr) { // Gradient could be @EMPTY@ + VLOG(5) << "Setting " << pg_names[i] << " following " << p_names[i] + << " type: " << p_var.GetType(); + g_var->SetType(p_var.GetType()); + g_var->SetDataType(p_var.GetDataType()); + } + } + } +}; + +class WhileGradOpShapeInference : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *ctx) const override { + ctx->HasInputs(kParameters); + ctx->HasOutputs(framework::GradVarName(kParameters)); + ctx->HasInputs(kOutputs); + ctx->HasInputs(framework::GradVarName(kOutputs)); + + auto p_names = ctx->Inputs(kParameters); + auto pg_names = ctx->Outputs(kParamGrads); + auto dims = ctx->GetInputsDim(kParameters); + auto var_types = ctx->GetInputsVarType(kParameters); + std::vector names_to_set; + std::vector dims_to_set; + for (size_t i = 0; i < p_names.size(); ++i) { + if (pg_names[i] == framework::kEmptyVarName) { + continue; + } + if (var_types[i] == framework::VarDesc::LOD_TENSOR) { + names_to_set.push_back(pg_names[i]); + dims_to_set.push_back(dims[i]); + } else if (var_types[i] == framework::VarDesc::LOD_TENSOR_ARRAY) { + // not sure how to set the dim of LOD_TENSOR_ARRAY + names_to_set.push_back(pg_names[i]); + dims_to_set.push_back(dims[i]); + } + } + ctx->SetDims(names_to_set, dims_to_set); + } +}; + } // namespace operators } // namespace paddle REGISTER_OPERATOR(while, paddle::operators::WhileOp, paddle::operators::WhileOpMaker, paddle::operators::WhileGradOpDescMaker); +REGISTER_OPERATOR(while_grad, paddle::operators::WhileGradOp, + paddle::operators::WhileGradOpShapeInference, + paddle::operators::WhileGradOpVarTypeInference); diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index f20567243a..a6eca2d719 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -12,9 +12,9 @@ def unique_name(prefix): return "_".join([prefix, str(uid)]) -def _debug_string_(proto): +def _debug_string_(proto, throw_on_error=True): error_fields = list() - if not proto.IsInitialized(error_fields): + if not proto.IsInitialized(error_fields) and throw_on_error: raise ValueError("{0} are not initialized\nThe message is {1}".format( error_fields, proto)) return proto.__str__() @@ -101,9 +101,12 @@ class Variable(object): self.stop_gradient = stop_gradient def __str__(self): + return self.to_string(True) + + def to_string(self, throw_on_error): protostr = self.desc.serialize_to_string() proto = framework_pb2.VarDesc.FromString(str(protostr)) - return _debug_string_(proto) + return _debug_string_(proto, throw_on_error) __repr__ = __str__ @@ -291,10 +294,13 @@ class Operator(object): self.desc.infer_var_type(self.block.desc) self.desc.infer_shape(self.block.desc) - def __str__(self): + def to_string(self, throw_on_error): protostr = self.desc.serialize_to_string() proto = framework_pb2.OpDesc.FromString(str(protostr)) - return _debug_string_(proto) + return _debug_string_(proto, throw_on_error) + + def __str__(self): + return self.to_string(True) __repr__ = __str__ @@ -349,9 +355,12 @@ class Block(object): self.program = program def __str__(self): + return self.to_string(True) + + def to_string(self, throw_on_error): protostr = self.desc.serialize_to_string() proto = framework_pb2.BlockDesc.FromString(str(protostr)) - return _debug_string_(proto) + return _debug_string_(proto, throw_on_error) __repr__ = __str__ @@ -454,9 +463,12 @@ class Program(object): self.current_block_idx = 0 def __str__(self): + return self.to_string(True) + + def to_string(self, throw_on_error): protostr = self.desc.serialize_to_string() proto = framework_pb2.ProgramDesc.FromString(str(protostr)) - return _debug_string_(proto) + return _debug_string_(proto, throw_on_error) def clone(self): p = Program() @@ -512,7 +524,14 @@ class Program(object): assert isinstance(target, Variable) if no_grad_set is None: no_grad_set = set() - param_to_grad_info = self.desc.append_backward(target.desc, no_grad_set) + try: + param_to_grad_info = self.desc.append_backward(target.desc, + no_grad_set) + except Exception as e: + raise core.EnforceNotMet( + str(e) + "\nCurrent protobuf is\n{0}".format( + self.to_string(False))) + self.sync_with_cpp() return param_to_grad_info diff --git a/python/paddle/v2/fluid/net_drawer.py b/python/paddle/v2/fluid/net_drawer.py index 17ad547c2b..94fdd5e389 100644 --- a/python/paddle/v2/fluid/net_drawer.py +++ b/python/paddle/v2/fluid/net_drawer.py @@ -66,10 +66,13 @@ def parse_graph(program, graph, var_dict, **kwargs): if not var_dict.has_key(var): var_dict[var] = "Feed" + temp_id = 0 proto = framework_pb2.ProgramDesc.FromString( program.desc.serialize_to_string()) for block in proto.blocks: for op in block.ops: + op.type = op.type + "_" + str(temp_id) + temp_id += 1 graph.node(**draw_node(op)) for o in op.outputs: for arg in o.arguments: @@ -78,6 +81,7 @@ def parse_graph(program, graph, var_dict, **kwargs): for arg in e.arguments: if var_dict.has_key(arg): graph.edge(**draw_edge(var_dict, op, e, arg)) + break # only plot the first block def draw_graph(startup_program, main_program, **kwargs): diff --git a/python/paddle/v2/fluid/tests/test_while_op.py b/python/paddle/v2/fluid/tests/test_while_op.py index 0f01acb3b9..84b432333f 100644 --- a/python/paddle/v2/fluid/tests/test_while_op.py +++ b/python/paddle/v2/fluid/tests/test_while_op.py @@ -2,6 +2,7 @@ import unittest import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor import paddle.v2.fluid.core as core +from paddle.v2.fluid.backward import append_backward_ops import numpy @@ -16,7 +17,7 @@ class TestWhileOp(unittest.TestCase): i = layers.zeros(shape=[1], dtype='int64') i.stop_gradient = True init = layers.zeros(shape=[10], dtype='float32') - mem_array = layers.array_write(init, i=i) + mem_array = layers.array_write(x=init, i=i) data_array = layers.array_write(x=d0, i=i) i = layers.increment(i) @@ -29,17 +30,23 @@ class TestWhileOp(unittest.TestCase): i.stop_gradient = True array_len = layers.fill_constant(shape=[1], dtype='int64', value=3) + array_len.stop_gradient = True cond = layers.less_than(x=i, y=array_len) while_op = layers.While(cond=cond) with while_op.block(): d = layers.array_read(array=data_array, i=i) prev = layers.array_read(array=mem_array, i=i) - i = layers.increment(x=i, in_place=True) result = layers.sums(input=[d, prev]) + + i = layers.increment(x=i, in_place=True) layers.array_write(result, i=i, array=mem_array) layers.less_than(x=i, y=array_len, cond=cond) - sum_result = layers.array_read(mem_array, i=array_len) + + sum_result = layers.array_read(array=mem_array, i=i) + loss = layers.mean(x=sum_result) + + append_backward_ops(loss) cpu = core.CPUPlace() exe = Executor(cpu) From b34f21bdf787a01aa271af260dadacbcda0165a6 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Thu, 16 Nov 2017 19:25:51 +0800 Subject: [PATCH 21/43] The pool_limit_size need be zero in mobile inference. --- paddle/math/Storage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/paddle/math/Storage.cpp b/paddle/math/Storage.cpp index 4adaaef983..a2ef731ecb 100644 --- a/paddle/math/Storage.cpp +++ b/paddle/math/Storage.cpp @@ -17,9 +17,13 @@ limitations under the License. */ #include "paddle/utils/StringUtil.h" #include "paddle/utils/Util.h" +#ifndef PADDLE_MOBILE_INFERENCE DEFINE_int32(pool_limit_size, 536870912, "maximum memory size managed by a memory pool, default is 512M"); +#else +DEFINE_int32(pool_limit_size, 0, "default is 0"); +#endif namespace paddle { From 186581d2cc598621df2d1f3941160d1f1051bf06 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Thu, 16 Nov 2017 20:18:31 +0800 Subject: [PATCH 22/43] add is empty op (#5639) --- paddle/operators/is_empty_op.cc | 67 +++++++++++++++++++ .../v2/framework/tests/test_is_empty_op.py | 43 ++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 paddle/operators/is_empty_op.cc create mode 100644 python/paddle/v2/framework/tests/test_is_empty_op.py diff --git a/paddle/operators/is_empty_op.cc b/paddle/operators/is_empty_op.cc new file mode 100644 index 0000000000..54fecf44e8 --- /dev/null +++ b/paddle/operators/is_empty_op.cc @@ -0,0 +1,67 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "paddle/framework/op_registry.h" +#include "paddle/framework/operator.h" + +namespace paddle { +namespace operators { + +constexpr char kInput[] = "X"; +constexpr char kOutput[] = "Out"; + +class IsEmptyOp : public framework::OperatorBase { + public: + IsEmptyOp(const std::string &type, const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + // get input + auto *var = scope.FindVar(Input(kInput)); + PADDLE_ENFORCE_NOT_NULL(var); + auto &tensor = var->Get(); + // get output + auto *out = scope.FindVar(Output(kOutput)); + PADDLE_ENFORCE_NOT_NULL(out); + auto *out_tensor = out->GetMutable(); + + out_tensor->Resize({1}); + out_tensor->mutable_data(platform::CPUPlace())[0] = + framework::product(tensor.dims()) == 0; + } +}; + +class IsEmptyOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + IsEmptyOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput(kInput, "(Tensor) Tensor which is to be checked."); + AddOutput(kOutput, "(Tensor) a boolean Tensor that indicate empty or not."); + AddComment(R"DOC( +IsEmpty Operator which checks whether a tensor is empty. + +It will just return product(tensor.ddims()) > 0; + )DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +REGISTER_OP_WITHOUT_GRADIENT(is_empty, paddle::operators::IsEmptyOp, + paddle::operators::IsEmptyOpProtoMaker); diff --git a/python/paddle/v2/framework/tests/test_is_empty_op.py b/python/paddle/v2/framework/tests/test_is_empty_op.py new file mode 100644 index 0000000000..129d1c1944 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_is_empty_op.py @@ -0,0 +1,43 @@ +import unittest +import numpy as np +from paddle.v2.framework.op import Operator +import paddle.v2.framework.core as core + + +def create_tensor(scope, name, np_data): + tensor = scope.var(name).get_tensor() + tensor.set_dims(np_data.shape) + tensor.set(np_data, core.CPUPlace()) + return tensor + + +class TestIsEmptyOp(unittest.TestCase): + def setUp(self): + self.scope = core.Scope() + # create input variables + np_data0 = np.array([0, 1, 2]) + create_tensor(self.scope, "X0", np_data0) + + np_data1 = np.array([1]) + t = create_tensor(self.scope, "X1", np_data1) + t.set_dims([0]) + + # create output variables + self.scope.var("out") + + def test_no_empty(self): + self.one_case("X0", False) + + def test_empty(self): + self.one_case("X1", True) + + def one_case(self, input, target): + op = Operator(type="is_empty", X=input, Out="out") + ctx = core.DeviceContext.create(core.CPUPlace()) + op.run(self.scope, ctx) + out = self.scope.var("out").get_tensor() + self.assertEqual(np.array(out)[0], target) + + +if __name__ == "__main__": + unittest.main() From 3d080f3ad53d10e858a1bcd6c34a8ff07c56d7b0 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Thu, 16 Nov 2017 22:34:54 +0800 Subject: [PATCH 23/43] =?UTF-8?q?Refine=20cmake=20about=20CUDA=20to=20auto?= =?UTF-8?q?matically=C2=A0detect=20GPU=20arch=20by=20default.=201.=20Autom?= =?UTF-8?q?atically=20detect=20GPU=20arch=20by=20default.=202.=20Specify?= =?UTF-8?q?=20-DCUDA=5FARCH=5FNAME=3DAll=20when=20releasing=20PaddlePaddle?= =?UTF-8?q?=20new=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +- cmake/cuda.cmake | 219 ++++++++++++++++++++++++++++++++++++++++++++++ cmake/flags.cmake | 55 ------------ 3 files changed, 220 insertions(+), 59 deletions(-) create mode 100644 cmake/cuda.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3582a1bc..fba5c58dc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,10 +158,7 @@ set(EXTERNAL_LIBS ) if(WITH_GPU) - list(APPEND EXTERNAL_LIBS ${CUDA_LIBRARIES} ${CUDA_rt_LIBRARY}) - if(NOT WITH_DSO) - list(APPEND EXTERNAL_LIBS ${CUDNN_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_curand_LIBRARY} ${NCCL_LIBRARY}) - endif(NOT WITH_DSO) + include(cuda) endif(WITH_GPU) if(WITH_MKLDNN) diff --git a/cmake/cuda.cmake b/cmake/cuda.cmake new file mode 100644 index 0000000000..5d0840f273 --- /dev/null +++ b/cmake/cuda.cmake @@ -0,0 +1,219 @@ +if(NOT WITH_GPU) + return() +endif() + +set(paddle_known_gpu_archs "20 21(20) 30 35 50 52 60 61 70") +set(paddle_known_gpu_archs7 "20 21(20) 30 35 50 52") +set(paddle_known_gpu_archs8 "20 21(20) 30 35 50 52 60 61") + +###################################################################################### +# A function for automatic detection of GPUs installed (if autodetection is enabled) +# Usage: +# detect_installed_gpus(out_variable) +function(detect_installed_gpus out_variable) + if(NOT CUDA_gpu_detect_output) + set(cufile ${PROJECT_BINARY_DIR}/detect_cuda_archs.cu) + + file(WRITE ${cufile} "" + "#include \n" + "int main() {\n" + " int count = 0;\n" + " if (cudaSuccess != cudaGetDeviceCount(&count)) return -1;\n" + " if (count == 0) return -1;\n" + " for (int device = 0; device < count; ++device) {\n" + " cudaDeviceProp prop;\n" + " if (cudaSuccess == cudaGetDeviceProperties(&prop, device))\n" + " std::printf(\"%d.%d \", prop.major, prop.minor);\n" + " }\n" + " return 0;\n" + "}\n") + + execute_process(COMMAND "${CUDA_NVCC_EXECUTABLE}" "-ccbin=${CUDA_HOST_COMPILER}" + "--run" "${cufile}" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/CMakeFiles/" + RESULT_VARIABLE nvcc_res OUTPUT_VARIABLE nvcc_out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(nvcc_res EQUAL 0) + # only keep the last line of nvcc_out + STRING(REGEX REPLACE ";" "\\\\;" nvcc_out "${nvcc_out}") + STRING(REGEX REPLACE "\n" ";" nvcc_out "${nvcc_out}") + list(GET nvcc_out -1 nvcc_out) + string(REPLACE "2.1" "2.1(2.0)" nvcc_out "${nvcc_out}") + set(CUDA_gpu_detect_output ${nvcc_out} CACHE INTERNAL "Returned GPU architetures from caffe_detect_gpus tool" FORCE) + endif() + endif() + + if(NOT CUDA_gpu_detect_output) + message(STATUS "Automatic GPU detection failed. Building for all known architectures.") + set(${out_variable} ${paddle_known_gpu_archs} PARENT_SCOPE) + else() + set(${out_variable} ${CUDA_gpu_detect_output} PARENT_SCOPE) + endif() +endfunction() + + +######################################################################## +# Function for selecting GPU arch flags for nvcc based on CUDA_ARCH_NAME +# Usage: +# select_nvcc_arch_flags(out_variable) +function(select_nvcc_arch_flags out_variable) + # List of arch names + set(archs_names "Kepler" "Maxwell" "Pascal" "All" "Manual") + set(archs_name_default "All") + if(NOT CMAKE_CROSSCOMPILING) + list(APPEND archs_names "Auto") + set(archs_name_default "Auto") + endif() + + # set CUDA_ARCH_NAME strings (so it will be seen as dropbox in CMake-Gui) + set(CUDA_ARCH_NAME ${archs_name_default} CACHE STRING "Select target NVIDIA GPU achitecture.") + set_property( CACHE CUDA_ARCH_NAME PROPERTY STRINGS "" ${archs_names} ) + mark_as_advanced(CUDA_ARCH_NAME) + + # verify CUDA_ARCH_NAME value + if(NOT ";${archs_names};" MATCHES ";${CUDA_ARCH_NAME};") + string(REPLACE ";" ", " archs_names "${archs_names}") + message(FATAL_ERROR "Only ${archs_names} architeture names are supported.") + endif() + + if(${CUDA_ARCH_NAME} STREQUAL "Manual") + set(CUDA_ARCH_BIN ${paddle_known_gpu_archs} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") + set(CUDA_ARCH_PTX "50" CACHE STRING "Specify 'virtual' PTX architectures to build PTX intermediate code for") + mark_as_advanced(CUDA_ARCH_BIN CUDA_ARCH_PTX) + else() + unset(CUDA_ARCH_BIN CACHE) + unset(CUDA_ARCH_PTX CACHE) + endif() + + if(${CUDA_ARCH_NAME} STREQUAL "Kepler") + set(cuda_arch_bin "30 35") + elseif(${CUDA_ARCH_NAME} STREQUAL "Maxwell") + set(cuda_arch_bin "50") + elseif(${CUDA_ARCH_NAME} STREQUAL "Pascal") + set(cuda_arch_bin "60 61") + elseif(${CUDA_ARCH_NAME} STREQUAL "Volta") + set(cuda_arch_bin "70") + elseif(${CUDA_ARCH_NAME} STREQUAL "All") + set(cuda_arch_bin ${paddle_known_gpu_archs}) + elseif(${CUDA_ARCH_NAME} STREQUAL "Auto") + detect_installed_gpus(cuda_arch_bin) + else() # (${CUDA_ARCH_NAME} STREQUAL "Manual") + set(cuda_arch_bin ${CUDA_ARCH_BIN}) + endif() + + # remove dots and convert to lists + string(REGEX REPLACE "\\." "" cuda_arch_bin "${cuda_arch_bin}") + string(REGEX REPLACE "\\." "" cuda_arch_ptx "${CUDA_ARCH_PTX}") + string(REGEX MATCHALL "[0-9()]+" cuda_arch_bin "${cuda_arch_bin}") + string(REGEX MATCHALL "[0-9]+" cuda_arch_ptx "${cuda_arch_ptx}") + list(REMOVE_DUPLICATES cuda_arch_bin) + list(REMOVE_DUPLICATES cuda_arch_ptx) + + set(nvcc_flags "") + set(nvcc_archs_readable "") + + # Tell NVCC to add binaries for the specified GPUs + foreach(arch ${cuda_arch_bin}) + if(arch MATCHES "([0-9]+)\\(([0-9]+)\\)") + # User explicitly specified PTX for the concrete BIN + list(APPEND nvcc_flags -gencode arch=compute_${CMAKE_MATCH_2},code=sm_${CMAKE_MATCH_1}) + list(APPEND nvcc_archs_readable sm_${CMAKE_MATCH_1}) + else() + # User didn't explicitly specify PTX for the concrete BIN, we assume PTX=BIN + list(APPEND nvcc_flags -gencode arch=compute_${arch},code=sm_${arch}) + list(APPEND nvcc_archs_readable sm_${arch}) + endif() + endforeach() + + # Tell NVCC to add PTX intermediate code for the specified architectures + foreach(arch ${cuda_arch_ptx}) + list(APPEND nvcc_flags -gencode arch=compute_${arch},code=compute_${arch}) + list(APPEND nvcc_archs_readable compute_${arch}) + endforeach() + + string(REPLACE ";" " " nvcc_archs_readable "${nvcc_archs_readable}") + set(${out_variable} ${nvcc_flags} PARENT_SCOPE) + set(${out_variable}_readable ${nvcc_archs_readable} PARENT_SCOPE) +endfunction() + +if(NOT CUDA_FOUND) + return() +endif() + +message(STATUS "CUDA detected: " ${CUDA_VERSION}) +if (${CUDA_VERSION} LESS 7.0) + set(paddle_known_gpu_archs ${paddle_known_gpu_archs}) +elseif (${CUDA_VERSION} LESS 8.0) # CUDA 7.x + set(paddle_known_gpu_archs ${paddle_known_gpu_archs7}) + list(APPEND CUDA_NVCC_FLAGS "-D_MWAITXINTRIN_H_INCLUDED") + list(APPEND CUDA_NVCC_FLAGS "-D__STRICT_ANSI__") +elseif (${CUDA_VERSION} LESS 9.0) # CUDA 8.x + set(paddle_known_gpu_archs ${paddle_known_gpu_archs8}) + list(APPEND CUDA_NVCC_FLAGS "-D_MWAITXINTRIN_H_INCLUDED") + list(APPEND CUDA_NVCC_FLAGS "-D__STRICT_ANSI__") + # CUDA 8 may complain that sm_20 is no longer supported. Suppress the + # warning for now. + list(APPEND CUDA_NVCC_FLAGS "-Wno-deprecated-gpu-targets") +endif() + +include_directories(${CUDA_INCLUDE_DIRS}) +list(APPEND EXTERNAL_LIBS ${CUDA_LIBRARIES} ${CUDA_rt_LIBRARY}) +if(NOT WITH_DSO) + list(APPEND EXTERNAL_LIBS ${CUDNN_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_curand_LIBRARY} ${NCCL_LIBRARY}) +endif(NOT WITH_DSO) + +# find libcuda.so and lbnvrtc.so +# For libcuda.so, we will find it under lib, lib64, and then the +# stubs folder, in case we are building on a system that does not +# have cuda driver installed. On windows, we also search under the +# folder lib/x64. + +find_library(CUDA_CUDA_LIB cuda + PATHS ${CUDA_TOOLKIT_ROOT_DIR} + PATH_SUFFIXES lib lib64 lib/stubs lib64/stubs lib/x64) +find_library(CUDA_NVRTC_LIB nvrtc + PATHS ${CUDA_TOOLKIT_ROOT_DIR} + PATH_SUFFIXES lib lib64 lib/x64) + +# setting nvcc arch flags +select_nvcc_arch_flags(NVCC_FLAGS_EXTRA) +list(APPEND CUDA_NVCC_FLAGS ${NVCC_FLAGS_EXTRA}) +message(STATUS "Added CUDA NVCC flags for: ${NVCC_FLAGS_EXTRA_readable}") + +if(CUDA_CUDA_LIB) + # message(STATUS "Found libcuda: ${CUDA_CUDA_LIB}") + list(APPEND Caffe2_DEPENDENCY_LIBS ${CUDA_CUDA_LIB}) +else() + message(FATAL_ERROR "Cannot find libcuda.so.") +endif() +if(CUDA_NVRTC_LIB) + # message(STATUS "Found libnvrtc: ${CUDA_NVRTC_LIB}") + list(APPEND Caffe2_DEPENDENCY_LIBS ${CUDA_NVRTC_LIB}) +else() + message(FATAL_ERROR "Cannot find libnvrtc.so.") +endif() + +# Set C++11 support +set(CUDA_PROPAGATE_HOST_FLAGS OFF) + +# Release/Debug flags set by cmake. Such as -O3 -g -DNDEBUG etc. +# So, don't set these flags here. +list(APPEND CUDA_NVCC_FLAGS "-std=c++11") +list(APPEND CUDA_NVCC_FLAGS "--use_fast_math") +list(APPEND CUDA_NVCC_FLAGS "-Xcompiler -fPIC") +# Set :expt-relaxed-constexpr to suppress Eigen warnings +list(APPEND CUDA_NVCC_FLAGS "--expt-relaxed-constexpr") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + list(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_DEBUG}) +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + list(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_RELEASE}) +elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + list(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) +elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") + list(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_MINSIZEREL}) +endif() + +mark_as_advanced(CUDA_BUILD_CUBIN CUDA_BUILD_EMULATION CUDA_VERBOSE_BUILD) +mark_as_advanced(CUDA_SDK_ROOT_DIR CUDA_SEPARABLE_COMPILATION) diff --git a/cmake/flags.cmake b/cmake/flags.cmake index 4593ae6180..2b125cef6a 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -149,58 +149,3 @@ endforeach() foreach(flag ${GPU_COMMON_FLAGS}) safe_set_nvflag(${flag}) endforeach() - - -set(CUDA_PROPAGATE_HOST_FLAGS OFF) - -# Release/Debug flags set by cmake. Such as -O3 -g -DNDEBUG etc. -# So, don't set these flags here. -LIST(APPEND CUDA_NVCC_FLAGS -std=c++11) -LIST(APPEND CUDA_NVCC_FLAGS --use_fast_math) - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - LIST(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_DEBUG}) -elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - LIST(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_RELEASE}) -elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - LIST(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) -elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") - LIST(APPEND CUDA_NVCC_FLAGS ${CMAKE_CXX_FLAGS_MINSIZEREL}) -endif() - -function(specify_cuda_arch cuda_version cuda_arch) - if(${cuda_version} VERSION_GREATER "8.0") - foreach(capability 61 62) - if(${cuda_arch} STREQUAL ${capability}) - list(APPEND __arch_flags " -gencode arch=compute_${cuda_arch},code=sm_${cuda_arch}") - endif() - endforeach() - elseif(${cuda_version} VERSION_GREATER "7.0" and ${cuda_arch} STREQUAL "53") - list(APPEND __arch_flags " -gencode arch=compute_${cuda_arch},code=sm_${cuda_arch}") - endif() -endfunction() - -# Common gpu architectures: Kepler, Maxwell -foreach(capability 30 35 50) - list(APPEND __arch_flags " -gencode arch=compute_${capability},code=sm_${capability}") -endforeach() - -if (CUDA_VERSION VERSION_GREATER "7.0" OR CUDA_VERSION VERSION_EQUAL "7.0") - list(APPEND __arch_flags " -gencode arch=compute_52,code=sm_52") -endif() - -# Modern gpu architectures: Pascal -if (CUDA_VERSION VERSION_GREATER "8.0" OR CUDA_VERSION VERSION_EQUAL "8.0") - list(APPEND __arch_flags " -gencode arch=compute_60,code=sm_60") - list(APPEND CUDA_NVCC_FLAGS --expt-relaxed-constexpr) -endif() - -# Custom gpu architecture -set(CUDA_ARCH) - -if(CUDA_ARCH) - specify_cuda_arch(${CUDA_VERSION} ${CUDA_ARCH}) -endif() - -set(CUDA_NVCC_FLAGS ${__arch_flags} ${CUDA_NVCC_FLAGS}) - From 8496eab45a23852cd9941227041bcf0fb289c29a Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 22:45:51 +0800 Subject: [PATCH 24/43] make mklml necessary when with_mkldnn --- CMakeLists.txt | 6 +++++- cmake/configure.cmake | 29 ++++++++--------------------- cmake/external/mkldnn.cmake | 13 ++++++------- cmake/util.cmake | 4 ++-- 4 files changed, 21 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3582a1bc..5209c40e0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,8 +164,12 @@ if(WITH_GPU) endif(NOT WITH_DSO) endif(WITH_GPU) +if(WITH_MKLML) + list(APPEND EXTERNAL_LIBS ${MKLML_IOMP_LIB}) +endif() + if(WITH_MKLDNN) - list(APPEND EXTERNAL_LIBS ${MKLDNN_LIB} ${MKLDNN_IOMP_LIB}) + list(APPEND EXTERNAL_LIBS ${MKLDNN_LIB}) endif() if(USE_NNPACK) diff --git a/cmake/configure.cmake b/cmake/configure.cmake index 24ddb24399..e550ec2856 100644 --- a/cmake/configure.cmake +++ b/cmake/configure.cmake @@ -76,27 +76,14 @@ else() include_directories(${CUDA_TOOLKIT_INCLUDE}) endif(NOT WITH_GPU) -if(WITH_MKLDNN) - add_definitions(-DPADDLE_USE_MKLDNN) - if (WITH_MKLML AND MKLDNN_IOMP_DIR) - message(STATUS "Enable Intel OpenMP at ${MKLDNN_IOMP_DIR}") - set(OPENMP_FLAGS "-fopenmp") - set(CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS ${OPENMP_FLAGS}) - set(CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS ${OPENMP_FLAGS}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPENMP_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMP_FLAGS}") - else() - find_package(OpenMP) - if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - else() - message(WARNING "Can not find OpenMP." - "Some performance features in MKLDNN may not be available") - endif() - endif() - -endif(WITH_MKLDNN) +if (WITH_MKLML AND MKLML_IOMP_LIB) + message(STATUS "Enable Intel OpenMP with ${MKLML_IOMP_LIB}") + set(OPENMP_FLAGS "-fopenmp") + set(CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS ${OPENMP_FLAGS}) + set(CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS ${OPENMP_FLAGS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OPENMP_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMP_FLAGS}") +endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_FLAG}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIMD_FLAG}") diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index 5a06825beb..b80b6b90c0 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -40,10 +40,9 @@ INCLUDE_DIRECTORIES(${MKLDNN_INC_DIR}) IF(${CBLAS_PROVIDER} STREQUAL "MKLML") SET(MKLDNN_DEPENDS ${MKLML_PROJECT}) - SET(MKLDNN_MKLROOT ${MKLML_ROOT}) - SET(MKLDNN_IOMP_LIB ${MKLML_IOMP_LIB}) - SET(MKLDNN_IOMP_DIR ${MKLML_LIB_DIR}) - MESSAGE(STATUS "Build MKLDNN with ${MKLDNN_MKLROOT}") + MESSAGE(STATUS "Build MKLDNN with MKLML ${MKLML_ROOT}") +ELSE() + MESSAGE(FATAL_ERROR "Should enable MKLML when build MKLDNN") ENDIF() SET(MKLDNN_CFLAG "${CMAKE_C_FLAGS} -Wno-error=strict-overflow") @@ -57,15 +56,15 @@ ExternalProject_Add( PREFIX ${MKLDNN_SOURCES_DIR} UPDATE_COMMAND "" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${MKLDNN_INSTALL_DIR} - CMAKE_ARGS -DMKLROOT=${MKLDNN_MKLROOT} + CMAKE_ARGS -DMKLROOT=${MKLML_ROOT} CMAKE_ARGS -DCMAKE_C_FLAGS=${MKLDNN_CFLAG} CMAKE_ARGS -DCMAKE_CXX_FLAGS=${MKLDNN_CXXFLAG} CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${MKLDNN_INSTALL_DIR} - -DMKLROOT:PATH=${MKLDNN_MKLROOT} + -DMKLROOT:PATH=${MKLML_ROOT} ) ADD_LIBRARY(mkldnn SHARED IMPORTED GLOBAL) SET_PROPERTY(TARGET mkldnn PROPERTY IMPORTED_LOCATION ${MKLDNN_LIB}) ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) -MESSAGE(STATUS "Mkldnn library: ${MKLDNN_LIB}") +MESSAGE(STATUS "MKLDNN library: ${MKLDNN_LIB}") LIST(APPEND external_project_dependencies mkldnn) diff --git a/cmake/util.cmake b/cmake/util.cmake index 117ab7f49c..ad905ab55b 100644 --- a/cmake/util.cmake +++ b/cmake/util.cmake @@ -115,8 +115,8 @@ function(link_paddle_exe TARGET_NAME) target_link_libraries(${TARGET_NAME} log) endif(ANDROID) - if(WITH_MKLDNN AND WITH_MKLML AND MKLDNN_IOMP_DIR) - target_link_libraries(${TARGET_NAME} "-L${MKLDNN_IOMP_DIR} -liomp5 -Wl,--as-needed") + if(WITH_MKLML AND MKLML_LIB_DIR AND MKLML_IOMP_LIB) + target_link_libraries(${TARGET_NAME} "-L${MKLML_LIB_DIR} -liomp5 -Wl,--as-needed") endif() add_dependencies(${TARGET_NAME} ${external_project_dependencies}) From 363f690d79aebc5b09bdeb5794ee70c968963e49 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 23:14:37 +0800 Subject: [PATCH 25/43] expose only one WITH_MKL to user, covering WITH_MKLDNN and WITH_MKLML --- CMakeLists.txt | 20 ++++++++++++++------ doc/design/mkldnn/README.MD | 8 ++++---- doc/howto/dev/write_docs_cn.rst | 2 +- paddle/gserver/layers/MKLDNNLayer.cpp | 2 +- paddle/scripts/docker/README.md | 3 +-- paddle/scripts/docker/build.sh | 6 ++---- paddle/scripts/submit_local.sh.in | 10 +++++----- paddle/scripts/travis/build_doc.sh | 2 +- 8 files changed, 29 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5209c40e0f..9e30dff70f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,7 @@ include(simd) ################################ Configurations ####################################### option(WITH_GPU "Compile PaddlePaddle with NVIDIA GPU" ${CUDA_FOUND}) option(WITH_AVX "Compile PaddlePaddle with AVX intrinsics" ${AVX_FOUND}) -option(WITH_MKLDNN "Compile PaddlePaddle with mkl-dnn support." ${AVX_FOUND}) -option(WITH_MKLML "Compile PaddlePaddle with mklml package." ${AVX_FOUND}) +option(WITH_MKL "Compile PaddlePaddle with MKL support." ${AVX_FOUND}) option(WITH_DSO "Compile PaddlePaddle with dynamic linked CUDA" ON) option(WITH_TESTING "Compile PaddlePaddle with unit testing" ON) option(WITH_SWIG_PY "Compile PaddlePaddle with inference api" ON) @@ -82,10 +81,8 @@ if(ANDROID OR IOS) "Disable PYTHON when cross-compiling for Android and iOS" FORCE) set(WITH_RDMA OFF CACHE STRING "Disable RDMA when cross-compiling for Android and iOS" FORCE) - set(WITH_MKLDNN OFF CACHE STRING - "Disable MKLDNN when cross-compiling for Android and iOS" FORCE) - set(WITH_MKLML OFF CACHE STRING - "Disable MKLML package when cross-compiling for Android and iOS" FORCE) + set(WITH_MKL OFF CACHE STRING + "Disable MKL when cross-compiling for Android and iOS" FORCE) # Compile PaddlePaddle mobile inference library if (NOT WITH_C_API) @@ -111,6 +108,17 @@ else() set(THIRD_PARTY_BUILD_TYPE Release) endif() +if(WITH_MKL) + set(WITH_MKLML ON) + set(WITH_MKLDNN ${AVX2_FOUND}) + if(NOT WITH_MKLDNN) + message(WARNING "Do not have AVX2 intrinsics and disabled MKL-DNN") + endif() +else() + set(WITH_MKLML OFF) + set(WITH_MKLDNN OFF) +endif() + ######################################################################################## include(external/mklml) # download mklml package diff --git a/doc/design/mkldnn/README.MD b/doc/design/mkldnn/README.MD index 16236763a7..ec6d468183 100644 --- a/doc/design/mkldnn/README.MD +++ b/doc/design/mkldnn/README.MD @@ -36,13 +36,13 @@ Figure 1. PaddlePaddle on IA. 我们把集成方案大致分为了如下几个方面。 ### CMake -我们会在`CMakeLists.txt`中会添加`WITH_MKLDNN`的选项,当设置这个值为`ON`的时候会启用编译MKL-DNN功能。同时会自动开启OpenMP用于提高MKL-DNN的性能。 +我们会在`CMakeLists.txt`中会给用户添加一个`WITH_MKL`的开关,他是负责`WITH_MKLML`和`WITH_MKLDNN`的总开关。 -同时,我们会引入`WITH_MKLML`选项,用于选择是否使用MKL-DNN自带的MKLML安装包。这个安装包可以独立于MKL-DNN使用,但是建议在开启MKL-DNN的同时也打开MKLML的开关,这样才能发挥最好的性能。 +当打开`WITH_MKL`时,会开启MKLML的功能,作为PaddlePaddle的CBLAS和LAPACK库,同时会开启Intel OpenMP用于提高MKLML的性能。 如果系统支持AVX2指令集及以上,同时会开启MKL-DNN功能。 -所以,我们会在`cmake/external`目录新建`mkldnn.cmake`和`mklml.cmake`文件,它们会在编译PaddlePaddle的时候下载对应的软件包,并放到PaddlePaddle的third party目录中。 +当关闭`WITH_MKL`时,MKLML和MKL-DNN功能会同时关闭。 -**备注**:当`WITH_MKLML=ON`的时候,会优先使用这个包作为PaddlePaddle的CBLAS和LAPACK库,所以会稍微改动`cmake/cblas.cmake`中的逻辑。 +所以,我们会在`cmake/external`目录新建`mkldnn.cmake`和`mklml.cmake`文件,它们会在编译PaddlePaddle的时候下载对应的软件包,并放到PaddlePaddle的third party目录中。 ### Layers 所有MKL-DNN相关的C++ layers,都会按照PaddlePaddle的目录结构存放在 diff --git a/doc/howto/dev/write_docs_cn.rst b/doc/howto/dev/write_docs_cn.rst index 731a63f945..61f3a22354 100644 --- a/doc/howto/dev/write_docs_cn.rst +++ b/doc/howto/dev/write_docs_cn.rst @@ -34,7 +34,7 @@ PaddlePaddle的文档构建有两种方式。 cd TO_YOUR_PADDLE_CLONE_PATH mkdir -p build cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_GPU=OFF -DWITH_MKLDNN=OFF -DWITH_MKLML=OFF -DWITH_DOC=ON + cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_GPU=OFF -DWITH_MKL=OFF -DWITH_DOC=ON make gen_proto_py make paddle_docs paddle_docs_cn diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index e75ac5ba46..2125155c6c 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -22,7 +22,7 @@ namespace paddle { bool MKLDNNLayer::init(const LayerMap& layerMap, const ParameterMap& parameterMap) { CHECK(FLAGS_use_mkldnn) << "MkldnnLayers only support use_mkldnn." - << "Please set WITH_MKLDNN=ON " + << "Please set WITH_MKL=ON " << "and set use_mkldnn=True"; CHECK(!useGpu_) << "Do not support GPU yet"; diff --git a/paddle/scripts/docker/README.md b/paddle/scripts/docker/README.md index b5fd68839d..f3a6f1dba7 100644 --- a/paddle/scripts/docker/README.md +++ b/paddle/scripts/docker/README.md @@ -57,8 +57,7 @@ Users can specify the following Docker build arguments with either "ON" or "OFF" | `WITH_GPU` | OFF | Generates NVIDIA CUDA GPU code and relies on CUDA libraries. | | `WITH_AVX` | OFF | Set to "ON" to enable AVX support. | | `WITH_TESTING` | ON | Build unit tests binaries. | -| `WITH_MKLDNN` | ON | Build with [Intel® MKL DNN](https://github.com/01org/mkl-dnn) support. | -| `WITH_MKLML` | ON | Build with [Intel® MKL](https://software.intel.com/en-us/mkl) support. | +| `WITH_MKL` | ON | Build with [Intel® MKL](https://software.intel.com/en-us/mkl) and [Intel® MKL-DNN](https://github.com/01org/mkl-dnn) support. | | `WITH_GOLANG` | ON | Build fault-tolerant parameter server written in go. | | `WITH_SWIG_PY` | ON | Build with SWIG python API support. | | `WITH_C_API` | OFF | Build capi libraries for inference. | diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index e9c89eee1a..595d25fd48 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -34,8 +34,7 @@ function cmake_gen() { ${PYTHON_FLAGS} -DWITH_DOC=OFF -DWITH_GPU=${WITH_GPU:-OFF} - -DWITH_MKLDNN=${WITH_MKLDNN:-ON} - -DWITH_MKLML=${WITH_MKLML:-ON} + -DWITH_MKL=${WITH_MKL:-ON} -DWITH_AVX=${WITH_AVX:-OFF} -DWITH_GOLANG=${WITH_GOLANG:-ON} -DWITH_SWIG_PY=ON @@ -56,8 +55,7 @@ EOF ${PYTHON_FLAGS} \ -DWITH_DOC=OFF \ -DWITH_GPU=${WITH_GPU:-OFF} \ - -DWITH_MKLDNN=${WITH_MKLDNN:-ON} \ - -DWITH_MKLML=${WITH_MKLML:-ON} \ + -DWITH_MKL=${WITH_MKL:-ON} \ -DWITH_AVX=${WITH_AVX:-OFF} \ -DWITH_GOLANG=${WITH_GOLANG:-ON} \ -DWITH_SWIG_PY=${WITH_SWIG_PY:-ON} \ diff --git a/paddle/scripts/submit_local.sh.in b/paddle/scripts/submit_local.sh.in index b9a49526a7..d71cb84df3 100755 --- a/paddle/scripts/submit_local.sh.in +++ b/paddle/scripts/submit_local.sh.in @@ -18,8 +18,8 @@ function version(){ echo "PaddlePaddle @PADDLE_VERSION@, compiled with" echo " with_avx: @WITH_AVX@" echo " with_gpu: @WITH_GPU@" + echo " with_mkl: @WITH_MKL@" echo " with_mkldnn: @WITH_MKLDNN@" - echo " with_mklml: @WITH_MKLML@" echo " with_double: @WITH_DOUBLE@" echo " with_python: @WITH_PYTHON@" echo " with_rdma: @WITH_RDMA@" @@ -45,8 +45,8 @@ function ver2num() { function cpu_config() { # auto set KMP_AFFINITY and OMP_DYNAMIC from Hyper Threading Status - # only when MKLDNN or MKLML enabled - if [ "@WITH_MKLDNN@" == "OFF" ] && [ "@WITH_MKLML@" == "OFF"]; then + # only when MKL enabled + if [ "@WITH_MKL@" == "OFF" ]; then return 0 fi ht=`lscpu |grep "per core"|awk -F':' '{print $2}'|xargs` @@ -70,8 +70,8 @@ function cpu_config() { function threads_config() { # auto set OMP_NUM_THREADS and MKL_NUM_THREADS # according to trainer_count and total processors - # only when MKLDNN or MKLML enabled - if [ "@WITH_MKLDNN@" == "OFF" ] && [ "@WITH_MKLML@" == "OFF"]; then + # only when MKL enabled + if [ "@WITH_MKL@" == "OFF" ]; then return 0 fi processors=`grep "processor" /proc/cpuinfo|sort -u|wc -l` diff --git a/paddle/scripts/travis/build_doc.sh b/paddle/scripts/travis/build_doc.sh index 973b2736e5..28d82343ed 100755 --- a/paddle/scripts/travis/build_doc.sh +++ b/paddle/scripts/travis/build_doc.sh @@ -6,7 +6,7 @@ mkdir -p $TRAVIS_BUILD_DIR/build cd $TRAVIS_BUILD_DIR/build # Compile Documentation only. -cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_GPU=OFF -DWITH_MKLDNN=OFF -DWITH_MKLML=OFF -DWITH_DOC=ON +cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_GPU=OFF -DWITH_MKL=OFF -DWITH_DOC=ON make -j `nproc` gen_proto_py make -j `nproc` paddle_docs paddle_docs_cn From defd7ec6412e0c9d4a5761a9500f22f5b58cf438 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 16 Nov 2017 23:35:01 +0800 Subject: [PATCH 26/43] mkldnn only need one trainer --- paddle/trainer/Trainer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/paddle/trainer/Trainer.cpp b/paddle/trainer/Trainer.cpp index b68e29cd5e..65ca217470 100644 --- a/paddle/trainer/Trainer.cpp +++ b/paddle/trainer/Trainer.cpp @@ -137,6 +137,10 @@ void Trainer::init(const std::shared_ptr& config, } } + if (FLAGS_trainer_count > 1) { + CHECK(!FLAGS_use_mkldnn) << "MKLDNN only need 1 trainer"; + } + if (testing) { LOG(INFO) << "trainer: in testing mode"; if (config_->getOptConfig().use_sparse_remote_updater() || From c808fbbfcbaaf5c08f6254bfdb860f5dac76a627 Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Fri, 17 Nov 2017 10:15:40 +0800 Subject: [PATCH 27/43] Support the build for multiple architectures at one cmake command (iOS). (#5677) * Support the build for multiple architectures at one cmake command (iOS). * Update the documentations. --- cmake/cross_compiling/ios.cmake | 8 +++----- cmake/external/openblas.cmake | 13 ++++++------- cmake/external/warpctc.cmake | 4 ++++ doc/mobile/cross_compiling_for_android_cn.md | 2 +- doc/mobile/cross_compiling_for_ios_cn.md | 12 ++++++------ doc/mobile/cross_compiling_for_raspberry_cn.md | 2 +- paddle/cuda/include/hl_gpu.h | 2 ++ 7 files changed, 23 insertions(+), 20 deletions(-) diff --git a/cmake/cross_compiling/ios.cmake b/cmake/cross_compiling/ios.cmake index 310450f7d0..d3f5bf6852 100644 --- a/cmake/cross_compiling/ios.cmake +++ b/cmake/cross_compiling/ios.cmake @@ -76,11 +76,9 @@ set(IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") # Set the architecture for iOS if(NOT DEFINED IOS_ARCH) if(IOS_PLATFORM STREQUAL "OS") - # FIXME(liuyiqun): support "armv7;armv7s;arm64" future - set(IOS_ARCH "arm64") + set(IOS_ARCH "armv7;armv7s;arm64") elseif(IOS_PLATFORM STREQUAL "SIMULATOR") - # FIXME(liuyiqun): support "i386;x86_64" future - set(IOS_ARCH "x86_64") + set(IOS_ARCH "i386;x86_64") endif() endif() set(CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") @@ -248,7 +246,7 @@ set(IOS_COMPILER_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${XCODE_IOS_BITCODE_ # Hidden visibilty is required for cxx on iOS set(CMAKE_C_FLAGS "${IOS_COMPILER_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "C flags") -set(CMAKE_CXX_FLAGS "${IOS_COMPILER_FLAGS} -fvisibility-inlines-hidden ${CMAKE_CXX_FLAGS}" CACHE STRING "CXX flags") +set(CMAKE_CXX_FLAGS "${IOS_COMPILER_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden ${CMAKE_CXX_FLAGS}" CACHE STRING "CXX flags") set(IOS_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -Wl,-search_paths_first") diff --git a/cmake/external/openblas.cmake b/cmake/external/openblas.cmake index 2253807981..4c4f59656d 100644 --- a/cmake/external/openblas.cmake +++ b/cmake/external/openblas.cmake @@ -45,15 +45,14 @@ IF(NOT ${CBLAS_FOUND}) SET(OPTIONAL_ARGS ${OPTIONAL_ARGS} TARGET=ARMV8 BINARY=64 USE_THREAD=0) ENDIF() ELSEIF(IOS) - # FIXME(liuyiqun): support multiple architectures - SET(OPENBLAS_COMMIT "b5c96fcfcdc82945502a2303116a64d89985daf5") - SET(OPENBLAS_CC "${OPENBLAS_CC} ${CMAKE_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") - IF(CMAKE_OSX_ARCHITECTURES MATCHES "armv7") - SET(OPENBLAS_CC "${OPENBLAS_CC} -arch armv7") - SET(OPTIONAL_ARGS ${OPTIONAL_ARGS} TARGET=ARMV7 ARM_SOFTFP_ABI=1 USE_THREAD=0) - ELSEIF(CMAKE_OSX_ARCHITECTURES MATCHES "arm64") + IF(CMAKE_OSX_ARCHITECTURES MATCHES "arm64") + SET(OPENBLAS_COMMIT "b5c96fcfcdc82945502a2303116a64d89985daf5") + SET(OPENBLAS_CC "${OPENBLAS_CC} ${CMAKE_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}") SET(OPENBLAS_CC "${OPENBLAS_CC} -arch arm64") SET(OPTIONAL_ARGS ${OPTIONAL_ARGS} TARGET=ARMV8 BINARY=64 USE_THREAD=0 CROSS_SUFFIX=${CROSS_SUFFIX}) + ELSE() + MESSAGE(FATAL_ERROR "OpenBLAS only support arm64 architectures on iOS. " + "You can set IOS_USE_VECLIB_FOR_BLAS=ON or USE_EIGEN_FOR_BLAS=ON to use other blas library instead.") ENDIF() ELSEIF(RPI) # use hardfp diff --git a/cmake/external/warpctc.cmake b/cmake/external/warpctc.cmake index 8bd0582228..a8e1aca49c 100644 --- a/cmake/external/warpctc.cmake +++ b/cmake/external/warpctc.cmake @@ -12,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +IF(MOBILE_INFERENCE) + return() +ENDIF() + INCLUDE(ExternalProject) SET(WARPCTC_SOURCES_DIR ${THIRD_PARTY_PATH}/warpctc) diff --git a/doc/mobile/cross_compiling_for_android_cn.md b/doc/mobile/cross_compiling_for_android_cn.md index 882066f237..424d7718c6 100644 --- a/doc/mobile/cross_compiling_for_android_cn.md +++ b/doc/mobile/cross_compiling_for_android_cn.md @@ -1,4 +1,4 @@ -# 构建Android平台上的PaddlePaddle库 +# Android平台编译指南 用户可通过如下两种方式,交叉编译Android平台上适用的PaddlePaddle库: - 基于Docker容器的编译方式 diff --git a/doc/mobile/cross_compiling_for_ios_cn.md b/doc/mobile/cross_compiling_for_ios_cn.md index cda636a67d..9da48e7f21 100644 --- a/doc/mobile/cross_compiling_for_ios_cn.md +++ b/doc/mobile/cross_compiling_for_ios_cn.md @@ -1,4 +1,4 @@ -# 构建iOS平台上的PaddlePaddle库 +# iOS平台编译指南 交叉编译iOS平台上适用的PaddlePaddle库,需要在MacOS系统上进行。本文的将介绍在MacOS上,从源码交叉编译iOS平台上适用的PaddlePaddle库。 ## 准备交叉编译环境 @@ -25,7 +25,7 @@ iOS平台可选配置参数: - `IOS_PLATFORM`,可设置为`OS/SIMULATOR`,默认值为`OS`。 - `OS`,构建目标为`arm`架构的iPhone或者iPad等物理设备。 - `SIMULATOR`,构建目标为`x86`架构的模拟器平台。 -- `IOS_ARCH`,目标架构。针对不同的`IOS_PLATFORM`,可设置的目标架构如下表所示: +- `IOS_ARCH`,目标架构。针对不同的`IOS_PLATFORM`,可设置的目标架构如下表所示,默认编译所有架构: @@ -41,11 +41,11 @@ iOS平台可选配置参数: - + - +
OSarmv7, armv7s, arm64 (默认)armv7, armv7s, arm64
SIMULATORi386, x86_64 (默认)i386, x86_64
@@ -66,7 +66,7 @@ iOS平台可选配置参数: ```bash cmake -DCMAKE_SYSTEM_NAME=iOS \ -DIOS_PLATFORM=OS \ - -DIOS_ARCH="arm64" \ + -DIOS_ARCH="armv7;arm64" \ -DIOS_ENABLE_BITCODE=ON \ -DIOS_USE_VECLIB_FOR_BLAS=ON \ -DCMAKE_INSTALL_PREFIX=your/path/to/install \ @@ -112,6 +112,6 @@ $ make install - `lib`目录,其中包含PaddlePaddle的C-API静态库 - `third_party`目录,其中包含所依赖的所有第三方库 -注意,不同架构的PaddlePaddle库建议安装到不同的目录下,然后使用`lipo`工具将多个静态库合并成一个支持多个架构的fat库。 +注意,如果PaddlePaddle库需要同时支持真机和模拟器,则需要分别编译真机和模拟器版本,然后使用`lipo`工具合并fat库。 自此,PaddlePaddle库已经安装完成,用户可将合成的fat库用于深度学习相关的iOS App中,调用方法见C-API文档。 diff --git a/doc/mobile/cross_compiling_for_raspberry_cn.md b/doc/mobile/cross_compiling_for_raspberry_cn.md index 6e983645fa..f8ef9dc803 100644 --- a/doc/mobile/cross_compiling_for_raspberry_cn.md +++ b/doc/mobile/cross_compiling_for_raspberry_cn.md @@ -1,4 +1,4 @@ -# 构建Raspberry Pi平台上的PaddlePaddle库 +# Raspberry Pi平台编译指南 通常有两个方法来构建基于 Rasspberry Pi 的版本: diff --git a/paddle/cuda/include/hl_gpu.h b/paddle/cuda/include/hl_gpu.h index ede2670882..4ab8de80d1 100644 --- a/paddle/cuda/include/hl_gpu.h +++ b/paddle/cuda/include/hl_gpu.h @@ -25,7 +25,9 @@ limitations under the License. */ #include "hl_matrix.h" #include "hl_sequence.h" #include "hl_sparse.h" +#ifndef PADDLE_MOBILE_INFERENCE #include "hl_warpctc_wrap.h" +#endif #ifdef HPPL_STUB_FUNC #include "stub/hl_aggregate_stub.h" From 23a674c98aee5eaf00280d6952d2cc3dec40b495 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 17 Nov 2017 10:33:19 +0800 Subject: [PATCH 28/43] switch the flag --- paddle/trainer/Trainer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/trainer/Trainer.cpp b/paddle/trainer/Trainer.cpp index 65ca217470..88e684849d 100644 --- a/paddle/trainer/Trainer.cpp +++ b/paddle/trainer/Trainer.cpp @@ -137,8 +137,8 @@ void Trainer::init(const std::shared_ptr& config, } } - if (FLAGS_trainer_count > 1) { - CHECK(!FLAGS_use_mkldnn) << "MKLDNN only need 1 trainer"; + if (FLAGS_use_mkldnn) { + CHECK_EQ(FLAGS_trainer_count, 1UL) << "MKLDNN only need 1 trainer"; } if (testing) { From d13c3a98ceffa807a8fb4e8d2971acf0235afa06 Mon Sep 17 00:00:00 2001 From: Yancey1989 Date: Fri, 17 Nov 2017 10:36:03 +0800 Subject: [PATCH 29/43] fix no framework proto file --- paddle/operators/math/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/operators/math/CMakeLists.txt b/paddle/operators/math/CMakeLists.txt index b9417f1d7f..002b68fecf 100644 --- a/paddle/operators/math/CMakeLists.txt +++ b/paddle/operators/math/CMakeLists.txt @@ -1,7 +1,7 @@ add_subdirectory(detail) if(WITH_GPU) - nv_library(math_function SRCS math_function.cc math_function.cu im2col.cc im2col.cu DEPS cblas device_context) + nv_library(math_function SRCS math_function.cc math_function.cu im2col.cc im2col.cu DEPS cblas device_context framework_proto) nv_test(math_function_gpu_test SRCS math_function_test.cu DEPS math_function tensor) nv_library(selected_rows_functor SRCS selected_rows_functor.cc selected_rows_functor.cu DEPS selected_rows math_function) nv_test(selected_rows_functor_gpu_test SRCS selected_rows_functor_test.cu DEPS selected_rows_functor) @@ -15,7 +15,7 @@ if(WITH_GPU) nv_library(lstm_compute SRCS lstm_compute.cc lstm_compute.cu DEPS device_context activation_functions) nv_library(gru_compute SRCS gru_compute.cc gru_compute.cu DEPS device_context activation_functions math_function) else() - cc_library(math_function SRCS math_function.cc im2col.cc DEPS cblas device_context) + cc_library(math_function SRCS math_function.cc im2col.cc DEPS cblas device_context framework_proto) cc_library(selected_rows_functor SRCS selected_rows_functor.cc DEPS selected_rows math_function) cc_library(softmax SRCS softmax.cc DEPS device_context) cc_library(cross_entropy SRCS cross_entropy.cc DEPS device_context) From eb070476996bb5d26de5969b4d98892f104dcb42 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Thu, 16 Nov 2017 19:58:49 +0800 Subject: [PATCH 30/43] add padding --- paddle/operators/conv_transpose_op.cc | 7 +--- paddle/operators/conv_transpose_op.h | 6 +-- paddle/operators/math/im2col.cu | 4 +- .../paddle/v2/fluid/tests/test_conv2d_op.py | 40 +++++++++++++------ .../fluid/tests/test_conv2d_transpose_op.py | 26 +++++++++--- .../fluid/tests/test_conv3d_transpose_op.py | 31 ++++++++++---- 6 files changed, 77 insertions(+), 37 deletions(-) diff --git a/paddle/operators/conv_transpose_op.cc b/paddle/operators/conv_transpose_op.cc index 13ac0cd54c..310e3f5c93 100644 --- a/paddle/operators/conv_transpose_op.cc +++ b/paddle/operators/conv_transpose_op.cc @@ -30,11 +30,6 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { std::vector strides = ctx->Attrs().Get>("strides"); std::vector paddings = ctx->Attrs().Get>("paddings"); - for (size_t i = 0; i < paddings.size(); ++i) { - PADDLE_ENFORCE_EQ(paddings[i], 0, - "No Padding allowed in conv transpose op."); - } - PADDLE_ENFORCE(in_dims.size() == 4 || in_dims.size() == 5, "ConvTransposeOp intput should be 4-D or 5-D tensor."); PADDLE_ENFORCE_EQ(in_dims.size(), filter_dims.size(), @@ -52,7 +47,7 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { std::vector output_shape({in_dims[0], filter_dims[1]}); for (size_t i = 0; i < strides.size(); ++i) { - output_shape.push_back((in_dims[i + 2] - 1) * strides[i] + + output_shape.push_back((in_dims[i + 2] - 1) * strides[i] - 2 * paddings[i] + filter_dims[i + 2]); } ctx->SetOutputDim("Output", framework::make_ddim(output_shape)); diff --git a/paddle/operators/conv_transpose_op.h b/paddle/operators/conv_transpose_op.h index 4b2bd60437..ab336ad23c 100644 --- a/paddle/operators/conv_transpose_op.h +++ b/paddle/operators/conv_transpose_op.h @@ -62,7 +62,6 @@ class GemmConvTransposeKernel : public framework::OpKernel { Tensor* output = context.Output("Output"); std::vector strides = context.Attr>("strides"); - // Actually, no paddings and groups allowed in conv transpose. std::vector paddings = context.Attr>("paddings"); // TODO(Zhuoyuan): Paddings can be added in future. // groups will alway be disabled in conv2dtranspose. @@ -148,8 +147,8 @@ class GemmConvTransposeKernel : public framework::OpKernel { } else if (filter_shape_vec.size() == 3) { // col2vol: col_matrix -> dy // from (c * k_d * k_h * k_w, d * h * w) to (c, o_d, o_h, o_w) - col2vol(context.device_context(), col, dilations, strides, - std::vector{0, 0, 0}, &output_batch); + col2vol(context.device_context(), col, dilations, strides, paddings, + &output_batch); } } } @@ -173,7 +172,6 @@ class GemmConvTransposeGradKernel : public framework::OpKernel { if ((!input_grad) && (!filter_grad)) return; std::vector strides = context.Attr>("strides"); - // Actually, no paddings and groups allowed in conv transpose. std::vector paddings = context.Attr>("paddings"); const int batch_size = static_cast(input->dims()[0]); diff --git a/paddle/operators/math/im2col.cu b/paddle/operators/math/im2col.cu index 347df7a0ff..bf78942439 100644 --- a/paddle/operators/math/im2col.cu +++ b/paddle/operators/math/im2col.cu @@ -119,8 +119,8 @@ __global__ void col2im(int n, const T* data_col, int im_height, int im_width, if (index < n) { T val = 0; - int w = index % im_width; - int h = (index / im_width) % im_height; + int w = index % im_width + padding_width; + int h = (index / im_width) % im_height + padding_height; int c = index / (im_width * im_height); // compute the start and end of the output diff --git a/python/paddle/v2/fluid/tests/test_conv2d_op.py b/python/paddle/v2/fluid/tests/test_conv2d_op.py index 907b52c405..2240dc73cd 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_op.py @@ -110,13 +110,30 @@ class TestConv2dOp(OpTest): self.op_type = "conv2d" +class TestWithPad(TestConv2dOp): + def init_test_case(self): + self.pad = [1, 1] + self.stride = [1, 1] + self.input_size = [2, 3, 5, 5] # NCHW + assert np.mod(self.input_size[1], self.groups) == 0 + f_c = self.input_size[1] / self.groups + self.filter_size = [6, f_c, 3, 3] + + +class TestWithStride(TestConv2dOp): + def init_test_case(self): + self.pad = [1, 1] + self.stride = [2, 2] + self.input_size = [2, 3, 6, 6] # NCHW + assert np.mod(self.input_size[1], self.groups) == 0 + f_c = self.input_size[1] / self.groups + self.filter_size = [6, f_c, 3, 3] + + class TestWithGroup(TestConv2dOp): def init_group(self): self.groups = 3 - def init_op_type(self): - self.op_type = "conv2d" - class TestWith1x1(TestConv2dOp): def init_test_case(self): @@ -127,15 +144,9 @@ class TestWith1x1(TestConv2dOp): f_c = self.input_size[1] / self.groups self.filter_size = [6, f_c, 1, 1] - def init_dilation(self): - self.dilations = [1, 1] - def init_group(self): self.groups = 3 - def init_op_type(self): - self.op_type = "conv2d" - class TestWithDilation(TestConv2dOp): def init_test_case(self): @@ -152,14 +163,19 @@ class TestWithDilation(TestConv2dOp): def init_group(self): self.groups = 3 + +#----------------Conv2dCudnn---------------- +class TestCudnn(TestConv2dOp): def init_op_type(self): - self.op_type = "conv2d" + self.op_type = "conv_cudnn" -#----------------Conv2dCudnn---------------- +class TestCudnnWithPad(TestWithPad): + def init_op_type(self): + self.op_type = "conv_cudnn" -class TestCudnn(TestConv2dOp): +class TestCudnnWithStride(TestWithStride): def init_op_type(self): self.op_type = "conv_cudnn" diff --git a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py index 54349c018c..d7b1f2f2a3 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py @@ -4,9 +4,7 @@ from op_test import OpTest def conv2dtranspose_forward_naive(input_, filter_, conv2dtranspose_param): - # [2, 3, 5, 5] in_n, in_c, in_h, in_w = input_.shape - # [3, 6, 3, 3] f_c, out_c, f_h, f_w = filter_.shape assert in_c == f_c @@ -29,6 +27,7 @@ def conv2dtranspose_forward_naive(input_, filter_, conv2dtranspose_param): j1, j2 = j * stride[0], j * stride[0] + f_w out[n, k, i1:i2, j1:j2] += tmp_out + out = out[:, :, pad[0]:out_h - pad[0], pad[1]:out_w - pad[1]] return out @@ -36,8 +35,6 @@ class TestConv2dTransposeOp(OpTest): def setUp(self): # init as conv transpose self.init_op_type() - - # [2, 3, 5, 5] -> kernel [3, 6, 3, 3] -> output [2, 6, 7, 7] self.init_test_case() conv2dtranspose_param = {'stride': self.stride, 'pad': self.pad} @@ -55,7 +52,6 @@ class TestConv2dTransposeOp(OpTest): self.outputs = {'Output': output} def test_check_output(self): - print 'check output here for', self.op_type self.check_output() def test_check_grad_no_input(self): @@ -88,6 +84,26 @@ class TestConv2dTransposeOp(OpTest): self.op_type = "conv2d_transpose" +class TestWithPad(TestConv2dTransposeOp): + def init_test_case(self): + self.pad = [1, 1] + self.stride = [1, 1] + self.dilations = [1, 1] + self.input_size = [2, 3, 5, 5] # NCHW + f_c = self.input_size[1] + self.filter_size = [f_c, 6, 3, 3] + + +class TestWithStride(TestConv2dTransposeOp): + def init_test_case(self): + self.pad = [1, 1] + self.stride = [2, 2] + self.dilations = [1, 1] + self.input_size = [2, 3, 5, 5] # NCHW + f_c = self.input_size[1] + self.filter_size = [f_c, 6, 3, 3] + + # ------------ test_cudnn ------------ class TestCudnn(TestConv2dTransposeOp): def init_op_type(self): diff --git a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py index 132fe79314..59a32c4082 100644 --- a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py @@ -4,9 +4,7 @@ from op_test import OpTest def conv3dtranspose_forward_naive(input_, filter_, conv3dtranspose_param): - # [2, 3, 5, 5, 5] in_n, in_c, in_d, in_h, in_w = input_.shape - # [3, 6, 3, 3, 3] f_c, out_c, f_d, f_h, f_w = filter_.shape assert in_c == f_c @@ -14,7 +12,6 @@ def conv3dtranspose_forward_naive(input_, filter_, conv3dtranspose_param): out_d = (in_d - 1) * stride[0] + f_d out_h = (in_h - 1) * stride[1] + f_h out_w = (in_w - 1) * stride[2] + f_w - out = np.zeros((in_n, out_c, out_d, out_h, out_w)) for n in range(in_n): @@ -33,6 +30,8 @@ def conv3dtranspose_forward_naive(input_, filter_, conv3dtranspose_param): j1, j2 = j * stride[2], j * stride[2] + f_w out[n, k, d1:d2, i1:i2, j1:j2] += tmp_out + out = out[:, :, pad[0]:out_d - pad[0], pad[1]:out_h - pad[1], pad[2]:out_w - + pad[2]] return out @@ -40,8 +39,6 @@ class TestConv3dTransposeOp(OpTest): def setUp(self): # init as conv transpose self.init_op_type() - - # [2, 3, 5, 5, 5] -> kernel [3, 6, 3, 3, 3] -> output [2, 6, 7, 7, 7] self.init_test_case() conv3dtranspose_param = {'stride': self.stride, 'pad': self.pad} @@ -49,7 +46,6 @@ class TestConv3dTransposeOp(OpTest): filter_ = np.random.random(self.filter_size).astype("float32") output = conv3dtranspose_forward_naive( input_, filter_, conv3dtranspose_param).astype("float32") - # print 'deconv output py', output, output.shape self.inputs = {'Input': input_, 'Filter': filter_} self.attrs = { @@ -60,7 +56,6 @@ class TestConv3dTransposeOp(OpTest): self.outputs = {'Output': output} def test_check_output(self): - print 'check output here' self.check_output() def test_check_grad(self): @@ -85,7 +80,7 @@ class TestConv3dTransposeOp(OpTest): self.pad = [0, 0, 0] self.stride = [1, 1, 1] self.dilations = [1, 1, 1] - self.input_size = [2, 3, 5, 5, 5] # NCHW + self.input_size = [2, 3, 5, 5, 5] # NCDHW f_c = self.input_size[1] self.filter_size = [f_c, 6, 3, 3, 3] @@ -93,5 +88,25 @@ class TestConv3dTransposeOp(OpTest): self.op_type = "conv3d_transpose" +class TestWithPad(TestConv3dTransposeOp): + def init_test_case(self): + self.pad = [1, 1, 1] + self.stride = [1, 1, 1] + self.dilations = [1, 1, 1] + self.input_size = [2, 3, 5, 5, 5] # NCDHW + f_c = self.input_size[1] + self.filter_size = [f_c, 6, 3, 3, 3] + + +class TestWithStride(TestConv3dTransposeOp): + def init_test_case(self): + self.pad = [1, 1, 1] + self.stride = [2, 2, 2] + self.dilations = [1, 1, 1] + self.input_size = [2, 3, 5, 5, 5] # NCDHW + f_c = self.input_size[1] + self.filter_size = [f_c, 6, 3, 3, 3] + + if __name__ == '__main__': unittest.main() From 0ce38b77f2312390a61c61fbd05ec4b72347fea6 Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Thu, 16 Nov 2017 22:16:22 -0600 Subject: [PATCH 31/43] correct optimizer import (#5699) --- .../v2/fluid/tests/book/test_fit_a_line.py | 29 ++--- .../book/test_image_classification_train.py | 100 ++++-------------- .../tests/book/test_recognize_digits_conv.py | 29 ++--- .../tests/book/test_recognize_digits_mlp.py | 35 ++---- .../tests/book/test_recommender_system.py | 99 +++++------------ .../book/test_understand_sentiment_conv.py | 11 +- .../test_understand_sentiment_dynamic_lstm.py | 10 +- .../book/test_understand_sentiment_lstm.py | 9 +- .../v2/fluid/tests/book/test_word2vec.py | 49 +++------ 9 files changed, 101 insertions(+), 270 deletions(-) diff --git a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py index ee677a2c56..a7f3bfc0ca 100644 --- a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py +++ b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py @@ -1,33 +1,22 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework -from paddle.v2.fluid.io import save_persistables, load_persistables +import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor +from paddle.v2.fluid.io import save_persistables, load_persistables +from paddle.v2.fluid.optimizer import SGDOptimizer -import numpy as np - -x = layers.data( - name='x', - shape=[13], - data_type='float32') +x = layers.data(name='x', shape=[13], data_type='float32') -y_predict = layers.fc(input=x, - size=1, - act=None) +y_predict = layers.fc(input=x, size=1, act=None) -y = layers.data( - name='y', - shape=[1], - data_type='float32') +y = layers.data(name='y', shape=[1], data_type='float32') -cost = layers.square_error_cost( - input=y_predict, - label=y) +cost = layers.square_error_cost(input=y_predict, label=y) avg_cost = layers.mean(x=cost) -sgd_optimizer = optimizer.SGDOptimizer(learning_rate=0.001) +sgd_optimizer = SGDOptimizer(learning_rate=0.001) opts = sgd_optimizer.minimize(avg_cost) BATCH_SIZE = 20 diff --git a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py index f4be835b3a..b850612550 100644 --- a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py +++ b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py @@ -1,21 +1,16 @@ import numpy as np import paddle.v2 as paddle import paddle.v2.fluid.core as core +import paddle.v2.fluid.framework as framework import paddle.v2.fluid.layers as layers import paddle.v2.fluid.nets as nets -import paddle.v2.fluid.optimizer as optimizer from paddle.v2.fluid.executor import Executor -import paddle.v2.fluid.framework as framework from paddle.v2.fluid.initializer import XavierInitializer +from paddle.v2.fluid.optimizer import AdamOptimizer def resnet_cifar10(input, depth=32): - def conv_bn_layer(input, - ch_out, - filter_size, - stride, - padding, - act='relu'): + def conv_bn_layer(input, ch_out, filter_size, stride, padding, act='relu'): tmp = layers.conv2d( input=input, filter_size=filter_size, @@ -24,9 +19,7 @@ def resnet_cifar10(input, depth=32): padding=padding, act=None, bias_attr=False) - return layers.batch_norm( - input=tmp, - act=act) + return layers.batch_norm(input=tmp, act=act) def shortcut(input, ch_in, ch_out, stride, program, init_program): if ch_in != ch_out: @@ -35,28 +28,11 @@ def resnet_cifar10(input, depth=32): else: return input - def basicblock(input, - ch_in, - ch_out, - stride): - tmp = conv_bn_layer( - input, - ch_out, - 3, - stride, - 1) - tmp = conv_bn_layer( - tmp, - ch_out, - 3, - 1, - 1, - act=None) + def basicblock(input, ch_in, ch_out, stride): + tmp = conv_bn_layer(input, ch_out, 3, stride, 1) + tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, act=None) short = shortcut(input, ch_in, ch_out, stride) - return layers.elementwise_add( - x=tmp, - y=short, - act='relu') + return layers.elementwise_add(x=tmp, y=short, act='relu') def layer_warp(block_func, input, ch_in, ch_out, count, stride): tmp = block_func(input, ch_in, ch_out, stride) @@ -67,45 +43,17 @@ def resnet_cifar10(input, depth=32): assert (depth - 2) % 6 == 0 n = (depth - 2) / 6 conv1 = conv_bn_layer( - input=input, - ch_out=16, - filter_size=3, - stride=1, - padding=1) - res1 = layer_warp( - basicblock, - conv1, - 16, - 16, - n, - 1) - res2 = layer_warp( - basicblock, - res1, - 16, - 32, - n, - 2) - res3 = layer_warp( - basicblock, - res2, - 32, - 64, - n, - 2) + input=input, ch_out=16, filter_size=3, stride=1, padding=1) + res1 = layer_warp(basicblock, conv1, 16, 16, n, 1) + res2 = layer_warp(basicblock, res1, 16, 32, n, 2) + res3 = layer_warp(basicblock, res2, 32, 64, n, 2) pool = layers.pool2d( - input=res3, - pool_size=8, - pool_type='avg', - pool_stride=1) + input=res3, pool_size=8, pool_type='avg', pool_stride=1) return pool def vgg16_bn_drop(input): - def conv_block(input, - num_filter, - groups, - dropouts): + def conv_block(input, num_filter, groups, dropouts): return nets.img_conv_group( input=input, pool_size=2, @@ -123,22 +71,14 @@ def vgg16_bn_drop(input): conv4 = conv_block(conv3, 512, 3, [0.4, 0.4, 0]) conv5 = conv_block(conv4, 512, 3, [0.4, 0.4, 0]) - drop = layers.dropout( - x=conv5, - dropout_prob=0.5) + drop = layers.dropout(x=conv5, dropout_prob=0.5) fc1 = layers.fc(input=drop, size=512, act=None, param_attr={"initializer": XavierInitializer()}) - reshape1 = layers.reshape( - x=fc1, - shape=list(fc1.shape + (1, 1))) - bn = layers.batch_norm( - input=reshape1, - act='relu') - drop2 = layers.dropout( - x=bn, - dropout_prob=0.5) + reshape1 = layers.reshape(x=fc1, shape=list(fc1.shape + (1, 1))) + bn = layers.batch_norm(input=reshape1, act='relu') + drop2 = layers.dropout(x=bn, dropout_prob=0.5) fc2 = layers.fc(input=drop2, size=512, act=None, @@ -165,8 +105,8 @@ cost = layers.cross_entropy(input=predict, label=label) avg_cost = layers.mean(x=cost) accuracy = layers.accuracy(input=predict, label=label) -# optimizer = optimizer.SGDOptimizer(learning_rate=0.001) -optimizer = optimizer.AdamOptimizer(learning_rate=0.001) +# optimizer = SGDOptimizer(learning_rate=0.001) +optimizer = AdamOptimizer(learning_rate=0.001) opts = optimizer.minimize(avg_cost) BATCH_SIZE = 128 diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py index f330ff5813..75fbaf83e8 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py @@ -1,22 +1,15 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers -import paddle.v2.fluid.nets as nets import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.evaluator as evaluator import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid.nets as nets from paddle.v2.fluid.executor import Executor +from paddle.v2.fluid.optimizer import AdamOptimizer -import numpy as np - -images = layers.data( - name='pixel', - shape=[1, 28, 28], - data_type='float32') -label = layers.data( - name='label', - shape=[1], - data_type='int64') +images = layers.data(name='pixel', shape=[1, 28, 28], data_type='float32') +label = layers.data(name='label', shape=[1], data_type='int64') conv_pool_1 = nets.simple_img_conv_pool( input=images, filter_size=5, @@ -32,17 +25,13 @@ conv_pool_2 = nets.simple_img_conv_pool( pool_stride=2, act="relu") -predict = layers.fc(input=conv_pool_2, - size=10, - act="softmax") +predict = layers.fc(input=conv_pool_2, size=10, act="softmax") cost = layers.cross_entropy(input=predict, label=label) avg_cost = layers.mean(x=cost) -optimizer = optimizer.AdamOptimizer(learning_rate=0.01, beta1=0.9, beta2=0.999) +optimizer = AdamOptimizer(learning_rate=0.01, beta1=0.9, beta2=0.999) opts = optimizer.minimize(avg_cost) -accuracy, acc_out = evaluator.accuracy( - input=predict, - label=label) +accuracy, acc_out = evaluator.accuracy(input=predict, label=label) BATCH_SIZE = 50 PASS_NUM = 3 diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py index b0164e3e36..cf10b1942e 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py @@ -1,19 +1,15 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor -from paddle.v2.fluid.regularizer import L2DecayRegularizer from paddle.v2.fluid.initializer import UniformInitializer - -import numpy as np +from paddle.v2.fluid.optimizer import MomentumOptimizer +from paddle.v2.fluid.regularizer import L2DecayRegularizer BATCH_SIZE = 128 -image = layers.data( - name='x', - shape=[784], - data_type='float32') +image = layers.data(name='x', shape=[784], data_type='float32') param_attr = { 'name': None, @@ -22,32 +18,21 @@ param_attr = { 'regularization': L2DecayRegularizer(0.0005 * BATCH_SIZE) } -hidden1 = layers.fc(input=image, - size=128, - act='relu', - param_attr=param_attr) -hidden2 = layers.fc(input=hidden1, - size=64, - act='relu', - param_attr=param_attr) +hidden1 = layers.fc(input=image, size=128, act='relu', param_attr=param_attr) +hidden2 = layers.fc(input=hidden1, size=64, act='relu', param_attr=param_attr) predict = layers.fc(input=hidden2, size=10, act='softmax', param_attr=param_attr) -label = layers.data( - name='y', - shape=[1], - data_type='int64') +label = layers.data(name='y', shape=[1], data_type='int64') cost = layers.cross_entropy(input=predict, label=label) avg_cost = layers.mean(x=cost) -accuracy = layers.accuracy( - input=predict, - label=label) +accuracy = layers.accuracy(input=predict, label=label) -optimizer = optimizer.MomentumOptimizer(learning_rate=0.001, momentum=0.9) +optimizer = MomentumOptimizer(learning_rate=0.001, momentum=0.9) opts = optimizer.minimize(avg_cost) train_reader = paddle.batch( diff --git a/python/paddle/v2/fluid/tests/book/test_recommender_system.py b/python/paddle/v2/fluid/tests/book/test_recommender_system.py index eefcb55beb..55ded3aed3 100644 --- a/python/paddle/v2/fluid/tests/book/test_recommender_system.py +++ b/python/paddle/v2/fluid/tests/book/test_recommender_system.py @@ -1,12 +1,11 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers -import paddle.v2.fluid.nets as nets import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid.nets as nets from paddle.v2.fluid.executor import Executor - -import numpy as np +from paddle.v2.fluid.optimizer import SGDOptimizer IS_SPARSE = True USE_GPU = False @@ -19,10 +18,7 @@ def get_usr_combined_features(): USR_DICT_SIZE = paddle.dataset.movielens.max_user_id() + 1 - uid = layers.data( - name='user_id', - shape=[1], - data_type='int64') + uid = layers.data(name='user_id', shape=[1], data_type='int64') usr_emb = layers.embedding( input=uid, @@ -31,15 +27,11 @@ def get_usr_combined_features(): param_attr={'name': 'user_table'}, is_sparse=IS_SPARSE) - usr_fc = layers.fc(input=usr_emb, - size=32) + usr_fc = layers.fc(input=usr_emb, size=32) USR_GENDER_DICT_SIZE = 2 - usr_gender_id = layers.data( - name='gender_id', - shape=[1], - data_type='int64') + usr_gender_id = layers.data(name='gender_id', shape=[1], data_type='int64') usr_gender_emb = layers.embedding( input=usr_gender_id, @@ -47,14 +39,10 @@ def get_usr_combined_features(): param_attr={'name': 'gender_table'}, is_sparse=IS_SPARSE) - usr_gender_fc = layers.fc(input=usr_gender_emb, - size=16) + usr_gender_fc = layers.fc(input=usr_gender_emb, size=16) USR_AGE_DICT_SIZE = len(paddle.dataset.movielens.age_table) - usr_age_id = layers.data( - name='age_id', - shape=[1], - data_type="int64") + usr_age_id = layers.data(name='age_id', shape=[1], data_type="int64") usr_age_emb = layers.embedding( input=usr_age_id, @@ -62,14 +50,10 @@ def get_usr_combined_features(): is_sparse=IS_SPARSE, param_attr={'name': 'age_table'}) - usr_age_fc = layers.fc(input=usr_age_emb, - size=16) + usr_age_fc = layers.fc(input=usr_age_emb, size=16) USR_JOB_DICT_SIZE = paddle.dataset.movielens.max_job_id() + 1 - usr_job_id = layers.data( - name='job_id', - shape=[1], - data_type="int64") + usr_job_id = layers.data(name='job_id', shape=[1], data_type="int64") usr_job_emb = layers.embedding( input=usr_job_id, @@ -77,16 +61,12 @@ def get_usr_combined_features(): param_attr={'name': 'job_table'}, is_sparse=IS_SPARSE) - usr_job_fc = layers.fc(input=usr_job_emb, - size=16) + usr_job_fc = layers.fc(input=usr_job_emb, size=16) concat_embed = layers.concat( - input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc], - axis=1) + input=[usr_fc, usr_gender_fc, usr_age_fc, usr_job_fc], axis=1) - usr_combined_features = layers.fc(input=concat_embed, - size=200, - act="tanh") + usr_combined_features = layers.fc(input=concat_embed, size=200, act="tanh") return usr_combined_features @@ -95,10 +75,7 @@ def get_mov_combined_features(): MOV_DICT_SIZE = paddle.dataset.movielens.max_movie_id() + 1 - mov_id = layers.data( - name='movie_id', - shape=[1], - data_type='int64') + mov_id = layers.data(name='movie_id', shape=[1], data_type='int64') mov_emb = layers.embedding( input=mov_id, @@ -107,36 +84,24 @@ def get_mov_combined_features(): param_attr={'name': 'movie_table'}, is_sparse=IS_SPARSE) - mov_fc = layers.fc(input=mov_emb, - size=32) + mov_fc = layers.fc(input=mov_emb, size=32) CATEGORY_DICT_SIZE = len(paddle.dataset.movielens.movie_categories()) - category_id = layers.data( - name='category_id', - shape=[1], - data_type='int64') + category_id = layers.data(name='category_id', shape=[1], data_type='int64') mov_categories_emb = layers.embedding( - input=category_id, - size=[CATEGORY_DICT_SIZE, 32], - is_sparse=IS_SPARSE) + input=category_id, size=[CATEGORY_DICT_SIZE, 32], is_sparse=IS_SPARSE) mov_categories_hidden = layers.sequence_pool( - input=mov_categories_emb, - pool_type="sum") + input=mov_categories_emb, pool_type="sum") MOV_TITLE_DICT_SIZE = len(paddle.dataset.movielens.get_movie_title_dict()) - mov_title_id = layers.data( - name='movie_title', - shape=[1], - data_type='int64') + mov_title_id = layers.data(name='movie_title', shape=[1], data_type='int64') mov_title_emb = layers.embedding( - input=mov_title_id, - size=[MOV_TITLE_DICT_SIZE, 32], - is_sparse=IS_SPARSE) + input=mov_title_id, size=[MOV_TITLE_DICT_SIZE, 32], is_sparse=IS_SPARSE) mov_title_conv = nets.sequence_conv_pool( input=mov_title_emb, @@ -146,13 +111,10 @@ def get_mov_combined_features(): pool_type="sum") concat_embed = layers.concat( - input=[mov_fc, mov_categories_hidden, mov_title_conv], - axis=1) + input=[mov_fc, mov_categories_hidden, mov_title_conv], axis=1) # FIXME(dzh) : need tanh operator - mov_combined_features = layers.fc(input=concat_embed, - size=200, - act="tanh") + mov_combined_features = layers.fc(input=concat_embed, size=200, act="tanh") return mov_combined_features @@ -162,18 +124,11 @@ def model(): mov_combined_features = get_mov_combined_features() # need cos sim - inference = layers.cos_sim( - X=usr_combined_features, - Y=mov_combined_features) + inference = layers.cos_sim(X=usr_combined_features, Y=mov_combined_features) - label = layers.data( - name='score', - shape=[1], - data_type='float32') + label = layers.data(name='score', shape=[1], data_type='float32') - square_cost = layers.square_error_cost( - input=inference, - label=label) + square_cost = layers.square_error_cost(input=inference, label=label) avg_cost = layers.mean(x=square_cost) @@ -182,7 +137,7 @@ def model(): def main(): cost = model() - sgd_optimizer = optimizer.SGDOptimizer(learning_rate=0.2) + sgd_optimizer = SGDOptimizer(learning_rate=0.2) opts = sgd_optimizer.minimize(cost) if USE_GPU: diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py index 91fc79a987..e69b915a9c 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py @@ -1,12 +1,11 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers -import paddle.v2.fluid.nets as nets import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid.nets as nets from paddle.v2.fluid.executor import Executor - -import numpy as np +from paddle.v2.fluid.optimizer import AdamOptimizer def convolution_net(input_dim, class_dim=2, emb_dim=32, hid_dim=32): @@ -31,7 +30,7 @@ def convolution_net(input_dim, class_dim=2, emb_dim=32, hid_dim=32): act="softmax") cost = layers.cross_entropy(input=prediction, label=label) avg_cost = layers.mean(x=cost) - adam_optimizer = optimizer.AdamOptimizer(learning_rate=0.002) + adam_optimizer = AdamOptimizer(learning_rate=0.002) opts = adam_optimizer.minimize(avg_cost) acc = layers.accuracy(input=prediction, label=label) return avg_cost, acc diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py index 8c3d448835..65d4454250 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py @@ -1,12 +1,10 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers -import paddle.v2.fluid.nets as nets import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor - -import numpy as np +from paddle.v2.fluid.optimizer import AdamOptimizer def stacked_lstm_net(input_dim, @@ -41,7 +39,7 @@ def stacked_lstm_net(input_dim, act='softmax') cost = layers.cross_entropy(input=prediction, label=label) avg_cost = layers.mean(x=cost) - adam_optimizer = optimizer.AdamOptimizer(learning_rate=0.002) + adam_optimizer = AdamOptimizer(learning_rate=0.002) opts = adam_optimizer.minimize(avg_cost) acc = layers.accuracy(input=prediction, label=label) return avg_cost, acc diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py index a7d791c1f3..280f6e902c 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py @@ -1,11 +1,10 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor - -import numpy as np +from paddle.v2.fluid.optimizer import AdamOptimizer def lstm_net(dict_dim, class_dim=2, emb_dim=32, seq_len=80, batch_size=50): @@ -33,7 +32,7 @@ def lstm_net(dict_dim, class_dim=2, emb_dim=32, seq_len=80, batch_size=50): cost = layers.cross_entropy(input=prediction, label=label) avg_cost = layers.mean(x=cost) - adam_optimizer = optimizer.AdamOptimizer(learning_rate=0.002) + adam_optimizer = AdamOptimizer(learning_rate=0.002) opts = adam_optimizer.minimize(avg_cost) acc = layers.accuracy(input=prediction, label=label) diff --git a/python/paddle/v2/fluid/tests/book/test_word2vec.py b/python/paddle/v2/fluid/tests/book/test_word2vec.py index 9dcb6f2fea..afa7b28519 100644 --- a/python/paddle/v2/fluid/tests/book/test_word2vec.py +++ b/python/paddle/v2/fluid/tests/book/test_word2vec.py @@ -1,11 +1,10 @@ +import numpy as np import paddle.v2 as paddle -import paddle.v2.fluid.layers as layers import paddle.v2.fluid.core as core -import paddle.v2.fluid.optimizer as optimizer import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor - -import numpy as np +from paddle.v2.fluid.optimizer import SGDOptimizer PASS_NUM = 100 EMBED_SIZE = 32 @@ -17,26 +16,11 @@ IS_SPARSE = True word_dict = paddle.dataset.imikolov.build_dict() dict_size = len(word_dict) -first_word = layers.data( - name='firstw', - shape=[1], - data_type='int64') -second_word = layers.data( - name='secondw', - shape=[1], - data_type='int64') -third_word = layers.data( - name='thirdw', - shape=[1], - data_type='int64') -forth_word = layers.data( - name='forthw', - shape=[1], - data_type='int64') -next_word = layers.data( - name='nextw', - shape=[1], - data_type='int64') +first_word = layers.data(name='firstw', shape=[1], data_type='int64') +second_word = layers.data(name='secondw', shape=[1], data_type='int64') +third_word = layers.data(name='thirdw', shape=[1], data_type='int64') +forth_word = layers.data(name='forthw', shape=[1], data_type='int64') +next_word = layers.data(name='nextw', shape=[1], data_type='int64') embed_first = layers.embedding( input=first_word, @@ -64,19 +48,12 @@ embed_forth = layers.embedding( param_attr={'name': 'shared_w'}) concat_embed = layers.concat( - input=[embed_first, embed_second, embed_third, embed_forth], - axis=1) -hidden1 = layers.fc(input=concat_embed, - size=HIDDEN_SIZE, - act='sigmoid') -predict_word = layers.fc(input=hidden1, - size=dict_size, - act='softmax') -cost = layers.cross_entropy( - input=predict_word, - label=next_word) + input=[embed_first, embed_second, embed_third, embed_forth], axis=1) +hidden1 = layers.fc(input=concat_embed, size=HIDDEN_SIZE, act='sigmoid') +predict_word = layers.fc(input=hidden1, size=dict_size, act='softmax') +cost = layers.cross_entropy(input=predict_word, label=next_word) avg_cost = layers.mean(x=cost) -sgd_optimizer = optimizer.SGDOptimizer(learning_rate=0.001) +sgd_optimizer = SGDOptimizer(learning_rate=0.001) opts = sgd_optimizer.minimize(avg_cost) train_reader = paddle.batch( From e01b09410d7408e539f9a51f954b7378ea0c4ce9 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 17 Nov 2017 12:30:05 +0800 Subject: [PATCH 32/43] remove test_CompareTwoOpts --- paddle/trainer/tests/CMakeLists.txt | 10 - .../tests/sample_trainer_config_opt_a.conf | 44 ----- .../tests/sample_trainer_config_opt_b.conf | 44 ----- paddle/trainer/tests/test_CompareTwoOpts.cpp | 184 ------------------ 4 files changed, 282 deletions(-) delete mode 100644 paddle/trainer/tests/sample_trainer_config_opt_a.conf delete mode 100644 paddle/trainer/tests/sample_trainer_config_opt_b.conf delete mode 100644 paddle/trainer/tests/test_CompareTwoOpts.cpp diff --git a/paddle/trainer/tests/CMakeLists.txt b/paddle/trainer/tests/CMakeLists.txt index 3168f3c0ff..80665551ec 100644 --- a/paddle/trainer/tests/CMakeLists.txt +++ b/paddle/trainer/tests/CMakeLists.txt @@ -29,16 +29,6 @@ if(WITH_PYTHON) WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) endif() -############### test_CompareTwoOpts ################### -add_unittest_without_exec(test_CompareTwoOpts - test_CompareTwoOpts.cpp) -add_test(NAME test_CompareTwoOpts - COMMAND ${PADDLE_SOURCE_DIR}/paddle/.set_python_path.sh -d ${PADDLE_SOURCE_DIR}/python/ - ${CMAKE_CURRENT_BINARY_DIR}/test_CompareTwoOpts - --config_file_a=trainer/tests/sample_trainer_config_opt_a.conf --config_file_b=trainer/tests/sample_trainer_config_opt_b.conf - --num_passes=1 --need_high_accuracy=0 - WORKING_DIRECTORY ${PADDLE_SOURCE_DIR}/paddle/) - ################# test_recurrent_machine_generation ############### add_unittest_without_exec(test_recurrent_machine_generation test_recurrent_machine_generation.cpp) diff --git a/paddle/trainer/tests/sample_trainer_config_opt_a.conf b/paddle/trainer/tests/sample_trainer_config_opt_a.conf deleted file mode 100644 index 8ece96f595..0000000000 --- a/paddle/trainer/tests/sample_trainer_config_opt_a.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -################################### Data Configuration ################################### -TrainData(SimpleData( - files = "trainer/tests/sample_filelist.txt", - feat_dim = 3, - context_len = 0, - buffer_capacity = 1000000)) -################################### Algorithm Configuration ################################### -settings(batch_size = 1000, - learning_method = MomentumOptimizer(momentum=0.5, sparse=False)) -################################### Network Configuration ################################### -data = data_layer(name ="input", size=3) - -fc1 = fc_layer(input=data, size=800, - bias_attr=True, - act=SigmoidActivation()) - -fc2 = fc_layer(input=fc1, size=800, - bias_attr=True, - act=SigmoidActivation()) - -output = fc_layer(input=[fc1, fc2], size=10, - bias_attr=True, - act=SoftmaxActivation()) - -lbl = data_layer(name ="label", size=1) - -cost = classification_cost(input=output, label=lbl) -outputs(cost) diff --git a/paddle/trainer/tests/sample_trainer_config_opt_b.conf b/paddle/trainer/tests/sample_trainer_config_opt_b.conf deleted file mode 100644 index 8ece96f595..0000000000 --- a/paddle/trainer/tests/sample_trainer_config_opt_b.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -################################### Data Configuration ################################### -TrainData(SimpleData( - files = "trainer/tests/sample_filelist.txt", - feat_dim = 3, - context_len = 0, - buffer_capacity = 1000000)) -################################### Algorithm Configuration ################################### -settings(batch_size = 1000, - learning_method = MomentumOptimizer(momentum=0.5, sparse=False)) -################################### Network Configuration ################################### -data = data_layer(name ="input", size=3) - -fc1 = fc_layer(input=data, size=800, - bias_attr=True, - act=SigmoidActivation()) - -fc2 = fc_layer(input=fc1, size=800, - bias_attr=True, - act=SigmoidActivation()) - -output = fc_layer(input=[fc1, fc2], size=10, - bias_attr=True, - act=SoftmaxActivation()) - -lbl = data_layer(name ="label", size=1) - -cost = classification_cost(input=output, label=lbl) -outputs(cost) diff --git a/paddle/trainer/tests/test_CompareTwoOpts.cpp b/paddle/trainer/tests/test_CompareTwoOpts.cpp deleted file mode 100644 index 383505f813..0000000000 --- a/paddle/trainer/tests/test_CompareTwoOpts.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include -#include -#include -#include - -#include "paddle/trainer/Trainer.h" - -using namespace paddle; // NOLINT -using namespace std; // NOLINT - -DECLARE_int32(gpu_id); - -DECLARE_bool(local); -DECLARE_bool(use_gpu); - -DECLARE_string(config); -DECLARE_string(nics); - -DEFINE_string(config_file_a, "", "config of one network to compare"); -DEFINE_string(config_file_b, "", "config of another network to compare"); -DEFINE_bool(need_high_accuracy, - true, - "whether need to run in double accuracy (recommended)"); -DEFINE_double( - max_diff_ratio, - 0.0f, - "max diff ratio allowed for outputs and parameters (value/gradient)"); - -struct ComData { - vector outArgs; - vector parameters; -}; - -void calcGradient(ComData& data, const string configFile) { - FLAGS_config = configFile; - - FLAGS_local = true; - FLAGS_use_gpu = false; - - FLAGS_nics = ""; - - *ThreadLocalRand::getSeed() = 0; - srand(0); - - Trainer trainer; - trainer.init(TrainerConfigHelper::createFromFlagConfig(), false); - - data.parameters = trainer.getGradientMachine()->getParameters(); - trainer.getDataProvider()->setSkipShuffle(); - trainer.train(); -} - -void checkBuffer(real* A, - const char* desA, - real* B, - const char* desB, - size_t len, - size_t width = 1) { - int nNum = 0; - for (size_t i = 0; i < len; ++i) { - real diff = fabs(A[i] - B[i]); - if (diff > 0.0f && - diff / std::max(fabs(A[i]), fabs(B[i])) > FLAGS_max_diff_ratio) { - nNum++; - LOG(INFO) << "Row: " << i / width << ", " << desA << " : " << A[i] - << " " << desB << " : " << B[i]; - } - } - EXPECT_EQ(0, nNum); - LOG(INFO) << "\n\n"; -} - -void compareGradient(ComData& comDataA, ComData& comDataB) { - vector outArgsA = comDataA.outArgs; - vector outArgsB = comDataB.outArgs; - - for (size_t i = 0; i < outArgsA.size(); ++i) { - CpuMatrix matA(outArgsA[i].value->getHeight(), - outArgsA[i].value->getWidth()); - CpuMatrix matB(outArgsB[i].value->getHeight(), - outArgsB[i].value->getWidth()); - - matA.copyFrom(*outArgsA[i].value); - matB.copyFrom(*outArgsB[i].value); - - LOG(INFO) << "\n--------------------------------" - << " Check Network Output_" << i << ":" - << " -------------------------------------\n"; - checkBuffer(matA.getData(), - "network A output", - matB.getData(), - "network B output", - matA.getElementCnt(), - matA.getWidth()); - } - - vector& parametersA = comDataA.parameters; - vector& parametersB = comDataB.parameters; - - LOG(INFO) << "\n\n--------------------------------" - << " Check Gradient Machine Parameters:" - << " -------------------------------------\n"; - for (size_t i = 0; i < parametersA.size(); ++i) { - ParameterPtr parameterA, parameterB; - parameterA = parametersA[i]; - parameterB = parametersB[i]; - - CpuVector paraA(parameterA->getSize()); - CpuVector paraB(parameterB->getSize()); - paraA.copyFrom(*parameterA->getBuf(PARAMETER_VALUE)); - paraB.copyFrom(*parameterB->getBuf(PARAMETER_VALUE)); - - LOG(INFO) << "\n\n----------- PARAMETER_VALUE: " << parameterA->getName() - << " ; size : " << paraA.getSize() << " ------------"; - checkBuffer(paraA.getData(), - "Network A", - paraB.getData(), - "Network B", - paraA.getSize()); - - CpuVector gradA(*parameterA->getBuf(PARAMETER_GRADIENT)); - CpuVector gradB(*parameterB->getBuf(PARAMETER_GRADIENT)); - - LOG(INFO) << "\n\n----------- PARAMETER_GRADIENT: " << parameterA->getName() - << " ; size : " << gradA.getSize() << " -----------"; - checkBuffer(gradA.getData(), - "Network A", - gradB.getData(), - "Network B", - gradA.getSize()); - } -} - -TEST(Trainer, create) { - ComData dataA; - calcGradient(dataA, FLAGS_config_file_a); - LOG(INFO) << "\n\ntraining of Network A is finished\n\n"; - - ComData dataB; - calcGradient(dataB, FLAGS_config_file_b); - LOG(INFO) << "\n\ntraining of the Network B is finished\n\n"; - - compareGradient(dataA, dataB); -} - -int main(int argc, char** argv) { - paddle::initMain(argc, argv); - testing::InitGoogleTest(&argc, argv); - initPython(argc, argv); - -#ifndef PADDLE_TYPE_DOUBLE - if (FLAGS_need_high_accuracy) { - LOG(INFO) << "skip test due to it's need high accuracy"; - return 0; - } - if (FLAGS_max_diff_ratio == 0.0f) { - FLAGS_max_diff_ratio = 2e-4; - LOG(INFO) << "auto set max_diff_ratio " << FLAGS_max_diff_ratio - << " in low accuracy mode"; - } -#else - if (FLAGS_max_diff_ratio == 0.0f) { - FLAGS_max_diff_ratio = 2e-7; - LOG(INFO) << "auto set max_diff_ratio " << FLAGS_max_diff_ratio - << " in high accuracy mode"; - } -#endif - int ret = RUN_ALL_TESTS(); - return ret; -} From 1578c20aaf474ecbb3c3d082be9964a9fce26fa6 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 17 Nov 2017 13:28:56 +0800 Subject: [PATCH 33/43] add the missing macro PADDLE_USE_MKLDNN --- cmake/external/mkldnn.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index b80b6b90c0..fc52d339d7 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -67,4 +67,5 @@ ADD_LIBRARY(mkldnn SHARED IMPORTED GLOBAL) SET_PROPERTY(TARGET mkldnn PROPERTY IMPORTED_LOCATION ${MKLDNN_LIB}) ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) MESSAGE(STATUS "MKLDNN library: ${MKLDNN_LIB}") +add_definitions(-DPADDLE_USE_MKLDNN) LIST(APPEND external_project_dependencies mkldnn) From aa2507187ef41d9c14de343751b7d6cf35a3af00 Mon Sep 17 00:00:00 2001 From: ranqiu Date: Fri, 17 Nov 2017 13:59:02 +0800 Subject: [PATCH 34/43] add dot_prod_layer --- paddle/gserver/layers/DotProdLayer.cpp | 95 +++++++++++++++++++ paddle/gserver/tests/test_LayerGrad.cpp | 15 +++ python/paddle/trainer/config_parser.py | 9 ++ .../paddle/trainer_config_helpers/layers.py | 41 ++++++++ 4 files changed, 160 insertions(+) create mode 100644 paddle/gserver/layers/DotProdLayer.cpp diff --git a/paddle/gserver/layers/DotProdLayer.cpp b/paddle/gserver/layers/DotProdLayer.cpp new file mode 100644 index 0000000000..ae71a3d4eb --- /dev/null +++ b/paddle/gserver/layers/DotProdLayer.cpp @@ -0,0 +1,95 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "Layer.h" +#include "paddle/math/Matrix.h" +#include "paddle/utils/Logging.h" +#include "paddle/utils/Stat.h" + +namespace paddle { + +/** + * @brief A layer for computing the dot product of two vectors + * Input1: vector (batchSize * dim) + * Input2: vector (batchSize * dim) + * Output: a matrix: (batchSize * 1) + */ + +class DotProdLayer : public Layer { +public: + explicit DotProdLayer(const LayerConfig& config) : Layer(config) {} + + ~DotProdLayer() {} + + bool init(const LayerMap& layerMap, + const ParameterMap& parameterMap) override; + + void forward(PassType passType) override; + void backward(const UpdateCallback& callback = nullptr) override; +}; + +REGISTER_LAYER(dot_prod, DotProdLayer); + +bool DotProdLayer::init(const LayerMap& layerMap, + const ParameterMap& parameterMap) { + Layer::init(layerMap, parameterMap); + + CHECK_EQ(inputLayers_.size(), 2U); + CHECK_EQ(1, getSize()) << "Dimension mismatch"; + + return true; +} + +void DotProdLayer::forward(PassType passType) { + Layer::forward(passType); + + MatrixPtr inV0 = getInputValue(0); + MatrixPtr inV1 = getInputValue(1); + + size_t batchSize = inV0->getHeight(); + CHECK_EQ(inV1->getHeight(), batchSize); + + { + REGISTER_TIMER_INFO("FwResetTimer", getName().c_str()); + reserveOutput(batchSize, 1); + } + + MatrixPtr outV = getOutputValue(); + { + REGISTER_TIMER_INFO("FwDotProdTimer", getName().c_str()); + outV->sumOfProducts(*inV0, *inV1, 1, 0); + } +} + +void DotProdLayer::backward(const UpdateCallback& callback) { + MatrixPtr inV0 = getInputValue(0); + MatrixPtr inV1 = getInputValue(1); + MatrixPtr outG = getOutputGrad(); + MatrixPtr inG0 = getInputGrad(0); + MatrixPtr inG1 = getInputGrad(1); + + { + REGISTER_TIMER_INFO("BwDotProdTimer", getName().c_str()); + + if (inG0) { + inG0->addRowScale(0, *inV1, *outG); + } + + if (inG1) { + inG1->addRowScale(0, *inV0, *outG); + } + } +} + +} // namespace paddle diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index 3517d293e3..de2db0b3f7 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -1081,6 +1081,21 @@ TEST(Layer, InterpolationLayer) { } } +TEST(Layer, DotProdLayer) { + TestConfig config; + config.layerConfig.set_type("dot_prod"); + config.layerConfig.set_size(1); + + config.inputDefs.push_back({INPUT_DATA, "layer_0", 10, 0}); + config.layerConfig.add_inputs(); + config.inputDefs.push_back({INPUT_DATA, "layer_1", 10, 0}); + config.layerConfig.add_inputs(); + + for (auto useGpu : {false, true}) { + testLayerGrad(config, "dot_prod", 100, false, useGpu); + } +} + TEST(Layer, OuterProdLayer) { TestConfig config; config.layerConfig.set_type("out_prod"); diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 5bd68e211a..6d1cc5ad70 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -3209,6 +3209,15 @@ class SubNestedSequenceLayer(LayerBase): self.set_layer_size(size) +@config_layer('dot_prod') +class DotProdLayer(LayerBase): + def __init__(self, name, inputs, device=None): + super(DotProdLayer, self).__init__( + name, 'dot_prod', 0, inputs, device=device) + config_assert(len(inputs) == 2, 'DotProdLayer must have 2 inputs') + self.set_layer_size(1) + + @config_layer('out_prod') class OuterProdLayer(LayerBase): def __init__(self, name, inputs, device=None): diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index a02eba007d..388535d53a 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -115,6 +115,7 @@ __all__ = [ 'huber_classification_cost', 'block_expand_layer', 'maxout_layer', + 'dot_prod_layer', 'out_prod_layer', 'printer_layer', 'print_layer', @@ -197,6 +198,7 @@ class LayerType(object): SCALING_LAYER = 'scaling' TRANS_LAYER = 'trans' ROTATE_LAYER = 'rotate' + DOT_PROD_LAYER = 'dot_prod' OUT_PROD_LAYER = 'out_prod' FEATURE_MAP_EXPAND_LAYER = 'featmap_expand' @@ -4140,6 +4142,45 @@ def maxid_layer(input, name=None, layer_attr=None): size=l.config.size) +@wrap_name_default() +def dot_prod_layer(input1, input2, name=None, layer_attr=None): + """ + A layer for computing the dot product of two vectors. + + The example usage is: + + .. code-block:: python + + dot_prod = dot_prod_layer(input1=vec1, input2=vec2) + + :param name: The name of this layer. It is optional. + :type name: basestring + :param input1: The first input layer. + :type input: LayerOutput + :param input2: The second input layer. + :type input2: LayerOutput + :param layer_attr: The extra layer attribute. See ExtraLayerAttribute for + details. + :type layer_attr: ExtraLayerAttribute. + :return: LayerOutput object. + :rtype: LayerOutput + """ + assert isinstance(input1, LayerOutput) + assert isinstance(input2, LayerOutput) + assert input1.size == input2.size, ("Two inputs should have the same size.") + + l = Layer( + name=name, + type=LayerType.DOT_PROD_LAYER, + inputs=[input1.name, input2.name], + **ExtraLayerAttribute.to_kwargs(layer_attr)) + return LayerOutput( + name=name, + layer_type=LayerType.DOT_PROD_LAYER, + parents=[input1, input2], + size=l.config.size) + + @wrap_name_default() def out_prod_layer(input1, input2, name=None, layer_attr=None): """ From 082bc7af56414cf3a8a156a4dbcbd4df18a61357 Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Fri, 17 Nov 2017 13:10:01 +0800 Subject: [PATCH 35/43] Use CUDA_ARCH_NAME=All in the paddle/scripts/docker/build.sh and remove 20 21(20) in cmake/cuda.cmake. --- cmake/cuda.cmake | 38 ++++------------------------------ paddle/scripts/docker/build.sh | 2 ++ 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/cmake/cuda.cmake b/cmake/cuda.cmake index 5d0840f273..9c7a52164a 100644 --- a/cmake/cuda.cmake +++ b/cmake/cuda.cmake @@ -2,9 +2,9 @@ if(NOT WITH_GPU) return() endif() -set(paddle_known_gpu_archs "20 21(20) 30 35 50 52 60 61 70") -set(paddle_known_gpu_archs7 "20 21(20) 30 35 50 52") -set(paddle_known_gpu_archs8 "20 21(20) 30 35 50 52 60 61") +set(paddle_known_gpu_archs "30 35 50 52 60 61 70") +set(paddle_known_gpu_archs7 "30 35 50 52") +set(paddle_known_gpu_archs8 "30 35 50 52 60 61") ###################################################################################### # A function for automatic detection of GPUs installed (if autodetection is enabled) @@ -40,7 +40,7 @@ function(detect_installed_gpus out_variable) STRING(REGEX REPLACE "\n" ";" nvcc_out "${nvcc_out}") list(GET nvcc_out -1 nvcc_out) string(REPLACE "2.1" "2.1(2.0)" nvcc_out "${nvcc_out}") - set(CUDA_gpu_detect_output ${nvcc_out} CACHE INTERNAL "Returned GPU architetures from caffe_detect_gpus tool" FORCE) + set(CUDA_gpu_detect_output ${nvcc_out} CACHE INTERNAL "Returned GPU architetures from detect_installed_gpus tool" FORCE) endif() endif() @@ -137,10 +137,6 @@ function(select_nvcc_arch_flags out_variable) set(${out_variable}_readable ${nvcc_archs_readable} PARENT_SCOPE) endfunction() -if(NOT CUDA_FOUND) - return() -endif() - message(STATUS "CUDA detected: " ${CUDA_VERSION}) if (${CUDA_VERSION} LESS 7.0) set(paddle_known_gpu_archs ${paddle_known_gpu_archs}) @@ -163,37 +159,11 @@ if(NOT WITH_DSO) list(APPEND EXTERNAL_LIBS ${CUDNN_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_curand_LIBRARY} ${NCCL_LIBRARY}) endif(NOT WITH_DSO) -# find libcuda.so and lbnvrtc.so -# For libcuda.so, we will find it under lib, lib64, and then the -# stubs folder, in case we are building on a system that does not -# have cuda driver installed. On windows, we also search under the -# folder lib/x64. - -find_library(CUDA_CUDA_LIB cuda - PATHS ${CUDA_TOOLKIT_ROOT_DIR} - PATH_SUFFIXES lib lib64 lib/stubs lib64/stubs lib/x64) -find_library(CUDA_NVRTC_LIB nvrtc - PATHS ${CUDA_TOOLKIT_ROOT_DIR} - PATH_SUFFIXES lib lib64 lib/x64) - # setting nvcc arch flags select_nvcc_arch_flags(NVCC_FLAGS_EXTRA) list(APPEND CUDA_NVCC_FLAGS ${NVCC_FLAGS_EXTRA}) message(STATUS "Added CUDA NVCC flags for: ${NVCC_FLAGS_EXTRA_readable}") -if(CUDA_CUDA_LIB) - # message(STATUS "Found libcuda: ${CUDA_CUDA_LIB}") - list(APPEND Caffe2_DEPENDENCY_LIBS ${CUDA_CUDA_LIB}) -else() - message(FATAL_ERROR "Cannot find libcuda.so.") -endif() -if(CUDA_NVRTC_LIB) - # message(STATUS "Found libnvrtc: ${CUDA_NVRTC_LIB}") - list(APPEND Caffe2_DEPENDENCY_LIBS ${CUDA_NVRTC_LIB}) -else() - message(FATAL_ERROR "Cannot find libnvrtc.so.") -endif() - # Set C++11 support set(CUDA_PROPAGATE_HOST_FLAGS OFF) diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index e9c89eee1a..8dddb2be9c 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -34,6 +34,7 @@ function cmake_gen() { ${PYTHON_FLAGS} -DWITH_DOC=OFF -DWITH_GPU=${WITH_GPU:-OFF} + -DCUDA_ARCH_NAME=All -DWITH_MKLDNN=${WITH_MKLDNN:-ON} -DWITH_MKLML=${WITH_MKLML:-ON} -DWITH_AVX=${WITH_AVX:-OFF} @@ -56,6 +57,7 @@ EOF ${PYTHON_FLAGS} \ -DWITH_DOC=OFF \ -DWITH_GPU=${WITH_GPU:-OFF} \ + -DCUDA_ARCH_NAME=All \ -DWITH_MKLDNN=${WITH_MKLDNN:-ON} \ -DWITH_MKLML=${WITH_MKLML:-ON} \ -DWITH_AVX=${WITH_AVX:-OFF} \ From aa83e19e24d2318381bd4859588f15d43336f041 Mon Sep 17 00:00:00 2001 From: guosheng Date: Fri, 17 Nov 2017 14:18:34 +0800 Subject: [PATCH 36/43] Remove lstm_op including in gru_op --- paddle/operators/gru_op.h | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/paddle/operators/gru_op.h b/paddle/operators/gru_op.h index a7264507bb..1b18368e0e 100644 --- a/paddle/operators/gru_op.h +++ b/paddle/operators/gru_op.h @@ -14,7 +14,6 @@ #pragma once -#include "paddle/operators/lstm_op.h" #include "paddle/operators/math/gru_compute.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/sequence2batch.h" @@ -25,6 +24,18 @@ namespace paddle { namespace operators { +using LoDTensor = framework::LoDTensor; +using Tensor = framework::Tensor; + +template +inline void ReorderInitState(const platform::DeviceContext& ctx, + const framework::Tensor& src, const size_t* index, + framework::Tensor* dst, bool indexed_src) { + math::CopyMatrixRowsFunctor row_shuffle; + dst->mutable_data(src.dims(), ctx.GetPlace()); + row_shuffle(ctx, src, index, *dst, indexed_src); +} + template class GRUKernel : public framework::OpKernel { public: @@ -194,16 +205,9 @@ class GRUGradKernel : public framework::OpKernel { batch_reset_hidden_prev_grad.Slice(bstart, bend); gru_grad.resetOutputGrad = reset_hidden_prev_grad_t.data(); if (n == 0) { - if (h0) { - gru_value.prevOutValue = ordered_h0.data(); - } else { - gru_value.prevOutValue = nullptr; - } - if (h0 && h0_grad) { - gru_grad.prevOutGrad = ordered_h0_grad.data(); - } else { - gru_grad.prevOutGrad = nullptr; - } + gru_value.prevOutValue = h0 ? ordered_h0.data() : nullptr; + gru_grad.prevOutGrad = + h0 && h0_grad ? ordered_h0_grad.data() : nullptr; } else { int bstart_pre = static_cast(batch_starts[n - 1]); Tensor hidden_prev_t = batch_hidden->Slice(bstart_pre, bstart); From a391a44dd0cc0c6dba0aa5e2e66a65689a8ccdfa Mon Sep 17 00:00:00 2001 From: QI JUN Date: Fri, 17 Nov 2017 00:45:01 -0600 Subject: [PATCH 37/43] remove v2 framework (#5722) --- .../paddle/v2/{framework => fluid}/tests/test_is_empty_op.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename python/paddle/v2/{framework => fluid}/tests/test_is_empty_op.py (92%) diff --git a/python/paddle/v2/framework/tests/test_is_empty_op.py b/python/paddle/v2/fluid/tests/test_is_empty_op.py similarity index 92% rename from python/paddle/v2/framework/tests/test_is_empty_op.py rename to python/paddle/v2/fluid/tests/test_is_empty_op.py index 129d1c1944..ed6e3fe24f 100644 --- a/python/paddle/v2/framework/tests/test_is_empty_op.py +++ b/python/paddle/v2/fluid/tests/test_is_empty_op.py @@ -1,7 +1,7 @@ import unittest import numpy as np -from paddle.v2.framework.op import Operator -import paddle.v2.framework.core as core +from paddle.v2.fluid.op import Operator +import paddle.v2.fluid.core as core def create_tensor(scope, name, np_data): From 40450401a68215fa86be900426ee54075371149e Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 17 Nov 2017 11:29:32 +0800 Subject: [PATCH 38/43] change macro, can use omp when paddle use mklml --- paddle/parameter/ParameterUpdateFunctions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/parameter/ParameterUpdateFunctions.cpp b/paddle/parameter/ParameterUpdateFunctions.cpp index 8b3be062b6..1898598e49 100644 --- a/paddle/parameter/ParameterUpdateFunctions.cpp +++ b/paddle/parameter/ParameterUpdateFunctions.cpp @@ -30,7 +30,7 @@ void sgdUpdateCpu(real learningRate, const real* grad, real* momentumVec) { decayRate *= learningRate; -#ifdef PADDLE_USE_MKLDNN +#ifdef PADDLE_USE_MKLML #pragma omp parallel for #endif for (size_t i = 0; i < size; ++i) { From f5df46e1a4beda7bd79e929b180ea91ee6c2ca9a Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 17 Nov 2017 15:32:50 +0800 Subject: [PATCH 39/43] rename all Mkldnn to MKLDNN --- paddle/gserver/layers/MKLDNNLayer.cpp | 2 +- paddle/gserver/tests/CMakeLists.txt | 2 +- paddle/gserver/tests/MKLDNNTester.h | 2 +- python/paddle/trainer/config_parser.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index 2125155c6c..671e00cad3 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -21,7 +21,7 @@ namespace paddle { bool MKLDNNLayer::init(const LayerMap& layerMap, const ParameterMap& parameterMap) { - CHECK(FLAGS_use_mkldnn) << "MkldnnLayers only support use_mkldnn." + CHECK(FLAGS_use_mkldnn) << "MKLDNNLayers only support use_mkldnn." << "Please set WITH_MKL=ON " << "and set use_mkldnn=True"; CHECK(!useGpu_) << "Do not support GPU yet"; diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index 09e1b949c2..c295ea19c9 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -29,7 +29,7 @@ gserver_test(test_KmaxSeqScore) gserver_test(test_Expand) gserver_test(test_MaxPoolingWithMaskOutput) -########## test_Mkldnn layers and activations ########## +########## test_MKLDNN layers and activations ########## if(WITH_MKLDNN) add_unittest_without_exec(test_MKLDNN test_MKLDNN.cpp diff --git a/paddle/gserver/tests/MKLDNNTester.h b/paddle/gserver/tests/MKLDNNTester.h index ca55a45bc7..9d61533c0b 100644 --- a/paddle/gserver/tests/MKLDNNTester.h +++ b/paddle/gserver/tests/MKLDNNTester.h @@ -23,7 +23,7 @@ limitations under the License. */ namespace paddle { /** - * @brief test the functionality of Mkldnnlayers + * @brief test the functionality of MKLDNNlayers and MKLDNNActivations * refer to paddle original function */ class MKLDNNTester { diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 5bd68e211a..d968dfb945 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -1826,7 +1826,7 @@ class FCLayer(LayerBase): self.layer_type = 'mkldnn_fc' config_assert( len(inputs) == 1, - "MkldnnFCLayer support one and only one input!") + "MKLDNNFCLayer support one and only one input!") super(FCLayer, self).__init__( name, self.layer_type, size, inputs=inputs, **xargs) for input_index in xrange(len(self.inputs)): @@ -1837,7 +1837,7 @@ class FCLayer(LayerBase): sparse = format == "csr" or format == "csc" if use_mkldnn: config_assert(not sparse, - "MkldnnFCLayer do not support sparse format yet") + "MKLDNNFCLayer do not support sparse format yet") if use_mkldnn_wgt: dims = [self.config.size, input_layer.size] if sparse: @@ -1853,7 +1853,7 @@ class FCLayer(LayerBase): @config_layer('mkldnn_fc') -class MkldnnFcLayer(FCLayer): +class MKLDNNFcLayer(FCLayer): layer_type = 'mkldnn_fc' From 2e1cd3313d502e3201551d3d443b549bc8c88cbf Mon Sep 17 00:00:00 2001 From: ranqiu Date: Fri, 17 Nov 2017 14:55:19 +0800 Subject: [PATCH 40/43] Update dot_prod_layer --- doc/api/v2/config/layer.rst | 10 +++++ paddle/gserver/layers/DotProdLayer.cpp | 6 ++- paddle/gserver/tests/test_LayerGrad.cpp | 2 +- python/paddle/trainer/config_parser.py | 5 ++- .../tests/configs/file_list.sh | 3 +- .../protostr/test_dot_prod_layer.protostr | 38 +++++++++++++++++++ .../tests/configs/test_dot_prod_layer.py | 7 ++++ 7 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 python/paddle/trainer_config_helpers/tests/configs/protostr/test_dot_prod_layer.protostr create mode 100644 python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py diff --git a/doc/api/v2/config/layer.rst b/doc/api/v2/config/layer.rst index 203506d7ab..b2b55ec419 100644 --- a/doc/api/v2/config/layer.rst +++ b/doc/api/v2/config/layer.rst @@ -335,6 +335,16 @@ bilinear_interp .. autoclass:: paddle.v2.layer.bilinear_interp :noindex: +dot_prod +--------- +.. autoclass:: paddle.v2.layer.dot_prod + :noindex: + +out_prod +-------- +.. autoclass:: paddle.v2.layer.out_prod + :noindex: + power ----- .. autoclass:: paddle.v2.layer.power diff --git a/paddle/gserver/layers/DotProdLayer.cpp b/paddle/gserver/layers/DotProdLayer.cpp index ae71a3d4eb..9e2dbe3c3c 100644 --- a/paddle/gserver/layers/DotProdLayer.cpp +++ b/paddle/gserver/layers/DotProdLayer.cpp @@ -20,7 +20,7 @@ limitations under the License. */ namespace paddle { /** - * @brief A layer for computing the dot product of two vectors + * @brief A layer for computing the dot product of two vectors. * Input1: vector (batchSize * dim) * Input2: vector (batchSize * dim) * Output: a matrix: (batchSize * 1) @@ -46,7 +46,8 @@ bool DotProdLayer::init(const LayerMap& layerMap, Layer::init(layerMap, parameterMap); CHECK_EQ(inputLayers_.size(), 2U); - CHECK_EQ(1, getSize()) << "Dimension mismatch"; + CHECK_EQ(1UL, getSize()) + << "The output dimensionality of this layer should be fixed to 1."; return true; } @@ -59,6 +60,7 @@ void DotProdLayer::forward(PassType passType) { size_t batchSize = inV0->getHeight(); CHECK_EQ(inV1->getHeight(), batchSize); + CHECK_EQ(inV0->getWidth(), inV1->getWidth()); { REGISTER_TIMER_INFO("FwResetTimer", getName().c_str()); diff --git a/paddle/gserver/tests/test_LayerGrad.cpp b/paddle/gserver/tests/test_LayerGrad.cpp index de2db0b3f7..fb4eea6f67 100644 --- a/paddle/gserver/tests/test_LayerGrad.cpp +++ b/paddle/gserver/tests/test_LayerGrad.cpp @@ -1092,7 +1092,7 @@ TEST(Layer, DotProdLayer) { config.layerConfig.add_inputs(); for (auto useGpu : {false, true}) { - testLayerGrad(config, "dot_prod", 100, false, useGpu); + testLayerGrad(config, "dot_prod", 10, false, useGpu); } } diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 6d1cc5ad70..fab280d1b0 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -3214,7 +3214,10 @@ class DotProdLayer(LayerBase): def __init__(self, name, inputs, device=None): super(DotProdLayer, self).__init__( name, 'dot_prod', 0, inputs, device=device) - config_assert(len(inputs) == 2, 'DotProdLayer must have 2 inputs') + config_assert(len(inputs) == 2, 'DotProdLayer must have 2 inputs.') + config_assert( + self.get_input_layer(0).size == self.get_input_layer(1).size, + "Two inputs should have the same size.") self.set_layer_size(1) diff --git a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh index 1c7451e0ab..0b269a1ff7 100755 --- a/python/paddle/trainer_config_helpers/tests/configs/file_list.sh +++ b/python/paddle/trainer_config_helpers/tests/configs/file_list.sh @@ -10,6 +10,7 @@ test_prelu_layer test_row_conv test_detection_output_layer test_multibox_loss_la test_recursive_topology test_gated_unit_layer test_clip_layer test_row_l2_norm_layer test_kmax_seq_socre_layer test_sub_nested_seq_select_layer test_scale_shift_layer test_seq_slice_layer test_cross_entropy_over_beam test_roi_pool_layer test_pooling3D_layer -test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer test_scale_sub_region_layer) +test_conv3d_layer test_deconv3d_layer test_BatchNorm3D test_resize_layer test_scale_sub_region_layer +test_dot_prod_layer) export whole_configs=(test_split_datasource) diff --git a/python/paddle/trainer_config_helpers/tests/configs/protostr/test_dot_prod_layer.protostr b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_dot_prod_layer.protostr new file mode 100644 index 0000000000..f1530c382c --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/protostr/test_dot_prod_layer.protostr @@ -0,0 +1,38 @@ +type: "nn" +layers { + name: "vector1" + type: "data" + size: 10 + active_type: "" +} +layers { + name: "vector2" + type: "data" + size: 10 + active_type: "" +} +layers { + name: "__dot_prod_layer_0__" + type: "dot_prod" + size: 1 + active_type: "" + inputs { + input_layer_name: "vector1" + } + inputs { + input_layer_name: "vector2" + } +} +input_layer_names: "vector1" +input_layer_names: "vector2" +output_layer_names: "__dot_prod_layer_0__" +sub_models { + name: "root" + layer_names: "vector1" + layer_names: "vector2" + layer_names: "__dot_prod_layer_0__" + input_layer_names: "vector1" + input_layer_names: "vector2" + output_layer_names: "__dot_prod_layer_0__" + is_recurrent_layer_group: false +} diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py new file mode 100644 index 0000000000..e52d48dde0 --- /dev/null +++ b/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py @@ -0,0 +1,7 @@ +from paddle.trainer_config_helpers import * + +vec1 = data_layer(name='vector1', size=10) +vec2 = data_layer(name='vector2', size=10) +dot_product = dot_prod_layer(input1=vec1, input2=vec2) + +outputs(dot_product) From c359e39b59d76abfb795e5eaf7d36bfec17c2bb9 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Fri, 17 Nov 2017 16:54:32 +0800 Subject: [PATCH 41/43] add double type kernel --- paddle/operators/conv_op.cc | 12 ++++++++---- paddle/operators/conv_op.cu.cc | 12 ++++++++---- paddle/operators/conv_transpose_op.cc | 12 ++++++++---- paddle/operators/conv_transpose_op.cu.cc | 12 ++++++++---- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/paddle/operators/conv_op.cc b/paddle/operators/conv_op.cc index 687d741cb2..7a36a9b21a 100644 --- a/paddle/operators/conv_op.cc +++ b/paddle/operators/conv_op.cc @@ -225,11 +225,15 @@ REGISTER_OP(conv3d, ops::ConvOp, ops::Conv3DOpMaker, conv3d_grad, ops::ConvOpGrad); REGISTER_OP_CPU_KERNEL(conv2d, - ops::GemmConvKernel); + ops::GemmConvKernel, + ops::GemmConvKernel); REGISTER_OP_CPU_KERNEL( - conv2d_grad, ops::GemmConvGradKernel); + conv2d_grad, ops::GemmConvGradKernel, + ops::GemmConvGradKernel); REGISTER_OP_CPU_KERNEL(conv3d, - ops::GemmConvKernel); + ops::GemmConvKernel, + ops::GemmConvKernel); REGISTER_OP_CPU_KERNEL( - conv3d_grad, ops::GemmConvGradKernel); + conv3d_grad, ops::GemmConvGradKernel, + ops::GemmConvGradKernel); diff --git a/paddle/operators/conv_op.cu.cc b/paddle/operators/conv_op.cu.cc index 8e6f9da455..546451234a 100644 --- a/paddle/operators/conv_op.cu.cc +++ b/paddle/operators/conv_op.cu.cc @@ -17,11 +17,15 @@ namespace ops = paddle::operators; REGISTER_OP_GPU_KERNEL(conv2d, - ops::GemmConvKernel); + ops::GemmConvKernel, + ops::GemmConvKernel); REGISTER_OP_GPU_KERNEL( - conv2d_grad, ops::GemmConvGradKernel); + conv2d_grad, ops::GemmConvGradKernel, + ops::GemmConvGradKernel); REGISTER_OP_GPU_KERNEL(conv3d, - ops::GemmConvKernel); + ops::GemmConvKernel, + ops::GemmConvKernel); REGISTER_OP_GPU_KERNEL( - conv3d_grad, ops::GemmConvGradKernel); + conv3d_grad, ops::GemmConvGradKernel, + ops::GemmConvGradKernel); diff --git a/paddle/operators/conv_transpose_op.cc b/paddle/operators/conv_transpose_op.cc index 310e3f5c93..3e55ef036a 100644 --- a/paddle/operators/conv_transpose_op.cc +++ b/paddle/operators/conv_transpose_op.cc @@ -185,17 +185,21 @@ REGISTER_OP(conv2d_transpose, ops::ConvTransposeOp, ops::Conv2DTransposeOpMaker, REGISTER_OP_CPU_KERNEL( conv2d_transpose, - ops::GemmConvTransposeKernel); + ops::GemmConvTransposeKernel, + ops::GemmConvTransposeKernel); REGISTER_OP_CPU_KERNEL( conv2d_transpose_grad, - ops::GemmConvTransposeGradKernel); + ops::GemmConvTransposeGradKernel, + ops::GemmConvTransposeGradKernel); REGISTER_OP(conv3d_transpose, ops::ConvTransposeOp, ops::Conv3DTransposeOpMaker, conv3d_transpose_grad, ops::ConvTransposeOpGrad); REGISTER_OP_CPU_KERNEL( conv3d_transpose, - ops::GemmConvTransposeKernel); + ops::GemmConvTransposeKernel, + ops::GemmConvTransposeKernel); REGISTER_OP_CPU_KERNEL( conv3d_transpose_grad, - ops::GemmConvTransposeGradKernel); + ops::GemmConvTransposeGradKernel, + ops::GemmConvTransposeGradKernel); diff --git a/paddle/operators/conv_transpose_op.cu.cc b/paddle/operators/conv_transpose_op.cu.cc index 401cddb379..4165eb0c7b 100644 --- a/paddle/operators/conv_transpose_op.cu.cc +++ b/paddle/operators/conv_transpose_op.cu.cc @@ -18,14 +18,18 @@ namespace ops = paddle::operators; REGISTER_OP_GPU_KERNEL( conv2d_transpose, - ops::GemmConvTransposeKernel); + ops::GemmConvTransposeKernel, + ops::GemmConvTransposeKernel); REGISTER_OP_GPU_KERNEL( conv2d_transpose_grad, - ops::GemmConvTransposeGradKernel); + ops::GemmConvTransposeGradKernel, + ops::GemmConvTransposeGradKernel); REGISTER_OP_GPU_KERNEL( conv3d_transpose, - ops::GemmConvTransposeKernel); + ops::GemmConvTransposeKernel, + ops::GemmConvTransposeKernel); REGISTER_OP_GPU_KERNEL( conv3d_transpose_grad, - ops::GemmConvTransposeGradKernel); + ops::GemmConvTransposeGradKernel, + ops::GemmConvTransposeGradKernel); From 18f1f53555b33323d16861ceef7cd925fa663973 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 17 Nov 2017 17:16:04 +0800 Subject: [PATCH 42/43] change message level from warning to status, and fix hard number in version --- CMakeLists.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e30dff70f..0f25fdee54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,14 +108,11 @@ else() set(THIRD_PARTY_BUILD_TYPE Release) endif() -if(WITH_MKL) - set(WITH_MKLML ON) - set(WITH_MKLDNN ${AVX2_FOUND}) - if(NOT WITH_MKLDNN) - message(WARNING "Do not have AVX2 intrinsics and disabled MKL-DNN") - endif() +set(WITH_MKLML ${WITH_MKL}) +if (WITH_MKL AND ${AVX2_FOUND}) + set(WITH_MKLDNN ON) else() - set(WITH_MKLML OFF) + message(STATUS "Do not have AVX2 intrinsics and disabled MKL-DNN") set(WITH_MKLDNN OFF) endif() From 044d671e73baf912c369bca61c1a0e494ac49091 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 17 Nov 2017 01:31:57 -0800 Subject: [PATCH 43/43] Rename 'argu' in framework.py to 'arg' (#5723) --- python/paddle/v2/fluid/framework.py | 45 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index a6eca2d719..acca6ba35c 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -4,7 +4,10 @@ import collections import numpy as np import copy -__all__ = ['Block', 'Variable', 'Program', 'Operator', 'default_startup_program', 'default_main_program'] +__all__ = [ + 'Block', 'Variable', 'Program', 'Operator', 'default_startup_program', + 'default_main_program' +] def unique_name(prefix): @@ -232,17 +235,17 @@ class Operator(object): in_proto.name) if found: - in_argus = inputs[in_proto.name] - if not isinstance(in_argus, list): - in_argus = [in_argus] - if not in_proto.duplicable and len(in_argus) > 1: + in_args = inputs[in_proto.name] + if not isinstance(in_args, list): + in_args = [in_args] + if not in_proto.duplicable and len(in_args) > 1: raise ValueError( "Input %s expects only one input, but %d are given." - % (in_proto.name, len(in_argus))) - in_argu_names = [] - for argu in in_argus: - in_argu_names.append(argu.name) - self.desc.set_input(in_proto.name, in_argu_names) + % (in_proto.name, len(in_args))) + in_arg_names = [] + for arg in in_args: + in_arg_names.append(arg.name) + self.desc.set_input(in_proto.name, in_arg_names) else: self.desc.set_input(in_proto.name, []) @@ -260,18 +263,18 @@ class Operator(object): str(e) for e in given))) for out_proto in proto.outputs: - out_argus = outputs[out_proto.name] - if not isinstance(out_argus, list): - out_argus = [out_argus] - if not out_proto.duplicable and len(out_argus) > 1: + out_args = outputs[out_proto.name] + if not isinstance(out_args, list): + out_args = [out_args] + if not out_proto.duplicable and len(out_args) > 1: raise ValueError( "Output %s expects only one output, but %d are given." % - (out_proto.name, len(out_argus))) - out_argu_names = [] - for argu in out_argus: - out_argu_names.append(argu.name) - argu.op = self - self.desc.set_output(out_proto.name, out_argu_names) + (out_proto.name, len(out_args))) + out_arg_names = [] + for arg in out_args: + out_arg_names.append(arg.name) + arg.op = self + self.desc.set_output(out_proto.name, out_arg_names) if attrs is not None: if not isinstance(attrs, dict): @@ -582,8 +585,10 @@ class Parameter(Variable): g_main_program = Program() g_startup_program = Program() + def default_startup_program(): return g_startup_program + def default_main_program(): return g_main_program