From: Frank Yung-Fong Tang Date: Sat, 8 May 2021 22:02:03 +0000 (+0000) Subject: ICU-21569 LSTM Part 3 Add Java implementation X-Git-Tag: cldr/2021-06-15~17 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2a72af07ace7ca4c29eb42ac5eddfa614fb77e8d;p=icu ICU-21569 LSTM Part 3 Add Java implementation See #1706 --- diff --git a/.github/lstm_for_th_my.json b/.github/lstm_for_th_my.json index 75d2f4ede53..b4aeb24d0ed 100644 --- a/.github/lstm_for_th_my.json +++ b/.github/lstm_for_th_my.json @@ -2,15 +2,9 @@ // License & terms of use: http://www.unicode.org/copyright.html // Generated using tools/cldr/cldr-to-icu/build-icu-data.xml // -// Remove Burmese and Thai dictionaries and replaced with lstm models. +// Include Burmese and Thai lstm models. { "featureFilters": { - "brkitr_dictionaries": { - "excludelist": [ - "burmesedict", - "thaidict" - ] - }, "brkitr_lstm": { "includelist": [ "Thai_graphclust_model4_heavy", diff --git a/.github/workflows/icu_ci.yml b/.github/workflows/icu_ci.yml index 17e0daa292a..967f1460f2c 100644 --- a/.github/workflows/icu_ci.yml +++ b/.github/workflows/icu_ci.yml @@ -53,6 +53,36 @@ jobs: [ -d icu4j/out/junit-results ] && cd icu4j && cat `find out/junit-results -name "*.txt" -exec grep -l FAILED {} \;`; if: ${{ failure() }} + # ICU4J build and unit test under lstm + lstm-icu4j-build-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout and setup + uses: actions/checkout@v2 + with: + lfs: true + - name: Checkout lfs objects + run: git lfs pull + - uses: actions/setup-java@v1 + with: + java-version: '11' + - name: Config LSTM and Rebuild data jar + run: | + cd icu4c/source; + ICU_DATA_BUILDTOOL_OPTS=--include_uni_core_data ICU_DATA_FILTER_FILE=../../.github/lstm_for_th_my.json ./runConfigureICU --enable-debug --disable-release Linux -disable-layoutex; + make clean; + make -j2 ICU4J_ROOT=../../../icu4j icu4j-data-install; + cd ../.. + - name: ICU4J + run: | + cd icu4j; + ant init; + ant check; + - name: List failures (if any) + run: | + [ -d icu4j/out/junit-results ] && cd icu4j && cat `find out/junit-results -name "*.txt" -exec grep -l FAILED {} \;`; + if: ${{ failure() }} + # gcc debug build. # Includes dependency checker. # Note - the dependency checker needs to be run on both a debug and an optimized build. diff --git a/icu4c/source/data/brkitr/root.txt b/icu4c/source/data/brkitr/root.txt index 052d736b0a7..114c5e780a0 100644 --- a/icu4c/source/data/brkitr/root.txt +++ b/icu4c/source/data/brkitr/root.txt @@ -23,7 +23,7 @@ root{ Thai:process(dependency){"thaidict.dict"} } lstm{ - Thai:process(dependency){"Thai_graphclust_model4_heavy.res"} - Mymr:process(dependency){"Burmese_graphclust_model5_heavy.res"} + Thai{"Thai_graphclust_model4_heavy.res"} + Mymr{"Burmese_graphclust_model5_heavy.res"} } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/breakiter/LSTMBreakEngine.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/breakiter/LSTMBreakEngine.java new file mode 100644 index 00000000000..28bcc60b7ba --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/breakiter/LSTMBreakEngine.java @@ -0,0 +1,444 @@ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +/** + * A LSTMBreakEngine + */ +package com.ibm.icu.impl.breakiter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.CharacterIterator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.ibm.icu.impl.ICUData; +import com.ibm.icu.impl.ICUResourceBundle; +import com.ibm.icu.lang.UCharacter; +import com.ibm.icu.lang.UProperty; +import com.ibm.icu.lang.UScript; +import com.ibm.icu.text.BreakIterator; +import com.ibm.icu.text.UnicodeSet; +import com.ibm.icu.util.UResourceBundle; + +/** + * @internal + */ +public class LSTMBreakEngine extends DictionaryBreakEngine { + public enum EmbeddingType { + UNKNOWN, + CODE_POINTS, + GRAPHEME_CLUSTER + } + + public enum LSTMClass { + BEGIN, + INSIDE, + END, + SINGLE, + } + + private static float[][] make2DArray(int[] data, int start, int d1, int d2) { + byte[] bytes = new byte[4]; + float [][] result = new float[d1][d2]; + for (int i = 0; i < d1 ; i++) { + for (int j = 0; j < d2 ; j++) { + int d = data[start++]; + bytes[0] = (byte) (d >> 24); + bytes[1] = (byte) (d >> 16); + bytes[2] = (byte) (d >> 8); + bytes[3] = (byte) (d /*>> 0*/); + result[i][j] = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); + } + } + return result; + } + + private static float[] make1DArray(int[] data, int start, int d1) { + byte[] bytes = new byte[4]; + float [] result = new float[d1]; + for (int i = 0; i < d1 ; i++) { + int d = data[start++]; + bytes[0] = (byte) (d >> 24); + bytes[1] = (byte) (d >> 16); + bytes[2] = (byte) (d >> 8); + bytes[3] = (byte) (d /*>> 0*/); + result[i] = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); + } + return result; + } + + /** @internal */ + public static class LSTMData { + private LSTMData() { + } + + public LSTMData(UResourceBundle rb) { + int embeddings = rb.get("embeddings").getInt(); + int hunits = rb.get("hunits").getInt(); + this.fType = EmbeddingType.UNKNOWN; + this.fName = rb.get("model").getString(); + String typeString = rb.get("type").getString(); + if (typeString.equals("codepoints")) { + this.fType = EmbeddingType.CODE_POINTS; + } else if (typeString.equals("graphclust")) { + this.fType = EmbeddingType.GRAPHEME_CLUSTER; + } + String[] dict = rb.get("dict").getStringArray(); + int[] data = rb.get("data").getIntVector(); + int dataLen = data.length; + int numIndex = dict.length; + fDict = new HashMap(numIndex + 1); + int idx = 0; + for (String embedding : dict){ + fDict.put(embedding, idx++); + } + int mat1Size = (numIndex + 1) * embeddings; + int mat2Size = embeddings * 4 * hunits; + int mat3Size = hunits * 4 * hunits; + int mat4Size = 4 * hunits; + int mat5Size = mat2Size; + int mat6Size = mat3Size; + int mat7Size = mat4Size; + int mat8Size = 2 * hunits * 4; + int mat9Size = 4; + assert dataLen == mat1Size + mat2Size + mat3Size + mat4Size + mat5Size + mat6Size + mat7Size + mat8Size + mat9Size; + int start = 0; + this.fEmbedding = make2DArray(data, start, (numIndex+1), embeddings); + start += mat1Size; + this.fForwardW = make2DArray(data, start, embeddings, 4 * hunits); + start += mat2Size; + this.fForwardU = make2DArray(data, start, hunits, 4 * hunits); + start += mat3Size; + this.fForwardB = make1DArray(data, start, 4 * hunits); + start += mat4Size; + this.fBackwardW = make2DArray(data, start, embeddings, 4 * hunits); + start += mat5Size; + this.fBackwardU = make2DArray(data, start, hunits, 4 * hunits); + start += mat6Size; + this.fBackwardB = make1DArray(data, start, 4 * hunits); + start += mat7Size; + this.fOutputW = make2DArray(data, start, 2 * hunits, 4); + start += mat8Size; + this.fOutputB = make1DArray(data, start, 4); + } + + public EmbeddingType fType; + public String fName; + public Map fDict; + public float fEmbedding[][]; + public float fForwardW[][]; + public float fForwardU[][]; + public float fForwardB[]; + public float fBackwardW[][]; + public float fBackwardU[][]; + public float fBackwardB[]; + public float fOutputW[][]; + public float fOutputB[]; + } + + // Minimum word size + private static final byte MIN_WORD = 2; + + // Minimum number of characters for two words + private static final byte MIN_WORD_SPAN = MIN_WORD * 2; + + abstract class Vectorizer { + public Vectorizer(Map dict) { + this.fDict = dict; + } + abstract public void vectorize(CharacterIterator fIter, int rangeStart, int rangeEnd, + List offsets, List indicies); + protected int getIndex(String token) { + return fDict.getOrDefault(token, fDict.size()); + } + private Map fDict; + } + + class CodePointsVectorizer extends Vectorizer { + public CodePointsVectorizer(Map dict) { + super(dict); + } + + public void vectorize(CharacterIterator fIter, int rangeStart, int rangeEnd, + List offsets, List indicies) { + fIter.setIndex(rangeStart); + for (char c = fIter.current(); + c != CharacterIterator.DONE && fIter.getIndex() < rangeEnd; + c = fIter.next()) { + offsets.add(fIter.getIndex()); + indicies.add(getIndex(String.valueOf(c))); + } + } + } + + class GraphemeClusterVectorizer extends Vectorizer { + public GraphemeClusterVectorizer(Map dict) { + super(dict); + } + + private String substring(CharacterIterator text, int startPos, int endPos) { + int saved = text.getIndex(); + text.setIndex(startPos); + StringBuilder sb = new StringBuilder(); + for (char c = text.current(); + c != CharacterIterator.DONE && text.getIndex() < endPos; + c = text.next()) { + sb.append(c); + } + text.setIndex(saved); + return sb.toString(); + } + + public void vectorize(CharacterIterator text, int startPos, int endPos, + List offsets, List indicies) { + BreakIterator iter = BreakIterator.getCharacterInstance(); + iter.setText(text); + int last = iter.next(startPos); + for (int curr = iter.next(); curr != BreakIterator.DONE && curr <= endPos; curr = iter.next()) { + offsets.add(last); + String segment = substring(text, last, curr); + int index = getIndex(segment); + indicies.add(index); + last = curr; + } + } + } + + private final LSTMData fData; + private int fScript; + private final Vectorizer fVectorizer; + + private Vectorizer makeVectorizer(LSTMData data) { + switch(data.fType) { + case CODE_POINTS: + return new CodePointsVectorizer(data.fDict); + case GRAPHEME_CLUSTER: + return new GraphemeClusterVectorizer(data.fDict); + default: + return null; + } + } + + public LSTMBreakEngine(int script, UnicodeSet set, LSTMData data) { + setCharacters(set); + this.fScript = script; + this.fData = data; + this.fVectorizer = makeVectorizer(this.fData); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public boolean handles(int c) { + return fScript == UCharacter.getIntPropertyValue(c, UProperty.SCRIPT); + } + + static private void addDotProductTo(final float [] a, final float[][] b, float[] result) { + assert a.length == b.length; + assert b[0].length == result.length; + for (int i = 0; i < result.length; i++) { + for (int j = 0; j < a.length; j++) { + result[i] += a[j] * b[j][i]; + } + } + } + + static private void addTo(final float [] a, float[] result) { + assert a.length == result.length; + for (int i = 0; i < result.length; i++) { + result[i] += a[i]; + } + } + + static private void hadamardProductTo(final float [] a, float[] result) { + assert a.length == result.length; + for (int i = 0; i < result.length; i++) { + result[i] *= a[i]; + } + } + + static private void addHadamardProductTo(final float [] a, final float [] b, float[] result) { + assert a.length == result.length; + assert b.length == result.length; + for (int i = 0; i < result.length; i++) { + result[i] += a[i] * b[i]; + } + } + + static private void sigmoid(float [] result, int start, int length) { + assert start < result.length; + assert start + length <= result.length; + for (int i = start; i < start + length; i++) { + result[i] = (float)(1.0/(1.0 + Math.exp(-result[i]))); + } + } + + static private void tanh(float [] result, int start, int length) { + assert start < result.length; + assert start + length <= result.length; + for (int i = start; i < start + length; i++) { + result[i] = (float)Math.tanh(result[i]); + } + } + + static private int maxIndex(float [] data) { + int index = 0; + float max = data[0]; + for (int i = 1; i < data.length; i++) { + if (data[i] > max) { + max = data[i]; + index = i; + } + } + return index; + } + + /* + static private void print(float [] data) { + for (int i=0; i < data.length; i++) { + System.out.format(" %e", data[i]); + if (i % 4 == 3) { + System.out.println(); + } + } + System.out.println(); + } + */ + + private float[] compute(final float[][] W, final float[][] U, final float[] B, + final float[] x, float[] h, float[] c) { + // ifco = x * W + h * U + b + float[] ifco = Arrays.copyOf(B, B.length); + addDotProductTo(x, W, ifco); + float[] hU = new float[B.length]; + addDotProductTo(h, U, ifco); + + int hunits = B.length / 4; + sigmoid(ifco, 0*hunits, hunits); // i + sigmoid(ifco, 1*hunits, hunits); // f + tanh(ifco, 2*hunits, hunits); // c_ + sigmoid(ifco, 3*hunits, hunits); // o + + hadamardProductTo(Arrays.copyOfRange(ifco, hunits, 2*hunits), c); + addHadamardProductTo(Arrays.copyOf(ifco, hunits), + Arrays.copyOfRange(ifco, 2*hunits, 3*hunits), c); + + h = Arrays.copyOf(c, c.length); + tanh(h, 0, h.length); + hadamardProductTo(Arrays.copyOfRange(ifco, 3*hunits, 4*hunits), h); + // System.out.println("c"); + // print(c); + // System.out.println("h"); + // print(h); + return h; + } + + @Override + public int divideUpDictionaryRange(CharacterIterator fIter, int rangeStart, int rangeEnd, + DequeI foundBreaks) { + int beginSize = foundBreaks.size(); + + if ((rangeEnd - rangeStart) < MIN_WORD_SPAN) { + return 0; // Not enough characters for word + } + List offsets = new ArrayList(rangeEnd - rangeStart); + List indicies = new ArrayList(rangeEnd - rangeStart); + + fVectorizer.vectorize(fIter, rangeStart, rangeEnd, offsets, indicies); + + // To save the needed memory usage, the following is different from the + // Python or ICU4X implementation. We first perform the Backward LSTM + // and then merge the iteration of the forward LSTM and the output layer + // together because we only need to remember the h[t-1] for Forward LSTM. + int inputSeqLength = indicies.size(); + int hunits = this.fData.fForwardU.length; + float c[] = new float[hunits]; + + // TODO: limit size of hBackward. If input_seq_len is too big, we could + // run out of memory. + // Backward LSTM + float hBackward[][] = new float[inputSeqLength][hunits]; + for (int i = inputSeqLength - 1; i >= 0; i--) { + if (i != inputSeqLength - 1) { + hBackward[i] = Arrays.copyOf(hBackward[i+1], hunits); + } + // System.out.println("Backward LSTM " + i); + hBackward[i] = compute(this.fData.fBackwardW, this.fData.fBackwardU, this.fData.fBackwardB, + this.fData.fEmbedding[indicies.get(i)], + hBackward[i], c); + } + + c = new float[hunits]; + float forwardH[] = new float[hunits]; + float both[] = new float[2*hunits]; + + // The following iteration merge the forward LSTM and the output layer + // together. + for (int i = 0 ; i < inputSeqLength; i++) { + // Forward LSTM + forwardH = compute(this.fData.fForwardW, this.fData.fForwardU, this.fData.fForwardB, + this.fData.fEmbedding[indicies.get(i)], + forwardH, c); + + System.arraycopy(forwardH, 0, both, 0, hunits); + System.arraycopy(hBackward[i], 0, both, hunits, hunits); + + //System.out.println("Merged " + i); + //print(both); + + // Output layer + // logp = fbRow * fOutputW + fOutputB + float logp[] = Arrays.copyOf(this.fData.fOutputB, this.fData.fOutputB.length); + addDotProductTo(both, this.fData.fOutputW, logp); + + int current = maxIndex(logp); + + // BIES logic. + if (current == LSTMClass.BEGIN.ordinal() || + current == LSTMClass.SINGLE.ordinal()) { + if (i != 0) { + foundBreaks.push(offsets.get(i)); + } + } + } + + return foundBreaks.size() - beginSize; + } + + public static LSTMData createData(UResourceBundle bundle) { + return new LSTMData(bundle); + } + + private static String defaultLSTM(int script) { + ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BRKITR_BASE_NAME); + return rb.getStringWithFallback("lstm/" + UScript.getShortName(script)); + } + + public static LSTMData createData(int script) { + if (script != UScript.KHMER && script != UScript.LAO && script != UScript.MYANMAR && script != UScript.THAI) { + return null; + } + String name = defaultLSTM(script); + name = name.substring(0, name.indexOf(".")); + + UResourceBundle rb = UResourceBundle.getBundleInstance( + ICUData.ICU_BRKITR_BASE_NAME, name, + ICUResourceBundle.ICU_DATA_CLASS_LOADER); + return createData(rb); + } + + public static LSTMBreakEngine create(int script, LSTMData data) { + String setExpr = "[[:" + UScript.getShortName(script) + ":]&[:LineBreak=SA:]]"; + UnicodeSet set = new UnicodeSet(); + set.applyPattern(setExpr); + set.compact(); + return new LSTMBreakEngine(script, set, data); + } +} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedBreakIterator.java b/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedBreakIterator.java index e2d628bba24..6bf2a264136 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedBreakIterator.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedBreakIterator.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.text.CharacterIterator; import java.util.ArrayList; import java.util.List; +import java.util.MissingResourceException; import com.ibm.icu.impl.CharacterIteration; import com.ibm.icu.impl.ICUBinary; @@ -30,6 +31,7 @@ import com.ibm.icu.impl.breakiter.BurmeseBreakEngine; import com.ibm.icu.impl.breakiter.CjkBreakEngine; import com.ibm.icu.impl.breakiter.DictionaryBreakEngine; import com.ibm.icu.impl.breakiter.KhmerBreakEngine; +import com.ibm.icu.impl.breakiter.LSTMBreakEngine; import com.ibm.icu.impl.breakiter.LanguageBreakEngine; import com.ibm.icu.impl.breakiter.LaoBreakEngine; import com.ibm.icu.impl.breakiter.ThaiBreakEngine; @@ -722,13 +724,21 @@ public class RuleBasedBreakIterator extends BreakIterator { try { switch (script) { case UScript.THAI: - eng = new ThaiBreakEngine(); + try { + eng = LSTMBreakEngine.create(script, LSTMBreakEngine.createData(script)); + } catch (MissingResourceException e) { + eng = new ThaiBreakEngine(); + } break; case UScript.LAO: eng = new LaoBreakEngine(); break; case UScript.MYANMAR: - eng = new BurmeseBreakEngine(); + try { + eng = LSTMBreakEngine.create(script, LSTMBreakEngine.createData(script)); + } catch (MissingResourceException e) { + eng = new BurmeseBreakEngine(); + } break; case UScript.KHMER: eng = new KhmerBreakEngine(); diff --git a/icu4j/main/shared/data/icudata.jar b/icu4j/main/shared/data/icudata.jar index 31762473f6d..c7d3d129e98 100644 --- a/icu4j/main/shared/data/icudata.jar +++ b/icu4j/main/shared/data/icudata.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f02ab2967eaf73b6d28c8340d70b20d5f194f6c0ac24fe8464b25fd56763b04 -size 13383786 +oid sha256:6614997945a564e6888da79d06283f519a34d9e13caf8b10eba68ee9f098efda +size 13383850 diff --git a/icu4j/main/shared/data/testdata.jar b/icu4j/main/shared/data/testdata.jar index 783e69a66c7..64da45ae4b0 100644 --- a/icu4j/main/shared/data/testdata.jar +++ b/icu4j/main/shared/data/testdata.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26a032e0c9492cd986546eefb5ba54687598eb431caed531ccb00b12469421ca -size 726547 +oid sha256:9184fdb3a90361165d9c90081a681450a6a049beaf669a24580dcf23134142c7 +size 825810 diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Burmese_graphclust_model5_heavy_Test.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Burmese_graphclust_model5_heavy_Test.txt new file mode 100644 index 00000000000..a559cd7da26 --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Burmese_graphclust_model5_heavy_Test.txt @@ -0,0 +1,8 @@ +# Copyright (C) 2021 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +Model: Burmese_graphclust_model5_heavy +Embedding: grapheme_clusters_tf +Input: အပြည်ပြည်ဆိုင်ရာလူ့အခွင့်အရေးကြေညာစာတမ်း +Output: |အပြည်|ပြည်|ဆိုင်ရာ|လူ့|အခွင့်အရေး|ကြေညာစာတမ်း| +Input: မျိုးရိုးဂုဏ်သိက္ခာနှင့်တကွ +Output: |မျိုး|ရိုး|ဂုဏ်|သိက္ခာ|နှင့်|တ|ကွ| diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/LSTMBreakEngineTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/LSTMBreakEngineTest.java new file mode 100644 index 00000000000..0208e68fe29 --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/LSTMBreakEngineTest.java @@ -0,0 +1,117 @@ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +package com.ibm.icu.dev.test.rbbi; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Iterator; +import java.util.stream.Stream; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.impl.breakiter.DictionaryBreakEngine; +import com.ibm.icu.impl.breakiter.LSTMBreakEngine; +import com.ibm.icu.lang.UScript; +import com.ibm.icu.util.UResourceBundle; + +/** + * LSTMBreakEngine data driven test. + * Perform the tests from the file *_Test.txt. + * The test data file is common to both ICU4C and ICU4J. + * See the data file for a description of the tests. + * + */ +@RunWith(JUnit4.class) +public class LSTMBreakEngineTest extends TestFmwk { + + private static final ClassLoader testLoader = LSTMBreakEngineTest.class.getClassLoader(); + + public LSTMBreakEngineTest() { + } + + @Test + public void TestThaiGraphclust() { + runTestFromFile("Thai_graphclust_model4_heavy_Test.txt", UScript.THAI); + } + + @Test + public void TestThaiCodepoints() { + runTestFromFile("Thai_codepoints_exclusive_model5_heavy_Test.txt", UScript.THAI); + } + + @Test + public void TestBurmeseGraphclust() { + runTestFromFile("Burmese_graphclust_model5_heavy_Test.txt", UScript.MYANMAR); + } + + private LSTMBreakEngine createEngineFromTestData(String modelName, int script) { + UResourceBundle bundle = UResourceBundle.getBundleInstance( + "com/ibm/icu/dev/data/testdata", modelName, testLoader); + return LSTMBreakEngine.create(script, LSTMBreakEngine.createData(bundle)); + } + + private void runTestFromFile(String filename, int script) { + + String testString; + InputStream is = LSTMBreakEngineTest.class.getResourceAsStream("/com/ibm/icu/dev/test/rbbi/" + filename); + if (is == null) { + errln("Could not open test data file " + filename); + return; + } + Stream lines = (new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))).lines(); + Iterator iterator = lines.iterator(); + int caseNum = 0; + String expected = ""; + String actual = ""; + LSTMBreakEngine engine = null; + while (iterator.hasNext()) { + String line = iterator.next(); + String fields[] = line.split("\t"); + if (fields[0].equals("Model:")) { + engine = createEngineFromTestData(fields[1], script); + } else if (fields[0].equals("Input:")) { + caseNum++; + int length = fields[1].length(); + CharacterIterator input = new StringCharacterIterator(fields[1]); + DictionaryBreakEngine.DequeI foundBreaks = new DictionaryBreakEngine.DequeI(); + int ret = engine.findBreaks(input, 0, length, foundBreaks); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (int i = 0; i < foundBreaks.size(); i++) { + sb.append(foundBreaks.elementAt(i)).append(", "); + } + sb.append(length).append('}'); + actual = sb.toString(); + } else if (fields[0].equals("Output:")) { + StringBuilder sb = new StringBuilder(); + int sep; + int start = 0; + int curr = 0; + sb.append('{'); + while ((sep = fields[1].indexOf('|', start)) >= 0) { + int len = sep - start; + if (len > 0) { + if (curr > 0) { + sb.append(", "); + } + curr += len; + sb.append(curr); + } + start = sep + 1; + } + sb.append('}'); + expected = sb.toString(); + + assertEquals(line + " Test Case#" + caseNum , expected, actual); + } + } + } +} diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBILSTMTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBILSTMTest.java new file mode 100644 index 00000000000..06eb2f40ae8 --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBILSTMTest.java @@ -0,0 +1,118 @@ +// © 2021 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +package com.ibm.icu.dev.test.rbbi; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Iterator; +import java.util.stream.Stream; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.dev.test.TestUtil; +import com.ibm.icu.impl.breakiter.LSTMBreakEngine; +import com.ibm.icu.lang.UScript; +import com.ibm.icu.text.BreakIterator; + +/** + * RBBILSTMTest data driven test. + * Perform the tests from the file *_Test.txt against the RBBI method + * under LSTM configuration. The test will not run if it is not under LSTM configuration. + * The test data file is common to both ICU4C and ICU4J. + * See the data file for a description of the tests. + */ +@RunWith(JUnit4.class) +public class RBBILSTMTest extends TestFmwk { + public RBBILSTMTest() { + } + + @Test + public void TestLSTMThai() { + runTestFromFile("Thai_graphclust_model4_heavy_Test.txt", UScript.THAI); + } + + @Test + public void TestLSTMBurmese() { + runTestFromFile("Burmese_graphclust_model5_heavy_Test.txt", UScript.MYANMAR); + } + + private void runTestFromFile(String filename, int script) { + // The expectation in this test depends on LSTM, skip the test if the + // configuration is not build with LSTM data. + org.junit.Assume.assumeTrue(!TestUtil.skipLSTMTest()); + + BreakIterator bi = BreakIterator.getWordInstance(); + String testString; + InputStream is = RBBILSTMTest.class.getResourceAsStream("/com/ibm/icu/dev/test/rbbi/" + filename); + if (is == null) { + errln("Could not open test data file " + filename); + return; + } + Stream lines = (new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))).lines(); + Iterator iterator = lines.iterator(); + int caseNum = 0; + String expected = ""; + String actual = ""; + LSTMBreakEngine engine = null; + while (iterator.hasNext()) { + String line = iterator.next(); + String fields[] = line.split("\t"); + if (fields[0].equals("Model:")) { + String actualModelName = LSTMBreakEngine.createData(script).fName; + if (!actualModelName.equals(fields[1])) { + errln("The name of the built in model " + actualModelName + + " does not match the model (" + fields[1] + ") expected for this test"); + return; + } + } else if (fields[0].equals("Input:")) { + caseNum++; + int length = fields[1].length(); + String input = "prefix " + fields[1] + " suffix"; + bi.setText(input); + System.out.println("Input = " + input); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (int bp = bi.first(); bp != BreakIterator.DONE; bp = bi.next()) { + sb.append(bp); + if (bp != input.length()) { + sb.append(", "); + } + } + sb.append('}'); + actual = sb.toString(); + } else if (fields[0].equals("Output:")) { + StringBuilder sb = new StringBuilder(); + int sep; + int start = 0; + int curr = 0; + sb.append("{0, "); + String input = "prefix| |" + fields[1] + "| |suffix"; + while ((sep = input.indexOf('|', start)) >= 0) { + int len = sep - start; + if (len > 0) { + if (curr > 0) { + sb.append(", "); + } + curr += len; + sb.append(curr); + } + start = sep + 1; + } + sb.append(", ").append(curr + input.length() - start); + sb.append('}'); + expected = sb.toString(); + + assertEquals(input + " Test Case#" + caseNum , expected, actual); + actual = ""; + } + } + } +} diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITest.java index dae29ad0785..6f5c2201e2c 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITest.java @@ -26,6 +26,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.dev.test.TestUtil; import com.ibm.icu.impl.RBBIDataWrapper; import com.ibm.icu.text.BreakIterator; import com.ibm.icu.text.RuleBasedBreakIterator; @@ -42,6 +43,9 @@ public class RBBITest extends TestFmwk { @Test public void TestThaiDictionaryBreakIterator() { + // The expectations in this test heavily depends on the Thai dictionary. + // Therefore, we skip this test under the LSTM configuration. + org.junit.Assume.assumeTrue(!TestUtil.skipDictionaryTest()); int position; int index; int result[] = { 1, 2, 5, 10, 11, 12, 11, 10, 5, 2, 1, 0 }; diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITestExtended.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITestExtended.java index c809da8e143..76c76b02680 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITestExtended.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITestExtended.java @@ -19,6 +19,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.dev.test.TestUtil; import com.ibm.icu.impl.Utility; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.text.BreakIterator; @@ -52,6 +53,9 @@ static class TestParams { @Test public void TestExtended() { + // The expectations in this test heavily depends on the Thai dictionary. + // Therefore, we skip this test under the LSTM configuration. + org.junit.Assume.assumeTrue(!TestUtil.skipDictionaryTest()); TestParams tp = new TestParams(); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_codepoints_exclusive_model5_heavy_Test.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_codepoints_exclusive_model5_heavy_Test.txt new file mode 100644 index 00000000000..d47ceea6590 --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_codepoints_exclusive_model5_heavy_Test.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2020 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +Note: model Thai_codepoints_exclusive_model5_heavy has been trained using an exclusive data set. However, if you like you can still test it by other types of data sets (not recommended). +Model: Thai_codepoints_exclusive_model5_heavy +Embedding: codepoints +Input: ปฏิญญาสากลว่าด้วยสิทธิมนุษยชน +Output: |ปฏิญญา|สากลว่า|ด้วย|สิทธิ|มนุษย|ชน| +Input: คำปรารภ +Output: |คำ|ปรารภ| +Input: โดยที่การยอมรับนับถือเกียรติศักดิ์ประจำตัว +Output: |โดย|ที่|การ|ยอม|รับ|นับ|ถือ|เกียรติศักดิ์|ประจำ|ตัว| +Input: และสิทธิเท่าเทียมกันและโอนมิได้ของบรรดา +Output: |และ|สิทธิ|เท่า|เทียม|กัน|และ|โอน|มิ|ได้|ของ|บรรดา| +Input: สมาชิก +Output: |สมา|ชิก| +Input: ทั้ง +Output: |ทั้ง| +Input: หลายแห่งครอบครัว +Output: |หลาย|แห่ง|ครอบครัว| +Input: มนุษย์เป็นหลักมูลเหตุแห่งอิสรภาพ +Output: |มนุษย์|เป็น|หลักมูล|เหตุ|แห่ง|อิสรภาพ| +Input: ความยุติธรรม +Output: |ความ|ยุติ|ธรรม| +Input: และสันติภาพในโลก +Output: |และ|สันติภาพ|ใน|โลก| +Input: โดยที่การไม่นำพาและการเหยียดหยามต่อสิทธิมนุษยชน +Output: |โดย|ที่|การ|ไม่|นำ|พา|และ|การ|เหยียด|หยาม|ต่อ|สิทธิ|มนุษยชน| +Input: ยังผลให้มีการหระทำอันป่าเถื่อน +Output: |ยัง|ผล|ให้|มี|การ|หระทำ|อัน|ป่า|เถื่อ|น| +Input: ซี่งเป็นการละเมิดมโนธรรมของมนุษยชาติอย่างร้ายแรง +Output: |ซี่ง|เป็น|การ|ละเมิดมโนธรรม|ของ|มนุษยชาติ|อย่าง|ร้าย|แรง| +Input: และใต้ +Output: |และ|ใต้| +Input: ได้ +Output: |ได้| +Input: มีการประกาศว่า +Output: |มี|การ|ประกาศ|ว่า| +Input: ปณิธานสูงสุดของสามัญชนได้แก่ความต้องการให้มนุษย์มีชีวิตอยู่ในโลกด้วยอิสรภาพในการพูด +Output: |ปณิธาน|สูงสุด|ของ|สามัญชน|ได้|แก่|ความ|ต้องการ|ให้|มนุษย์|มี|ชีวิต|อยู่|ใน|โลก|ด้วย|อิสรภาพ|ใน|การ|พูด| +Input: และความเชื่อถือ +Output: |และ|ความ|เชื่อถือ| +Input: และอิสรภาพพ้นจากความหวาดกลัวและความต้องการ +Output: |และ|อิสรภาพ|พ้น|จาก|ความ|หวาด|กลัว|และ|ความ|ต้องการ| +Input: โดยที่เป็นการจำเป็นอย่างยิ่งที่สิทธิมนุษยชนควรได้รับความคุ้มครองโดยหลักบังคับของกฎหมาย +Output: |โดย|ที่|เป็น|การ|จำเป็น|อย่าง|ยิ่ง|ที่|สิทธิ|มนุษยชน|ควร|ได้|รับ|ความ|คุ้มครอง|โดย|หลัก|บังคับ|ของ|กฎหมาย| +Input: ถ้าไม่ประสงค์จะให้คนตกอยู่ในบังคับให้หันเข้าหาการขบถขัดขืนต่อทรราชและการกดขี่เป็นวิถีทางสุดท้าย +Output: |ถ้า|ไม่|ประสงค์|จะ|ให้|คน|ตก|อยู่|ใน|บังคับ|ให้|หัน|เข้า|หา|การ|ขบถ|ขัด|ขืน|ต่อทรราช|และ|การ|กด|ขี่|เป็น|วิถี|ทาง|สุด|ท้าย| +Input: โดยที่ประชากรแห่งสหประชาชาติได้ยืนยันไว้ในกฎบัตรถึงความเชื่อมั่นในสิทธิมนุษยชนอันเป็นหลักมูล +Output: |โดย|ที่|ประชากร|แห่ง|สหประชา|ชาติ|ได้|ยืน|ยัน|ไว้|ใน|กฎบัตร|ถึง|ความ|เชื่อมั่น|ใน|สิทธิ|มนุษยชน|อัน|เป็น|หลัก|มู|ล| +Input: ในเกียรติศักดิ์และคุณค่าของมนุษย์และในสิทธิเท่าเทียมกันของบรรดาชายและหญิง +Output: |ใน|เกียรติศักดิ์|และ|คุณค่า|ของ|มนุษย์|และ|ใน|สิทธิ|เท่า|เทียม|กัน|ของ|บรรดา|ชาย|และ|หญิง| +Input: และได้ตกลงใจที่จะส่งเสริมความก้าวหน้าทางสังคม +Output: |และ|ได้|ตก|ลงใจ|ที่|จะ|ส่ง|เสริม|ความ|ก้าว|หน้า|ทาง|สังคม| +Input: และมาตรฐานแห่งชีวิตที่ดีขึ้นด้วยในอิสรภาพ +Output: |และ|มาตรฐาน|แห่ง|ชีวิต|ที่|ดี|ขึ้น|ด้วย|ใน|อิสรภาพ| +Input: อันกว้างขวางยิ่งขึ้น +Output: |อัน|กว้าง|ขวาง|ยิ่ง|ขึ้น| +Input: โดยที่รัฐสมาชิกต่างปฎิญาณจะให้บรรลุถึงซึ่งการส่งเสริมการเคารพและการปฎิบัติตามทั่วสากลต่อสิทธิมนุษยชนและอิสรภาพหลักมูล +Output: |โดย|ที่|รัฐสมา|ชิก|ต่าง|ปฎิญาณ|จะ|ให้|บรรลุ|ถึง|ซึ่ง|การ|ส่ง|เสริม|การ|เคารพ|และ|การ|ปฎิบัติ|ตาม|ทั่วสากล|ต่อ|สิทธิ|มนุษยชน|และ|อิสรภาพ|หลัก|มู|ล| +Input: โดยร่วมมือกับสหประชาชาติ +Output: |โดย|ร่วม|มือ|กับ|สหประชา|ชาติ| +Input: โดยที่ความเข้าใจร่วมกันในสิทธิ +Output: |โดย|ที่|ความ|เข้าใจ|ร่วม|กัน|ใน|สิทธิ| +Input: และอิสรภาพเหล่านี้เป็นสิ่งสำคัญอย่างยิ่ง +Output: |และ|อิสรภาพ|เหล่า|นี้|เป็น|สิ่ง|สำคัญ|อย่าง|ยิ่ง| +Input: เพื่อให้ปฏิญาณนี้สำเร็จผลเต็มบริบูรณ์ +Output: |เพื่อ|ให้|ปฏิญาณ|นี้|สำเร็จ|ผล|เต็ม|บริบูรณ์| +Input: ฉะนั้น +Output: |ฉะนั้น| +Input: บัดนี้สมัชชาจึงประกาศว่า +Output: |บัด|นี้|สมัชชา|จึง|ประกาศ|ว่า| +Input: ปฏิญญาสากลว่าด้วยสิทธิมนุษยชนนี้ +Output: |ปฏิญญา|สากลว่า|ด้วย|สิทธิ|มนุษยชน|นี้| +Input: เป็นมาตรฐานร่วมกันแห่งความสำเร็จสำหรับบรรดาประชากรและประชาชาติทั้งหลาย +Output: |เป็น|มาตรฐาน|ร่วม|กัน|แห่ง|ความ|สำเร็จ|สำหรับ|บรรดา|ประชากร|และ|ประชาชาติ|ทั้ง|หลาย| +Input: เพื่อจุดหมายปลายทางที่ว่า +Output: |เพื่อ|จุดหมาย|ปลาย|ทาง|ที่|ว่า| +Input: เอกชนทุกคนและองค์การชองสังคมทุกองค์การ +Output: |เอกชน|ทุก|คน|และ|องค์|การ|ชอง|สังคม|ทุก|องค์|การ| +Input: โดยการรำลึกถึงปฏิญญานี้เป็นเนืองนิจ +Output: |โดย|การ|รำลึก|ถึง|ปฏิญญา|นี้|เป็น|เนือง|นิจ| +Input: จะบากบั่นพยายามด้วยการสอนและศึกษา +Output: |จะ|บาก|บั่นพยายาม|ด้วย|การ|สอน|และ|ศึกษา| +Input: ในอันที่จะส่งเสริมการเคารพสิทธิและอิสรภาพเหล่านี้ +Output: |ใน|อัน|ที่|จะ|ส่ง|เสริม|การ|เคารพ|สิทธิ|และ|อิสรภาพ|เหล่า|นี้| +Input: และด้วยมาตรการอันก้าวหน้าทั้งในประเทศและระหว่างประเทศ +Output: |และ|ด้วย|มาตรการ|อัน|ก้าว|หน้า|ทั้ง|ใน|ประเทศ|และ|ระหว่าง|ประเทศ| +Input: ในอันที่จะให้มีการยอมรับนับถือ +Output: |ใน|อัน|ที่|จะ|ให้|มี|การ|ยอม|รับ|นับ|ถือ| +Input: และการปฏิบัติตามโดยสากลและอย่างเป็นผลจริงจัง +Output: |และ|การ|ปฏิบัติ|ตาม|โดย|สากล|และ|อย่าง|เป็น|ผล|จริง|จัง| +Input: ทั้งในบรรดาประชาชนของรัฐสมาชิกด้วยกันเอง +Output: |ทั้ง|ใน|บรรดา|ประชาชน|ของ|รัฐสมา|ชิก|ด้วย|กัน|เอง| +Input: และในบรรดาประชาชนของดินแดนที่อยู่ใตัอำนาจของรัฐนั้น +Output: |และ|ใน|บรรดา|ประชาชน|ของ|ดิน|แดน|ที่|อยู่|ใตัอำนาจ|ของ|รัฐ|นั้น| +Input: ๆ +Output: |ๆ| diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_graphclust_model4_heavy_Test.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_graphclust_model4_heavy_Test.txt new file mode 100644 index 00000000000..e8fc2c0423a --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_graphclust_model4_heavy_Test.txt @@ -0,0 +1,96 @@ +# Copyright (C) 2018 and later: Unicode, Inc. and others. +# License & terms of use: http://www.unicode.org/copyright.html +Model: Thai_graphclust_model4_heavy +Embedding: grapheme_clusters_tf +Input: ปฏิญญาสากลว่าด้วยสิทธิมนุษยชน +Output: |ปฏิญญา|สากลว่า|ด้วย|สิทธิ|มนุ|ษย|ชน| +Input: คำปรารภ +Output: คำปราร|ภ| +Input: โดยที่การยอมรับนับถือเกียรติศักดิ์ประจำตัว +Output: |โดย|ที่|การ|ยอม|รับ|นับถือเกียรติ|ศักดิ์|ประจำ|ตัว| +Input: และสิทธิเท่าเทียมกันและโอนมิได้ของบรรดา +Output: |และ|สิทธิ|เท่า|เทีย|มกัน|และ|โอน|มิได้|ของ|บรรดา| +Input: สมาชิก +Output: |สมาชิก| +Input: ทั้ง +Output: ทั้ง| +Input: หลายแห่งครอบครัว +Output: |หลาย|แห่ง|ครอบ|ครัว| +Input: มนุษย์เป็นหลักมูลเหตุแห่งอิสรภาพ +Output: ม|นุ|ษย์|เป็น|หลักมูล|เหตุแห่งอิ|สรภาพ| +Input: ความยุติธรรม +Output: |ความ|ยุติธรรม| +Input: และสันติภาพในโลก +Output: |และ|สันติภาพ|ใน|โลก| +Input: โดยที่การไม่นำพาและการเหยียดหยามต่อสิทธิมนุษยชน +Output: |โดย|ที่|การ|ไม่|นำ|พา|และ|การ|เหยียด|หยาม|ต่อ|สิทธิ|มนุ|ษย|ชน| +Input: ยังผลให้มีการหระทำอันป่าเถื่อน +Output: |ยังผล|ให้|มี|การ|หระทำ|อัน|ป่า|เถื่อน| +Input: ซี่งเป็นการละเมิดมโนธรรมของมนุษยชาติอย่างร้ายแรง +Output: |ซี่ง|เป็น|การ|ละเมิดม|โนธรรม|ของ|มนุษย|ชาติ|อย่าง|ร้าย|แรง| +Input: และใต้ +Output: |และ|ใต้| +Input: ได้ +Output: |ได้| +Input: มีการประกาศว่า +Output: |มี|การ|ประกาศ|ว่า| +Input: ปณิธานสูงสุดของสามัญชนได้แก่ความต้องการให้มนุษย์มีชีวิตอยู่ในโลกด้วยอิสรภาพในการพูด +Output: |ปณิธา|นสูงสุด|ของ|สามัญชน|ได้|แก่|ความ|ต้อง|การ|ให้|ม|นุษย์|มี|ชีวิต|อยู่|ใน|โลก|ด้วยอิ|สรภาพ|ใน|การ|พูด| +Input: และความเชื่อถือ +Output: |และ|ความ|เชื่อถือ| +Input: และอิสรภาพพ้นจากความหวาดกลัวและความต้องการ +Output: และอิ|สรภาพพ้น|จาก|ความ|หวาดกลัว|และ|ความ|ต้องการ| +Input: โดยที่เป็นการจำเป็นอย่างยิ่งที่สิทธิมนุษยชนควรได้รับความคุ้มครองโดยหลักบังคับของกฎหมาย +Output: |โดย|ที่|เป็น|การ|จำเป็น|อย่าง|ยิ่งที่|สิทธิม|นุ|ษย|ชน|ควร|ได้|รับ|ความ|คุ้มครอง|โดย|หลักบัง|คับ|ของ|กฎหมา|ย| +Input: ถ้าไม่ประสงค์จะให้คนตกอยู่ในบังคับให้หันเข้าหาการขบถขัดขืนต่อทรราชและการกดขี่เป็นวิถีทางสุดท้าย +Output: |ถ้า|ไม่|ประสงค์|จะ|ให้|คน|ตก|อยู่|ใน|บังคับ|ให้|หั|นเข้า|หา|การ|ขบ|ถขัด|ขืน|ต่อ|ทรราช|และ|การ|กดขี่|เป็น|วิ|ถี|ทาง|สุดท้าย| +Input: โดยที่ประชากรแห่งสหประชาชาติได้ยืนยันไว้ในกฎบัตรถึงความเชื่อมั่นในสิทธิมนุษยชนอันเป็นหลักมูล +Output: |โดย|ที่|ประชากร|แห่ง|สหประชาชาติ|ได้|ยืนยัน|ไว้|ใน|กฎบัตร|ถึง|ความ|เชื่อมั่น|ใน|สิทธิ|มนุ|ษย|ชน|อัน|เป็น|หลักมูล| +Input: ในเกียรติศักดิ์และคุณค่าของมนุษย์และในสิทธิเท่าเทียมกันของบรรดาชายและหญิง +Output: |ใน|เกียรติ|ศักดิ์|และ|คุณค่า|ของ|มนุษย์|และ|ใน|สิทธิ|เท่า|เทีย|มกัน|ของ|บรรดา|ชาย|และ|หญิง| +Input: และได้ตกลงใจที่จะส่งเสริมความก้าวหน้าทางสังคม +Output: |และ|ได้|ตกลงใจ|ที่|จะ|ส่ง|เสริม|ความ|ก้าว|หน้าทาง|สังคม| +Input: และมาตรฐานแห่งชีวิตที่ดีขึ้นด้วยในอิสรภาพ +Output: |และ|มาตรฐาน|แห่งชีวิต|ที่|ดี|ขึ้น|ด้วย|ในอิ|สรภาพ| +Input: อันกว้างขวางยิ่งขึ้น +Output: |อัน|กว้าง|ขวาง|ยิ่ง|ขึ้น| +Input: โดยที่รัฐสมาชิกต่างปฎิญาณจะให้บรรลุถึงซึ่งการส่งเสริมการเคารพและการปฎิบัติตามทั่วสากลต่อสิทธิมนุษยชนและอิสรภาพหลักมูล +Output: |โดย|ที่|รัฐส|มา|ชิก|ต่าง|ปฎิญาณ|จะ|ให้|บรรลุ|ถึง|ซึ่ง|การ|ส่ง|เสริม|การ|เคา|รพ|และ|การ|ปฎิบัติ|ตา|มทั่วสาก|ล|ต่อ|สิทธิม|นุ|ษย|ชนและอิ|สรภาพ|หลักมูล| +Input: โดยร่วมมือกับสหประชาชาติ +Output: |โดย|ร่วมมือ|กับ|สหประชาชาติ| +Input: โดยที่ความเข้าใจร่วมกันในสิทธิ +Output: |โดย|ที่|ความ|เข้าใจ|ร่วม|กัน|ใน|สิทธิ| +Input: และอิสรภาพเหล่านี้เป็นสิ่งสำคัญอย่างยิ่ง +Output: และอิ|สรภาพ|เหล่า|นี้|เป็น|สิ่ง|สำคัญ|อย่าง|ยิ่ง| +Input: เพื่อให้ปฏิญาณนี้สำเร็จผลเต็มบริบูรณ์ +Output: |เพื่อ|ให้|ปฏิญาณ|นี้|สำเร็จผล|เต็ม|บริบูรณ์| +Input: ฉะนั้น +Output: ฉะนั้น| +Input: บัดนี้สมัชชาจึงประกาศว่า +Output: |บัด|นี้|สมัชชา|จึง|ประกาศ|ว่า| +Input: ปฏิญญาสากลว่าด้วยสิทธิมนุษยชนนี้ +Output: |ปฏิญญา|สากลว่า|ด้วย|สิทธิ|มนุ|ษย|ชน|นี้| +Input: เป็นมาตรฐานร่วมกันแห่งความสำเร็จสำหรับบรรดาประชากรและประชาชาติทั้งหลาย +Output: |เป็น|มาตรฐาน|ร่วม|กัน|แห่ง|ความ|สำเร็จ|สำหรับ|บรรดา|ประชากร|และ|ประชาชาติ|ทั้งหลา|ย| +Input: เพื่อจุดหมายปลายทางที่ว่า +Output: |เพื่อจุดหมาย|ปลาย|ทาง|ที่|ว่า| +Input: เอกชนทุกคนและองค์การชองสังคมทุกองค์การ +Output: |เอกชน|ทุก|คน|และ|องค์การ|ชอง|สังคม|ทุกองค์การ| +Input: โดยการรำลึกถึงปฏิญญานี้เป็นเนืองนิจ +Output: |โดย|การ|รำลึก|ถึง|ปฏิญญานี้|เป็น|เนือง|นิ|จ| +Input: จะบากบั่นพยายามด้วยการสอนและศึกษา +Output: |จะ|บาก|บั่น|พยายาม|ด้วย|การ|สอน|และ|ศึก|ษา| +Input: ในอันที่จะส่งเสริมการเคารพสิทธิและอิสรภาพเหล่านี้ +Output: |ใน|อัน|ที่|จะ|ส่ง|เสริม|การ|เคารพ|สิทธิ|และอิ|สรภาพ|เหล่า|นี้| +Input: และด้วยมาตรการอันก้าวหน้าทั้งในประเทศและระหว่างประเทศ +Output: |และ|ด้วย|มาตรการ|อัน|ก้าว|หน้าทั้ง|ใน|ประเทศ|และ|ระหว่าง|ประเทศ| +Input: ในอันที่จะให้มีการยอมรับนับถือ +Output: |ใน|อัน|ที่|จะ|ให้|มี|การ|ยอม|รับ|นับถือ| +Input: และการปฏิบัติตามโดยสากลและอย่างเป็นผลจริงจัง +Output: |และ|การ|ปฏิบัติตาม|โดย|สากล|และ|อย่าง|เป็นผล|จริง|จัง| +Input: ทั้งในบรรดาประชาชนของรัฐสมาชิกด้วยกันเอง +Output: ทั้ง|ใน|บรรดา|ประชาชน|ของ|รัฐส|มาชิก|ด้วย|กัน|เอง| +Input: และในบรรดาประชาชนของดินแดนที่อยู่ใตัอำนาจของรัฐนั้น +Output: |และ|ใน|บรรดา|ประชาชน|ของ|ดินแดน|ที่|อยู่|ใตัอำนาจ|ของ|รัฐนั้น| +Input: ๆ +Output: |ๆ| diff --git a/icu4j/main/tests/framework/src/com/ibm/icu/dev/test/TestUtil.java b/icu4j/main/tests/framework/src/com/ibm/icu/dev/test/TestUtil.java index 354723cdf4f..d09dacbf0ef 100644 --- a/icu4j/main/tests/framework/src/com/ibm/icu/dev/test/TestUtil.java +++ b/icu4j/main/tests/framework/src/com/ibm/icu/dev/test/TestUtil.java @@ -15,6 +15,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Locale; +import java.util.MissingResourceException; + +import com.ibm.icu.impl.breakiter.LSTMBreakEngine; +import com.ibm.icu.lang.UScript; public final class TestUtil { /** @@ -279,4 +283,28 @@ public final class TestUtil { } return ver; } + + private static boolean lstmDataIsBuilt() { + try { + LSTMBreakEngine.createData(UScript.THAI); + return true; + } catch (MissingResourceException e) { + // do nothing + } + try { + LSTMBreakEngine.createData(UScript.MYANMAR); + return true; + } catch (MissingResourceException e) { + // do nothing + } + return false; + } + + public static boolean skipLSTMTest() { + return ! lstmDataIsBuilt(); + } + + public static boolean skipDictionaryTest() { + return lstmDataIsBuilt(); + } } diff --git a/icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/TransliteratorTest.java b/icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/TransliteratorTest.java index 0808f758e38..140539829e5 100644 --- a/icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/TransliteratorTest.java +++ b/icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/TransliteratorTest.java @@ -3603,6 +3603,9 @@ the ::BEGIN/::END stuff) */ @Test public void TestThai() { + // The expectations in this test heavily depends on the Thai dictionary. + // Therefore, we skip this test under the LSTM configuration. + org.junit.Assume.assumeTrue(!TestUtil.skipDictionaryTest()); Transliterator tr = Transliterator.getInstance("Any-Latin", Transliterator.FORWARD); String thaiText = "\u0e42\u0e14\u0e22\u0e1e\u0e37\u0e49\u0e19\u0e10\u0e32\u0e19\u0e41\u0e25\u0e49\u0e27, \u0e04\u0e2d" +