ICU-21569 LSTM Part 3 Add Java implementation

See #1706
This commit is contained in:
Frank Yung-Fong Tang 2021-05-08 22:02:03 +00:00
parent 17c7563078
commit 2a72af07ac
16 changed files with 968 additions and 15 deletions

View 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",

View 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.

View 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"}
}
}

View file

@ -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);
}
}

View 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();

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8f02ab2967eaf73b6d28c8340d70b20d5f194f6c0ac24fe8464b25fd56763b04
size 13383786
oid sha256:6614997945a564e6888da79d06283f519a34d9e13caf8b10eba68ee9f098efda
size 13383850

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:26a032e0c9492cd986546eefb5ba54687598eb431caed531ccb00b12469421ca
size 726547
oid sha256:9184fdb3a90361165d9c90081a681450a6a049beaf669a24580dcf23134142c7
size 825810

View file

@ -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: |မျိုး|ရိုး|ဂုဏ်|သိက္ခာ|နှင့်|တ|ကွ|

View file

@ -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);
}
}
}
}

View file

@ -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 = "";
}
}
}
}

View 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 };

View 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();

View file

@ -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: |ๆ|

View file

@ -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: |ๆ|

View 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();
}
}

View 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" +