This is an automated email from the ASF dual-hosted git repository.

zhangzp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-ml.git


The following commit(s) were added to refs/heads/master by this push:
     new 6825291  [FLINK-29044] Add Transformer for DCT
6825291 is described below

commit 6825291e0a808474c89a0bf65ed43f0cf1034e79
Author: yunfengzhou-hub <[email protected]>
AuthorDate: Wed Aug 24 21:15:51 2022 +0800

    [FLINK-29044] Add Transformer for DCT
    
    This closes #145.
---
 .../flink/ml/examples/feature/DCTExample.java      |  61 +++++++
 flink-ml-lib/pom.xml                               |  27 +++
 .../java/org/apache/flink/ml/feature/dct/DCT.java  | 139 +++++++++++++++
 .../org/apache/flink/ml/feature/dct/DCTParams.java |  45 +++++
 .../java/org/apache/flink/ml/feature/DCTTest.java  | 187 +++++++++++++++++++++
 .../pyflink/examples/ml/feature/dct_example.py     |  61 +++++++
 flink-ml-python/pyflink/ml/lib/feature/dct.py      |  74 ++++++++
 .../pyflink/ml/lib/feature/tests/test_dct.py       |  89 ++++++++++
 flink-ml-uber/src/main/resources/META-INF/NOTICE   |   6 +
 .../META-INF/licenses/LICENSE.JLargeArrays         |  23 +++
 .../META-INF/licenses/LICENSE.JTransforms          |  23 +++
 11 files changed, 735 insertions(+)

diff --git 
a/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/feature/DCTExample.java
 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/feature/DCTExample.java
new file mode 100644
index 0000000..2b3d683
--- /dev/null
+++ 
b/flink-ml-examples/src/main/java/org/apache/flink/ml/examples/feature/DCTExample.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.ml.examples.feature;
+
+import org.apache.flink.ml.feature.dct.DCT;
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CloseableIterator;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Simple program that creates a DCT instance and uses it for feature 
engineering. */
+public class DCTExample {
+    public static void main(String[] args) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
+
+        // Generates input data.
+        List<Vector> inputData =
+                Arrays.asList(
+                        Vectors.dense(1.0, 1.0, 1.0, 1.0), Vectors.dense(1.0, 
0.0, -1.0, 0.0));
+        Table inputTable = 
tEnv.fromDataStream(env.fromCollection(inputData)).as("input");
+
+        // Creates a DCT object and initializes its parameters.
+        DCT dct = new DCT();
+
+        // Uses the DCT object for feature transformations.
+        Table outputTable = dct.transform(inputTable)[0];
+
+        // Extracts and displays the results.
+        for (CloseableIterator<Row> it = outputTable.execute().collect(); 
it.hasNext(); ) {
+            Row row = it.next();
+
+            Vector inputValue = row.getFieldAs(dct.getInputCol());
+            Vector outputValue = row.getFieldAs(dct.getOutputCol());
+
+            System.out.printf("Input Value: %s\tOutput Value: %s\n", 
inputValue, outputValue);
+        }
+    }
+}
diff --git a/flink-ml-lib/pom.xml b/flink-ml-lib/pom.xml
index 9a0fc0b..42eff45 100644
--- a/flink-ml-lib/pom.xml
+++ b/flink-ml-lib/pom.xml
@@ -58,6 +58,12 @@ under the License.
       <scope>provided</scope>
     </dependency>
 
+    <dependency>
+      <groupId>com.github.wendykierp</groupId>
+      <artifactId>JTransforms</artifactId>
+      <version>3.1</version>
+    </dependency>
+
     <dependency>
       <groupId>org.apache.flink</groupId>
       <artifactId>flink-table-planner-loader</artifactId>
@@ -101,6 +107,27 @@ under the License.
 
   <build>
     <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>shade-flink</id>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <artifactSet>
+                <includes combine.children="append">
+                  <include>com.github.wendykierp:JTransforms</include>
+                  <include>pl.edu.icm:JLargeArrays</include>
+                </includes>
+              </artifactSet>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
diff --git 
a/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCT.java 
b/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCT.java
new file mode 100644
index 0000000..c343cd7
--- /dev/null
+++ b/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCT.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.ml.feature.dct;
+
+import org.apache.flink.api.common.functions.MapFunction;
+import org.apache.flink.api.java.typeutils.RowTypeInfo;
+import org.apache.flink.ml.api.Transformer;
+import org.apache.flink.ml.common.datastream.TableUtils;
+import org.apache.flink.ml.linalg.DenseVector;
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.linalg.typeinfo.DenseVectorTypeInfo;
+import org.apache.flink.ml.param.Param;
+import org.apache.flink.ml.util.ParamUtils;
+import org.apache.flink.ml.util.ReadWriteUtils;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.table.api.internal.TableImpl;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.Preconditions;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.jtransforms.dct.DoubleDCT_1D;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * A Transformer that takes the 1D discrete cosine transform of a real vector. 
No zero padding is
+ * performed on the input vector. It returns a real vector of the same length 
representing the DCT.
+ * The return vector is scaled such that the transform matrix is unitary (aka 
scaled DCT-II).
+ *
+ * <p>See 
https://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II(DCT-II in 
Discrete cosine
+ * transform).
+ */
+public class DCT implements Transformer<DCT>, DCTParams<DCT> {
+    private final Map<Param<?>, Object> paramMap = new HashMap<>();
+
+    public DCT() {
+        ParamUtils.initializeMapWithDefaultValues(paramMap, this);
+    }
+
+    @Override
+    public Table[] transform(Table... inputs) {
+        Preconditions.checkArgument(inputs.length == 1);
+
+        StreamTableEnvironment tEnv =
+                (StreamTableEnvironment) ((TableImpl) 
inputs[0]).getTableEnvironment();
+
+        RowTypeInfo inputTypeInfo = 
TableUtils.getRowTypeInfo(inputs[0].getResolvedSchema());
+        RowTypeInfo outputTypeInfo =
+                new RowTypeInfo(
+                        ArrayUtils.addAll(
+                                inputTypeInfo.getFieldTypes(), 
DenseVectorTypeInfo.INSTANCE),
+                        ArrayUtils.addAll(inputTypeInfo.getFieldNames(), 
getOutputCol()));
+
+        DataStream<Row> stream =
+                tEnv.toDataStream(inputs[0])
+                        .map(new DCTFunction(getInputCol(), getInverse()), 
outputTypeInfo);
+
+        return new Table[] {tEnv.fromDataStream(stream)};
+    }
+
+    /**
+     * A {@link MapFunction} that contains the main logic to perform discrete 
cosine transformation.
+     */
+    private static class DCTFunction implements MapFunction<Row, Row> {
+        private final String inputCol;
+
+        private final boolean isInverse;
+
+        private BiConsumer<double[], Boolean> dctTransformer;
+
+        private long previousVectorSize;
+
+        private DCTFunction(String inputCol, boolean isInverse) {
+            this.inputCol = inputCol;
+            this.isInverse = isInverse;
+            this.dctTransformer = null;
+            this.previousVectorSize = -1;
+        }
+
+        @Override
+        public Row map(Row row) throws Exception {
+            Vector vector = row.getFieldAs(inputCol);
+
+            if (previousVectorSize != vector.size()) {
+                if (isInverse) {
+                    dctTransformer = new DoubleDCT_1D(vector.size())::inverse;
+                } else {
+                    dctTransformer = new DoubleDCT_1D(vector.size())::forward;
+                }
+                previousVectorSize = vector.size();
+            }
+
+            double[] array = vector.toArray();
+            if (vector instanceof DenseVector) {
+                array = array.clone();
+            }
+
+            dctTransformer.accept(array, true);
+
+            return Row.join(row, Row.of(Vectors.dense(array)));
+        }
+    }
+
+    @Override
+    public void save(String path) throws IOException {
+        ReadWriteUtils.saveMetadata(this, path);
+    }
+
+    public static DCT load(StreamTableEnvironment tEnv, String path) throws 
IOException {
+        return ReadWriteUtils.loadStageParam(path);
+    }
+
+    @Override
+    public Map<Param<?>, Object> getParamMap() {
+        return paramMap;
+    }
+}
diff --git 
a/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCTParams.java 
b/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCTParams.java
new file mode 100644
index 0000000..4410883
--- /dev/null
+++ b/flink-ml-lib/src/main/java/org/apache/flink/ml/feature/dct/DCTParams.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.ml.feature.dct;
+
+import org.apache.flink.ml.common.param.HasInputCol;
+import org.apache.flink.ml.common.param.HasOutputCol;
+import org.apache.flink.ml.param.BooleanParam;
+import org.apache.flink.ml.param.Param;
+
+/**
+ * Params for {@link DCT}.
+ *
+ * @param <T> The class type of this instance.
+ */
+public interface DCTParams<T> extends HasInputCol<T>, HasOutputCol<T> {
+    Param<Boolean> INVERSE =
+            new BooleanParam(
+                    "inverse",
+                    "Whether to perform the inverse DCT (true) or forward DCT 
(false).",
+                    false);
+
+    default boolean getInverse() {
+        return get(INVERSE);
+    }
+
+    default T setInverse(boolean value) {
+        return set(INVERSE, value);
+    }
+}
diff --git 
a/flink-ml-lib/src/test/java/org/apache/flink/ml/feature/DCTTest.java 
b/flink-ml-lib/src/test/java/org/apache/flink/ml/feature/DCTTest.java
new file mode 100644
index 0000000..e2146e2
--- /dev/null
+++ b/flink-ml-lib/src/test/java/org/apache/flink/ml/feature/DCTTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.ml.feature;
+
+import org.apache.flink.api.common.restartstrategy.RestartStrategies;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.ml.feature.dct.DCT;
+import org.apache.flink.ml.linalg.SparseVector;
+import org.apache.flink.ml.linalg.Vector;
+import org.apache.flink.ml.linalg.Vectors;
+import org.apache.flink.ml.util.TestUtils;
+import 
org.apache.flink.streaming.api.environment.ExecutionCheckpointingOptions;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.table.api.Table;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.apache.flink.test.util.AbstractTestBase;
+import org.apache.flink.types.Row;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/** Tests the {@link DCT}. */
+public class DCTTest extends AbstractTestBase {
+    private StreamExecutionEnvironment env;
+    private StreamTableEnvironment tEnv;
+
+    private static final List<Vector> inputData =
+            Arrays.asList(Vectors.dense(1.0, 1.0, 1.0, 1.0), 
Vectors.dense(1.0, 0.0, -1.0, 0.0));
+
+    private static final List<Row> expectedForwardOutputData =
+            Arrays.asList(
+                    Row.of(Vectors.dense(1.0, 1.0, 1.0, 1.0), 
Vectors.dense(2.0, 0.0, 0.0, 0.0)),
+                    Row.of(
+                            Vectors.dense(1.0, 0.0, -1.0, 0.0),
+                            Vectors.dense(0.0, 0.924, 1.0, -0.383)));
+
+    private static final List<Row> expectedInverseOutputData =
+            Arrays.asList(
+                    Row.of(
+                            Vectors.dense(1.0, 1.0, 1.0, 1.0),
+                            Vectors.dense(1.924, -0.383, 0.383, 0.076)),
+                    Row.of(Vectors.dense(1.0, 0.0, -1.0, 0.0), 
Vectors.dense(0.0, 1.0, 1.0, 0.0)));
+
+    private Table inputTable;
+
+    @Before
+    public void before() {
+        Configuration config = new Configuration();
+        
config.set(ExecutionCheckpointingOptions.ENABLE_CHECKPOINTS_AFTER_TASKS_FINISH, 
true);
+        env = StreamExecutionEnvironment.getExecutionEnvironment(config);
+        env.setParallelism(4);
+        env.enableCheckpointing(100);
+        env.setRestartStrategy(RestartStrategies.noRestart());
+        tEnv = StreamTableEnvironment.create(env);
+        inputTable = 
tEnv.fromDataStream(env.fromCollection(inputData)).as("input");
+    }
+
+    @Test
+    public void testParam() {
+        DCT dct = new DCT();
+
+        assertEquals("input", dct.getInputCol());
+        assertEquals("output", dct.getOutputCol());
+        assertFalse(dct.getInverse());
+
+        
dct.setInputCol("test_input").setOutputCol("test_output").setInverse(true);
+
+        assertEquals("test_input", dct.getInputCol());
+        assertEquals("test_output", dct.getOutputCol());
+        assertTrue(dct.getInverse());
+    }
+
+    @Test
+    public void testOutputSchema() {
+        Table inputTable =
+                
tEnv.fromDataStream(env.fromElements(Row.of(Vectors.dense(0.0), "")))
+                        .as("test_input", "dummy_input");
+
+        DCT dct = new 
DCT().setInputCol("test_input").setOutputCol("test_output");
+
+        Table outputTable = dct.transform(inputTable)[0];
+
+        assertEquals(
+                Arrays.asList(dct.getInputCol(), "dummy_input", 
dct.getOutputCol()),
+                outputTable.getResolvedSchema().getColumnNames());
+    }
+
+    @Test
+    public void testTransformForward() {
+        DCT dct = new DCT();
+        Table outputTable = dct.transform(inputTable)[0];
+
+        verifyTransformResult(
+                outputTable, expectedForwardOutputData, dct.getInputCol(), 
dct.getOutputCol());
+    }
+
+    @Test
+    public void testTransformInverse() {
+        DCT dct = new DCT().setInverse(true);
+        Table outputTable = dct.transform(inputTable)[0];
+
+        verifyTransformResult(
+                outputTable, expectedInverseOutputData, dct.getInputCol(), 
dct.getOutputCol());
+    }
+
+    @Test
+    public void testInputTypeConversion() throws Exception {
+        inputTable = TestUtils.convertDataTypesToSparseInt(tEnv, inputTable);
+        assertArrayEquals(
+                new Class<?>[] {SparseVector.class}, 
TestUtils.getColumnDataTypes(inputTable));
+
+        DCT dct = new DCT();
+        Table outputTable = dct.transform(inputTable)[0];
+
+        verifyTransformResult(
+                outputTable, expectedForwardOutputData, dct.getInputCol(), 
dct.getOutputCol());
+    }
+
+    @Test
+    public void testSaveLoadAndTransform() throws Exception {
+        DCT dct = new DCT().setInverse(true);
+
+        DCT loadedDCT =
+                TestUtils.saveAndReload(tEnv, dct, 
TEMPORARY_FOLDER.newFolder().getAbsolutePath());
+
+        Table outputTable = loadedDCT.transform(inputTable)[0];
+
+        verifyTransformResult(
+                outputTable,
+                expectedInverseOutputData,
+                loadedDCT.getInputCol(),
+                loadedDCT.getOutputCol());
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void verifyTransformResult(
+            Table outputTable, List<Row> expectedOutputData, String inputCol, 
String outputCol) {
+        List<Row> actualOutputData = 
IteratorUtils.toList(outputTable.execute().collect());
+        actualOutputData.sort(
+                Comparator.comparingLong(
+                        x ->
+                                ((Vector) 
Objects.requireNonNull(x.getField(inputCol)))
+                                        .toDense()
+                                        .hashCode()));
+
+        expectedOutputData.sort(
+                Comparator.comparingLong(
+                        x ->
+                                ((Vector) 
Objects.requireNonNull(x.getField(0)))
+                                        .toDense()
+                                        .hashCode()));
+
+        assertEquals(actualOutputData.size(), expectedOutputData.size());
+        for (int i = 0; i < actualOutputData.size(); i++) {
+            Vector actualVector = 
actualOutputData.get(i).getFieldAs(outputCol);
+            Vector expectedVector = expectedOutputData.get(i).getFieldAs(1);
+            assertArrayEquals(expectedVector.toArray(), 
actualVector.toArray(), 1e-3);
+        }
+    }
+}
diff --git a/flink-ml-python/pyflink/examples/ml/feature/dct_example.py 
b/flink-ml-python/pyflink/examples/ml/feature/dct_example.py
new file mode 100644
index 0000000..1ecff6d
--- /dev/null
+++ b/flink-ml-python/pyflink/examples/ml/feature/dct_example.py
@@ -0,0 +1,61 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+# Simple program that creates a Bucketizer instance and uses it for feature
+# engineering.
+#
+# Before executing this program, please make sure you have followed Flink ML's
+# quick start guideline to set up Flink ML and Flink environment. The guideline
+# can be found at
+#
+# 
https://nightlies.apache.org/flink/flink-ml-docs-master/docs/try-flink-ml/quick-start/
+
+from pyflink.common import Types
+from pyflink.datastream import StreamExecutionEnvironment
+from pyflink.ml.core.linalg import Vectors, DenseVectorTypeInfo
+from pyflink.ml.lib.feature.dct import DCT
+from pyflink.table import StreamTableEnvironment
+
+# create a new StreamExecutionEnvironment
+env = StreamExecutionEnvironment.get_execution_environment()
+
+# create a StreamTableEnvironment
+t_env = StreamTableEnvironment.create(env)
+
+# generate input data
+input_data = t_env.from_data_stream(
+    env.from_collection([
+        (Vectors.dense(1.0, 1.0, 1.0, 1.0),),
+        (Vectors.dense(1.0, 0.0, -1.0, 0.0),),
+    ],
+        type_info=Types.ROW_NAMED(
+            ['input'],
+            [DenseVectorTypeInfo()])))
+
+# create a DCT object and initialize its parameters
+dct = DCT()
+
+# use the dct for feature engineering
+output = dct.transform(input_data)[0]
+
+# extract and display the results
+field_names = output.get_schema().get_field_names()
+for result in t_env.to_data_stream(output).execute_and_collect():
+    input_value = result[field_names.index(dct.get_input_col())]
+    output_value = result[field_names.index(dct.get_output_col())]
+    print('Input Value: ' + str(input_value) + '\tOutput Value: ' + 
str(output_value))
diff --git a/flink-ml-python/pyflink/ml/lib/feature/dct.py 
b/flink-ml-python/pyflink/ml/lib/feature/dct.py
new file mode 100644
index 0000000..ce431fd
--- /dev/null
+++ b/flink-ml-python/pyflink/ml/lib/feature/dct.py
@@ -0,0 +1,74 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+import typing
+
+from pyflink.ml.core.param import Param, BooleanParam
+from pyflink.ml.core.wrapper import JavaWithParams
+from pyflink.ml.lib.feature.common import JavaFeatureTransformer
+from pyflink.ml.lib.param import HasInputCol, HasOutputCol
+
+
+class _DCTParams(
+    JavaWithParams,
+    HasInputCol,
+    HasOutputCol
+):
+    """
+    Params for :class:`DCT`.
+    """
+
+    INVERSE: Param[bool] = BooleanParam(
+        "inverse",
+        "Whether to perform the inverse DCT (true) or forward DCT (false).",
+        False)
+
+    def __init__(self, java_params):
+        super(_DCTParams, self).__init__(java_params)
+
+    def set_inverse(self, value: bool):
+        return typing.cast(_DCTParams, self.set(self.INVERSE, value))
+
+    def get_inverse(self) -> bool:
+        return self.get(self.INVERSE)
+
+    @property
+    def inverse(self):
+        return self.get_inverse()
+
+
+class DCT(JavaFeatureTransformer, _DCTParams):
+    """
+    A Transformer that takes the 1D discrete cosine transform of a real vector.
+    No zero padding is performed on the input vector. It returns a real vector
+    of the same length representing the DCT. The return vector is scaled such
+    that the transform matrix is unitary (aka scaled DCT-II).
+
+    See https://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II (DCT-II
+    in Discrete cosine transform).
+    """
+
+    def __init__(self, java_model=None):
+        super(DCT, self).__init__(java_model)
+
+    @classmethod
+    def _java_transformer_package_name(cls) -> str:
+        return "dct"
+
+    @classmethod
+    def _java_transformer_class_name(cls) -> str:
+        return "DCT"
diff --git a/flink-ml-python/pyflink/ml/lib/feature/tests/test_dct.py 
b/flink-ml-python/pyflink/ml/lib/feature/tests/test_dct.py
new file mode 100644
index 0000000..08c2b0c
--- /dev/null
+++ b/flink-ml-python/pyflink/ml/lib/feature/tests/test_dct.py
@@ -0,0 +1,89 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+import os
+from typing import List
+
+from pyflink.common import Row, Types
+from pyflink.ml.core.linalg import Vectors, DenseVectorTypeInfo
+from pyflink.ml.lib.feature.dct import DCT
+from pyflink.ml.tests.test_utils import PyFlinkMLTestCase
+from pyflink.table import Table
+
+
+class DCTTest(PyFlinkMLTestCase):
+    def setUp(self):
+        super(DCTTest, self).setUp()
+        self.input = self.t_env.from_data_stream(
+            self.env.from_collection([
+                (Vectors.dense(1.0, 1.0, 1.0, 1.0),),
+                (Vectors.dense(1.0, 0.0, -1.0, 0.0),),
+            ],
+                type_info=Types.ROW_NAMED(
+                    ['input'],
+                    [DenseVectorTypeInfo()])))
+
+        self.expected_output = [
+            Row(Vectors.dense(1.0, 1.0, 1.0, 1.0), Vectors.dense(2.0, 0.0, 
0.0, 0.0)),
+            Row(Vectors.dense(1.0, 0.0, -1.0, 0.0), Vectors.dense(0.0, 0.924, 
1.0, -0.383))]
+
+    def test_param(self):
+        dct = DCT()
+
+        self.assertEqual('input', dct.get_input_col())
+        self.assertEqual('output', dct.get_output_col())
+        self.assertFalse(dct.get_inverse())
+
+        dct.set_input_col('test_input') \
+            .set_output_col('test_output') \
+            .set_inverse(True)
+
+        self.assertEqual('test_input', dct.get_input_col())
+        self.assertEqual('test_output', dct.get_output_col())
+        self.assertTrue(dct.get_inverse())
+
+    def test_output_schema(self):
+        temp_table = self.input.alias('test_input')
+        dct = DCT().set_input_col('test_input').set_output_col('test_output')
+        output = dct.transform(temp_table)[0]
+        self.assertEqual(['test_input', 'test_output'], 
output.get_schema().get_field_names())
+
+    def test_transform(self):
+        dct = DCT()
+        output = dct.transform(self.input)[0]
+        self.verify_output_result(output, self.expected_output)
+
+    def test_save_load_transform(self):
+        dct = DCT()
+        path = os.path.join(self.temp_dir, 'test_save_load_transform_dct')
+        dct.save(path)
+        dct = DCT.load(self.t_env, path)
+        output = dct.transform(self.input)[0]
+        self.verify_output_result(output, self.expected_output)
+
+    def verify_output_result(
+            self,
+            output: Table,
+            expected_result: List[Row]):
+        actual_result = [result for result in
+                         
self.t_env.to_data_stream(output).execute_and_collect()]
+        actual_result.sort(key=lambda x: hash(x[0]))
+        expected_result.sort(key=lambda x: hash(x[0]))
+
+        for item1, item2 in zip(actual_result, expected_result):
+            for i, j in zip(item1[1]._values, item2[1]._values):
+                self.assertAlmostEqual(i, j, delta=1e-3)
diff --git a/flink-ml-uber/src/main/resources/META-INF/NOTICE 
b/flink-ml-uber/src/main/resources/META-INF/NOTICE
index ee0576d..2a47205 100644
--- a/flink-ml-uber/src/main/resources/META-INF/NOTICE
+++ b/flink-ml-uber/src/main/resources/META-INF/NOTICE
@@ -8,3 +8,9 @@ This project bundles the following dependencies under the MIT 
license.
 See bundled license files for details.
 
 - dev.ludovic.netlib:blas:2.2.0
+
+This project bundles the following dependencies under the BSD license.
+See bundled license files for details.
+
+- com.github.wendykierp:JTransforms:3.1
+- pl.edu.icm:JLargeArrays:1.5
diff --git 
a/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JLargeArrays 
b/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JLargeArrays
new file mode 100644
index 0000000..a0bae8f
--- /dev/null
+++ b/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JLargeArrays
@@ -0,0 +1,23 @@
+JLargeArrays
+Copyright (C) 2013 onward University of Warsaw, ICM
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git 
a/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JTransforms 
b/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JTransforms
new file mode 100644
index 0000000..a03bcb7
--- /dev/null
+++ b/flink-ml-uber/src/main/resources/META-INF/licenses/LICENSE.JTransforms
@@ -0,0 +1,23 @@
+JTransforms
+Copyright (c) 2007 onward, Piotr Wendykier
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Reply via email to