]> granicus.if.org Git - icu/commitdiff
ICU-21569 LSTM Part 3 Add Java implementation
authorFrank Yung-Fong Tang <ftang@google.com>
Sat, 8 May 2021 22:02:03 +0000 (22:02 +0000)
committerFrank Yung-Fong Tang <ftang@google.com>
Sun, 9 May 2021 04:15:44 +0000 (21:15 -0700)
See #1706

16 files changed:
.github/lstm_for_th_my.json
.github/workflows/icu_ci.yml
icu4c/source/data/brkitr/root.txt
icu4j/main/classes/core/src/com/ibm/icu/impl/breakiter/LSTMBreakEngine.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedBreakIterator.java
icu4j/main/shared/data/icudata.jar
icu4j/main/shared/data/testdata.jar
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Burmese_graphclust_model5_heavy_Test.txt [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/LSTMBreakEngineTest.java [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBILSTMTest.java [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/RBBITestExtended.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_codepoints_exclusive_model5_heavy_Test.txt [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/rbbi/Thai_graphclust_model4_heavy_Test.txt [new file with mode: 0644]
icu4j/main/tests/framework/src/com/ibm/icu/dev/test/TestUtil.java
icu4j/main/tests/translit/src/com/ibm/icu/dev/test/translit/TransliteratorTest.java

index 75d2f4ede5363e82b8b5304fc726cac68c424054..b4aeb24d0ed883dd7f0ee5770ce176f4c9fa319a 100644 (file)
@@ -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",
index 17e0daa292a2b205227b52fad67f6faf27b01ce5..967f1460f2cb4e4835cf68364e5afdb6bed79a8c 100644 (file)
@@ -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.
index 052d736b0a767c1f22f0c72d220a10accbbc2c7d..114c5e780a0f6b2243c50e64f4d924b22861c1b7 100644 (file)
@@ -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 (file)
index 0000000..28bcc60
--- /dev/null
@@ -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<String, Integer>(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<String, Integer> 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<String, Integer> dict) {
+            this.fDict = dict;
+        }
+        abstract public void vectorize(CharacterIterator fIter, int rangeStart, int rangeEnd,
+                              List<Integer> offsets, List<Integer> indicies);
+        protected int getIndex(String token) {
+            return fDict.getOrDefault(token, fDict.size());
+        }
+        private Map<String, Integer> fDict;
+    }
+
+    class CodePointsVectorizer extends Vectorizer {
+        public CodePointsVectorizer(Map<String, Integer> dict) {
+            super(dict);
+        }
+
+        public void vectorize(CharacterIterator fIter, int rangeStart, int rangeEnd,
+                              List<Integer> offsets, List<Integer> 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<String, Integer> 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<Integer> offsets, List<Integer> 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<Integer> offsets = new ArrayList<Integer>(rangeEnd - rangeStart);
+        List<Integer> indicies = new ArrayList<Integer>(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);
+    }
+}
index e2d628bba245495fbb892c0e038ed535dc8ff30e..6bf2a26413606c0c43a419f9f042c4333a89ad58 100644 (file)
@@ -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();
index 31762473f6d75c223c1551c9754ec6af4c61713e..c7d3d129e98eb34c6e05240159053207a711361c 100644 (file)
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:8f02ab2967eaf73b6d28c8340d70b20d5f194f6c0ac24fe8464b25fd56763b04
-size 13383786
+oid sha256:6614997945a564e6888da79d06283f519a34d9e13caf8b10eba68ee9f098efda
+size 13383850
index 783e69a66c7e8df3d0c811bc3f9ac708899c052b..64da45ae4b07333ae42a753e5017a2db3e0a763f 100644 (file)
@@ -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 (file)
index 0000000..a559cd7
--- /dev/null
@@ -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 (file)
index 0000000..0208e68
--- /dev/null
@@ -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<String> lines = (new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))).lines();
+        Iterator<String> 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 (file)
index 0000000..06eb2f4
--- /dev/null
@@ -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<String> lines = (new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))).lines();
+        Iterator<String> 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 = "";
+            }
+        }
+    }
+}
index dae29ad07856b5a3d909751bedfe422964500bb6..6f5c2201e2cacb2cccceb83018073a5d114dba3b 100644 (file)
@@ -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 };
index c809da8e143341940130259292a170912551a4d7..76c76b026806e54765459a38d55a4fa0ea37b8be 100644 (file)
@@ -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 (file)
index 0000000..d47ceea
--- /dev/null
@@ -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 (file)
index 0000000..e8fc2c0
--- /dev/null
@@ -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:        |ๆ|
index 354723cdf4fc76c9f03250a8b963e490e6144256..d09dacbf0ef75401219aac4c1d903e930b974bde 100644 (file)
@@ -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();
+    }
 }
index 0808f758e38477ae264d2c9588c0492a32153248..140539829e503526450ef8762b961348618b68e6 100644 (file)
@@ -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" +