| // Copyright 2016 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| import 'package:dlog/dlog.dart' show Table; |
| |
| import '../mobile/device.dart' show Device; |
| import '../mobile/device_spec.dart' show DeviceSpec; |
| import '../util.dart'; |
| |
| class ClusterInfo { |
| Map<String, List<Device>> _deviceClusters; |
| Map<String, List<DeviceSpec>> _deviceSpecClusters; |
| List<String> _deviceClustersOrder; |
| List<String> _deviceSpecClustersOrder; |
| |
| ClusterInfo( |
| Map<String, List<Device>> deviceClusters, |
| Map<String, List<DeviceSpec>> deviceSpecClusters |
| ) { |
| _deviceClusters = deviceClusters; |
| _deviceSpecClusters = deviceSpecClusters; |
| _deviceClustersOrder = new List.from(_deviceClusters.keys); |
| _deviceSpecClustersOrder = new List.from(_deviceSpecClusters.keys); |
| } |
| |
| Map<String, List<Device>> get deviceClusters => _deviceClusters; |
| Map<String, List<DeviceSpec>> get deviceSpecClusters => _deviceSpecClusters; |
| List<String> get deviceClustersOrder => _deviceClustersOrder; |
| List<String> get deviceSpecClustersOrder => _deviceSpecClustersOrder; |
| } |
| |
| class CoverageMatrix { |
| |
| CoverageMatrix(this.clusterInfo) { |
| this.matrix = new List<List<int>>(clusterInfo.deviceSpecClusters.length); |
| for (int i = 0; i < matrix.length; i++) { |
| matrix[i] = new List<int>.filled(clusterInfo.deviceClusters.length, 0); |
| } |
| } |
| |
| ClusterInfo clusterInfo; |
| // Coverage matrix, where a row indicats an app cluster and a column |
| // indicates a device cluster |
| List<List<int>> matrix; |
| |
| void fill(Map<DeviceSpec, Device> match) { |
| match.forEach((DeviceSpec spec, Device device) { |
| int rowNum = clusterInfo.deviceSpecClustersOrder |
| .indexOf(spec.clusterKey()); |
| int colNum = clusterInfo.deviceClustersOrder |
| .indexOf(device.clusterKey()); |
| matrix[rowNum][colNum] = 1; |
| }); |
| } |
| |
| void union(CoverageMatrix newCoverage) { |
| for (int i = 0; i < matrix.length; i++) { |
| List<int> row = matrix[i]; |
| for (int j = 0; j < row.length; j++) { |
| matrix[i][j] |= newCoverage.matrix[i][j]; |
| } |
| } |
| } |
| |
| void printMatrix() { |
| Table prettyMatrix = new Table(1); |
| prettyMatrix.columns.add('app key \\ device key'); |
| prettyMatrix.columns.addAll(clusterInfo.deviceClustersOrder); |
| int startIndx = beginOfDiff(clusterInfo.deviceSpecClustersOrder); |
| for (int i = 0; i < matrix.length; i++) { |
| prettyMatrix.data.add(clusterInfo.deviceSpecClustersOrder[i].substring(startIndx)); |
| prettyMatrix.data.addAll(matrix[i]); |
| } |
| print(prettyMatrix); |
| } |
| } |
| |
| Map<CoverageMatrix, Map<DeviceSpec, Device>> buildCoverage2MatchMapping( |
| List<Map<DeviceSpec, Device>> allMatches, |
| ClusterInfo clusterInfo |
| ) { |
| Map<CoverageMatrix, Map<DeviceSpec, Device>> cov2match |
| = <CoverageMatrix, Map<DeviceSpec, Device>>{}; |
| for (Map<DeviceSpec, Device> match in allMatches) { |
| CoverageMatrix cov = new CoverageMatrix(clusterInfo); |
| cov.fill(match); |
| cov2match[cov] = match; |
| } |
| return cov2match; |
| } |
| |
| /// Find a small number of mappings which cover the maximum app-device coverage |
| /// feasible in given the available devices and specs. The problem can be |
| /// treated as a set cover problem which is NP-complete and the implementation |
| /// follow the spirit of greedy algorithm which is O(log(n)). |
| /// [ref link]: https://en.wikipedia.org/wiki/Set_cover_problem |
| Set<Map<DeviceSpec, Device>> findMinimumMappings( |
| Map<CoverageMatrix, Map<DeviceSpec, Device>> cov2match, |
| ClusterInfo clusterInfo |
| ) { |
| Set<CoverageMatrix> minSet = new Set<CoverageMatrix>(); |
| CoverageMatrix base = new CoverageMatrix(clusterInfo); |
| while (true) { |
| CoverageMatrix currentBestCoverage = null; |
| int maxReward = 0; |
| for (CoverageMatrix coverage in cov2match.keys) { |
| if (minSet.contains(coverage)) continue; |
| int reward = computeReward(base, coverage); |
| if (maxReward < reward) { |
| maxReward = reward; |
| currentBestCoverage = coverage; |
| } |
| } |
| if (currentBestCoverage == null) break; |
| minSet.add(currentBestCoverage); |
| base.union(currentBestCoverage); |
| } |
| print('Best coverage matrix:'); |
| base.printMatrix(); |
| Set<Map<DeviceSpec, Device>> bestMatches = new Set<Map<DeviceSpec, Device>>(); |
| for (CoverageMatrix coverage in minSet) { |
| bestMatches.add(cov2match[coverage]); |
| } |
| return bestMatches; |
| } |
| |
| int computeReward(CoverageMatrix base, CoverageMatrix newCoverage) { |
| int reward = 0; |
| for (int i = 0; i < base.matrix.length; i++) { |
| List<int> row = base.matrix[i]; |
| for (int j = 0; j < row.length; j++) { |
| if (base.matrix[i][j] == 0 && newCoverage.matrix[i][j] == 1) |
| reward++; |
| } |
| } |
| return reward; |
| } |